Compare commits
5 Commits
2ad00c8895
...
db9c6c9446
Author | SHA1 | Date |
---|---|---|
Brett | db9c6c9446 | |
Brett | 889f24d402 | |
Brett | 58ba957b1d | |
Brett | 7033474455 | |
Brett | a1331db389 |
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
project(BLT VERSION 0.5.2)
|
||||
project(BLT VERSION 0.6.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
|
@ -7,7 +7,13 @@ option(BUILD_STD "Build the BLT standard utilities." ON)
|
|||
option(BUILD_PROFILING "Build the BLT profiler extension" ON)
|
||||
option(BUILD_NBT "Build the BLT NBT + eNBT extension" ON)
|
||||
option(BUILD_TESTS "Build the BLT test set" OFF)
|
||||
option(BLT_ENABLE_LOGGING "Enable blt::logging" ON)
|
||||
option(BLT_ENABLE_LOGGING "Enable blt::logging (all macros and will safely disable logging function!)" ON)
|
||||
option(BLT_ENABLE_TRACE "Enable blt::logging BLT_TRACE macro" ON)
|
||||
option(BLT_ENABLE_DEBUG "Enable blt::logging BLT_DEBUG macro" ON)
|
||||
option(BLT_ENABLE_INFO "Enable blt::logging BLT_INFO macro" ON)
|
||||
option(BLT_ENABLE_WARN "Enable blt::logging BLT_WARN macro" ON)
|
||||
option(BLT_ENABLE_ERROR "Enable blt::logging BLT_ERROR macro" ON)
|
||||
option(BLT_ENABLE_FATAL "Enable blt::logging BLT_FATAL macro" ON)
|
||||
|
||||
if(${BUILD_STD} OR ${BUILD_PROFILING})
|
||||
file(GLOB_RECURSE STD_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/blt/std/*.cpp")
|
||||
|
|
|
@ -3,5 +3,11 @@
|
|||
|
||||
#cmakedefine ZLIB_FOUND
|
||||
#cmakedefine BLT_ENABLE_LOGGING
|
||||
#cmakedefine BLT_ENABLE_TRACE
|
||||
#cmakedefine BLT_ENABLE_DEBUG
|
||||
#cmakedefine BLT_ENABLE_INFO
|
||||
#cmakedefine BLT_ENABLE_WARN
|
||||
#cmakedefine BLT_ENABLE_ERROR
|
||||
#cmakedefine BLT_ENABLE_FATAL
|
||||
|
||||
#endif // BLT_CONFIG_H
|
|
@ -26,7 +26,7 @@ namespace blt {
|
|||
template<typename T, uint32_t size>
|
||||
struct vec {
|
||||
private:
|
||||
T elements[size]{};
|
||||
T elements[size];
|
||||
public:
|
||||
|
||||
vec() {
|
||||
|
@ -48,7 +48,7 @@ namespace blt {
|
|||
|
||||
template<typename o_T, uint32_t o_size>
|
||||
explicit vec(const vec<o_T, o_size>& copy) {
|
||||
for (uint32_t i = 0; i < o_size; i++)
|
||||
for (uint32_t i = 0; i < std::min(o_size, size); i++)
|
||||
elements[i] = copy[i];
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace blt::profiling {
|
|||
profile getProfile(const std::string& profileName);
|
||||
|
||||
void printProfile(
|
||||
const std::string& profileName, logging::LOG_LEVEL loggingLevel = logging::BLT_NONE,
|
||||
const std::string& profileName, logging::log_level loggingLevel = logging::log_level::NONE,
|
||||
bool averageHistory = false
|
||||
);
|
||||
|
||||
|
|
|
@ -1,115 +1,387 @@
|
|||
/*
|
||||
* Created by Brett on 23/01/23.
|
||||
* Created by Brett on 20/07/23.
|
||||
* Licensed under GNU General Public License V3.0
|
||||
* See LICENSE file for license detail
|
||||
*/
|
||||
|
||||
#ifndef BLT_LOGGING_H
|
||||
#define BLT_LOGGING_H
|
||||
#ifndef BLT_TESTS_LOGGING2_H
|
||||
#define BLT_TESTS_LOGGING2_H
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <blt/config.h>
|
||||
#include <iostream>
|
||||
|
||||
namespace blt::logging {
|
||||
|
||||
enum LOG_LEVEL {
|
||||
// normal
|
||||
BLT_TRACE = 0, BLT_DEBUG = 1, BLT_INFO = 2,
|
||||
// errors
|
||||
BLT_WARN = 3, BLT_ERROR = 4, BLT_FATAL = 5,
|
||||
template<typename K, typename V>
|
||||
using hashmap = std::unordered_map<K, V>;
|
||||
|
||||
enum class log_level {
|
||||
// default
|
||||
BLT_NONE = 6
|
||||
NONE,
|
||||
// low level
|
||||
TRACE0, TRACE1, TRACE2, TRACE3,
|
||||
// normal
|
||||
TRACE, DEBUG, INFO,
|
||||
// errors
|
||||
WARN, ERROR, FATAL,
|
||||
};
|
||||
|
||||
struct LOG_PROPERTIES {
|
||||
bool m_useColor = true;
|
||||
bool m_logToConsole = true;
|
||||
bool m_logToFile = true;
|
||||
// include file + line data?
|
||||
bool m_logWithData = true;
|
||||
// print the whole path or just the file name?
|
||||
bool m_logFullPath = false;
|
||||
const char* m_directory = "./";
|
||||
LOG_LEVEL minLevel = BLT_TRACE;
|
||||
|
||||
explicit constexpr LOG_PROPERTIES(bool useColor, bool logToConsole, bool logToFile, const char* directory):
|
||||
m_useColor(useColor), m_logToConsole(logToConsole), m_logToFile(logToFile), m_directory(directory) {}
|
||||
|
||||
explicit constexpr LOG_PROPERTIES() = default;
|
||||
struct tag_func_param {
|
||||
blt::logging::log_level level;
|
||||
const std::string& file, line, raw_string, formatted_string;
|
||||
};
|
||||
|
||||
struct tag {
|
||||
// tag without the ${{ or }}
|
||||
std::string tag;
|
||||
// function to run: log level, file, line, and raw user input string are provided
|
||||
std::function<std::string(const tag_func_param&)> func;
|
||||
};
|
||||
|
||||
struct log_format {
|
||||
/**
|
||||
* the log output format is the string which will be used to create the log output string
|
||||
*
|
||||
* Available tags:
|
||||
* - ${{YEAR}} // current year
|
||||
* - ${{MONTH}} // current month
|
||||
* - ${{DAY}} // current day
|
||||
* - ${{HOUR}} // current hour
|
||||
* - ${{MINUTE}} // current minute
|
||||
* - ${{SECOND}} // current second
|
||||
* - ${{MS}} // current unix time
|
||||
* - ${{NS}} // current ns from the high resolution system timer
|
||||
* - ${{ISO_YEAR}} // ISO formatted 'year-month-day' in a single variable
|
||||
* - ${{TIME}} // 'hour:minute:second' formatted string in a single variable
|
||||
* - ${{FULL_TIME}} // 'year-month-day hour:minute:second' in a single variable
|
||||
* - ${{LF}} // log level color (ANSI color code)
|
||||
* - ${{ER}} // Error red
|
||||
* - ${{CNR}} // conditional error red (only outputs if log level is an error!)
|
||||
* - ${{RC}} // ANSI color reset
|
||||
* - ${{LOG_LEVEL}} // current log level
|
||||
* - ${{THREAD_NAME}} // current thread name, NOTE: thread names must be set by calling "setThreadName()" from the thread in question!
|
||||
* - ${{FILE}} // file from which the macro is invoked
|
||||
* - ${{LINE}} // line in the from which the macro is invoked
|
||||
* - ${{RAW_STR}} // raw user string without formatting applied (NOTE: format args are not provided!)
|
||||
* - ${{STR}} // the user supplied string (format applied!)
|
||||
*/
|
||||
std::string logOutputFormat = "[${{HOUR}}:${{MINUTE}}:${{SECOND}] ${{LF}}[${{LOG_LEVEL}}]${{R}} ${{CNR}}${{STR}}";
|
||||
std::string levelNames[10] = {"STDOUT", "TRACE0, TRACE1, TRACE2", "TRACE3", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
|
||||
std::string levelColors[10] = {"\033[0m", "\033[22;97m", "\033[97m", "\033[97m", "\033[97m", "\033[36m", "\033[92m", "\033[93m", "\033[91m", "\033[97;41m"};
|
||||
};
|
||||
|
||||
struct logger {
|
||||
LOG_LEVEL level;
|
||||
void log_internal(const std::string& str) const;
|
||||
// evil hack, todo: better way
|
||||
#ifdef BLT_DISABLE_LOGGING
|
||||
void log(const std::string& str) const {
|
||||
|
||||
}
|
||||
#else
|
||||
void log(const std::string& str) const {
|
||||
log_internal(str);
|
||||
}
|
||||
#endif
|
||||
void flush() const;
|
||||
static void flush_all();
|
||||
log_level level;
|
||||
const char* file;
|
||||
int line;
|
||||
};
|
||||
|
||||
static logger std_out{BLT_NONE};
|
||||
struct empty_logger {
|
||||
|
||||
};
|
||||
|
||||
void log(const std::string& format, log_level level, const char* file, int line, ...);
|
||||
void log_stream(const std::string& str, const logger& logger);
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
inline void log(T t, log_level level, const char* file, int line) {
|
||||
log(std::to_string(t), level, file, line);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
inline void log_stream(T t, const logger& logger) {
|
||||
log_stream(std::to_string(t), logger);
|
||||
}
|
||||
|
||||
static inline const blt::logging::logger& operator<<(const blt::logging::logger& out, const std::string& str) {
|
||||
log_stream(str, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
static inline const blt::logging::logger& operator<<(const blt::logging::logger& out, T t) {
|
||||
log_stream(std::to_string(t), out);
|
||||
return out;
|
||||
}
|
||||
|
||||
static logger trace{BLT_TRACE};
|
||||
static logger debug{BLT_DEBUG};
|
||||
static logger info{BLT_INFO};
|
||||
static logger warn{BLT_WARN};
|
||||
static logger error{BLT_ERROR};
|
||||
static logger fatal{BLT_FATAL};
|
||||
|
||||
static inline logger& getLoggerFromLevel(LOG_LEVEL level) {
|
||||
static logger loggerLevelDecode[7]{trace, debug, info, warn, error, fatal, std_out};
|
||||
return loggerLevelDecode[level];
|
||||
}
|
||||
|
||||
static inline logger& operator<<(logger& out, const std::string& str) {
|
||||
out.log(str);
|
||||
static inline const blt::logging::empty_logger& operator<<(const blt::logging::empty_logger& out, const std::string&) {
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
static inline logger& operator<<(logger& out, T t) {
|
||||
out.log(std::to_string(t));
|
||||
static inline const blt::logging::empty_logger& operator<<(const blt::logging::empty_logger& out, T) {
|
||||
return out;
|
||||
}
|
||||
|
||||
void init(LOG_PROPERTIES properties);
|
||||
void log_internal(const std::string& format, LOG_LEVEL level, const char* file, int currentLine, int auto_line, ...);
|
||||
|
||||
// template voodoo magic (SFINAE, "Substitution Failure Is Not An Error")
|
||||
// https://stackoverflow.com/questions/44848011/c-limit-template-type-to-numbers
|
||||
// std::enable_if has a member called type if the condition is true.
|
||||
// What I am doing is saying that if the condition is true then the template has a non type
|
||||
// template parameter of type type* (which is void* since the default type for enable_if is void) and it's value is nullptr.
|
||||
// if the condition is false, the substitution fails, and the entire template is silently (no compiler error) discarded from the overload set.
|
||||
/**
|
||||
* Logs an is_arithmetic type to the console.
|
||||
* @tparam T type to log
|
||||
* @param t the value to log
|
||||
* @param level logger level to log at
|
||||
* @param auto_line put a new line at the end if none exists if true
|
||||
*/
|
||||
template<typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
|
||||
inline void log_internal(T t, LOG_LEVEL level, const char* file, int currentLine, int auto_line){
|
||||
log_internal(std::to_string(t), level, file, currentLine, auto_line);
|
||||
}
|
||||
/**
|
||||
* Will flush all buffers! This might cause issues with threads!
|
||||
*/
|
||||
void flush();
|
||||
|
||||
void testLogging();
|
||||
void setThreadName(const std::string& name);
|
||||
}
|
||||
|
||||
#ifdef BLT_DISABLE_LOGGING
|
||||
#define BLT_LOGGING_IMPLEMENTATION
|
||||
#ifdef BLT_LOGGING_IMPLEMENTATION
|
||||
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdarg>
|
||||
|
||||
namespace blt::logging {
|
||||
|
||||
/**
|
||||
* Used to store fast associations between built in tags and their respective values
|
||||
*/
|
||||
class tag_map {
|
||||
private:
|
||||
tag* tags;
|
||||
size_t size;
|
||||
|
||||
[[nodiscard]] inline size_t hash(const tag& t) const {
|
||||
size_t h = t.tag[0]+ t.tag[1] * 3;
|
||||
return h;
|
||||
}
|
||||
|
||||
inline void expand(){
|
||||
auto newSize = size * 2;
|
||||
tag* newTags = new tag[newSize];
|
||||
for (size_t i = 0; i < size; i++)
|
||||
newTags[i] = tags[i];
|
||||
delete[] tags;
|
||||
tags = newTags;
|
||||
size = newSize;
|
||||
}
|
||||
public:
|
||||
tag_map(std::initializer_list<tag> initial_tags){
|
||||
tags = new tag[(size = 16)];
|
||||
for (auto& t : initial_tags)
|
||||
insert(t);
|
||||
}
|
||||
|
||||
tag_map& insert(const tag& t){
|
||||
auto h = hash(t);
|
||||
if (h > size)
|
||||
expand();
|
||||
if (!tags[h].tag.empty())
|
||||
std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n";
|
||||
tags[h] = t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
tag& operator[](const std::string& name){
|
||||
auto h = hash(tag{name, nullptr});
|
||||
if (h > size)
|
||||
std::cerr << "Tag out of bounds";
|
||||
return tags[h];
|
||||
}
|
||||
|
||||
~tag_map(){
|
||||
delete[] tags;
|
||||
}
|
||||
};
|
||||
|
||||
#define BLT_NOW() auto t = std::time(nullptr); auto now = std::localtime(&t)
|
||||
#define BLT_ISO_YEAR(S) auto S = std::to_string(now->tm_year); \
|
||||
S += '-'; \
|
||||
S += std::to_string(now->tm_mon); \
|
||||
S += '-'; \
|
||||
S += std::to_string(now->tm_mday);
|
||||
#define BLT_CUR_TIME(S) auto S = std::to_string(now->tm_hour); \
|
||||
S += '-'; \
|
||||
S += std::to_string(now->tm_min); \
|
||||
S += '-'; \
|
||||
S += std::to_string(now->tm_sec);
|
||||
|
||||
static inline std::string ensureHasDigits(int current, int digits) {
|
||||
std::string asString = std::to_string(current);
|
||||
auto length = digits - asString.length();
|
||||
if (length <= 0)
|
||||
return asString;
|
||||
std::string zeros;
|
||||
zeros.reserve(length);
|
||||
for (unsigned int i = 0; i < length; i++){
|
||||
zeros += '0';
|
||||
}
|
||||
return zeros + asString;
|
||||
}
|
||||
|
||||
log_format loggingFormat {};
|
||||
hashmap<std::thread::id, std::string> loggingThreadNames;
|
||||
hashmap<std::thread::id, hashmap<blt::logging::log_level, std::string>> loggingStreamLines;
|
||||
|
||||
const tag_map tagMap = {
|
||||
{"YEAR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return std::to_string(now->tm_year);
|
||||
}},
|
||||
{"MONTH", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now->tm_mon, 2);
|
||||
}},
|
||||
{"DAY", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now->tm_mday, 2);
|
||||
}},
|
||||
{"HOUR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now->tm_hour, 2);
|
||||
}},
|
||||
{"MINUTE", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now->tm_min, 2);
|
||||
}},
|
||||
{"SECOND", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
return ensureHasDigits(now->tm_sec, 2);
|
||||
}},
|
||||
{"MS", [](const tag_func_param&) -> std::string {
|
||||
return std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
|
||||
);
|
||||
}},
|
||||
{"NS", [](const tag_func_param&) -> std::string {
|
||||
return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch()).count()
|
||||
);
|
||||
}},
|
||||
{"ISO_YEAR", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_ISO_YEAR(returnStr);
|
||||
return returnStr;
|
||||
}},
|
||||
{"TIME", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_CUR_TIME(returnStr);
|
||||
return returnStr;
|
||||
}},
|
||||
{"FULL_TIME", [](const tag_func_param&) -> std::string {
|
||||
BLT_NOW();
|
||||
BLT_ISO_YEAR(ISO);
|
||||
BLT_CUR_TIME(TIME);
|
||||
ISO += ' ';
|
||||
ISO += TIME;
|
||||
return ISO;
|
||||
}},
|
||||
{"LF", [](const tag_func_param& f) -> std::string {
|
||||
return loggingFormat.levelColors[(int)f.level];
|
||||
}},
|
||||
{"ER", [](const tag_func_param&) -> std::string {
|
||||
return loggingFormat.levelColors[(int)log_level::ERROR];
|
||||
}},
|
||||
{"CNR", [](const tag_func_param& f) -> std::string {
|
||||
return f.level >= log_level::ERROR ? loggingFormat.levelColors[(int)log_level::ERROR] : "";
|
||||
}},
|
||||
{"RC", [](const tag_func_param&) -> std::string {
|
||||
return "\033[0m";
|
||||
}},
|
||||
{"LOG_LEVEL", [](const tag_func_param& f) -> std::string {
|
||||
return loggingFormat.levelNames[(int)f.level];
|
||||
}},
|
||||
{"THREAD_NAME", [](const tag_func_param&) -> std::string {
|
||||
if (loggingThreadNames.find(std::this_thread::get_id()) == loggingThreadNames.end())
|
||||
return "UNKNOWN";
|
||||
return loggingThreadNames[std::this_thread::get_id()];
|
||||
}},
|
||||
{"FILE", [](const tag_func_param& f) -> std::string {
|
||||
return f.file;
|
||||
}},
|
||||
{"LINE", [](const tag_func_param& f) -> std::string {
|
||||
return f.line;
|
||||
}},
|
||||
{"RAW_STR", [](const tag_func_param& f) -> std::string {
|
||||
return f.raw_string;
|
||||
}},
|
||||
{"STR", [](const tag_func_param& f) -> std::string {
|
||||
return f.formatted_string;
|
||||
}},
|
||||
};
|
||||
|
||||
static inline std::vector<std::string> split(std::string s, const std::string& delim) {
|
||||
size_t pos = 0;
|
||||
std::vector<std::string> tokens;
|
||||
while ((pos = s.find(delim)) != std::string::npos) {
|
||||
auto token = s.substr(0, pos);
|
||||
tokens.push_back(token);
|
||||
s.erase(0, pos + delim.length());
|
||||
}
|
||||
tokens.push_back(s);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
inline std::string filename(const std::string& path){
|
||||
// if (BLT_LOGGING_PROPERTIES.m_logFullPath)
|
||||
// return path;
|
||||
auto paths = split(path, "/");
|
||||
auto final = paths[paths.size()-1];
|
||||
if (final == "/")
|
||||
return paths[paths.size()-2];
|
||||
return final;
|
||||
}
|
||||
|
||||
class string_parser {
|
||||
private:
|
||||
std::string _str;
|
||||
size_t _pos;
|
||||
public:
|
||||
explicit string_parser(std::string str): _str(std::move(str)), _pos(0) {}
|
||||
|
||||
inline char next(){
|
||||
return _str[_pos++];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool has_next() const {
|
||||
return _pos < _str.size();
|
||||
}
|
||||
};
|
||||
|
||||
std::string applyFormatString(const std::string& str){
|
||||
return str;
|
||||
}
|
||||
|
||||
void log(const std::string& format, log_level level, const char* file, int line, ...) {
|
||||
va_list args;
|
||||
va_start(args, line);
|
||||
|
||||
std::string out = format;
|
||||
|
||||
if (out.length() > 0 && out[out.length() - 1] == '\n')
|
||||
out = out.substr(0, out.length()-1);
|
||||
|
||||
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void log_stream(const std::string& str, const logger& logger) {
|
||||
auto& s = loggingStreamLines[std::this_thread::get_id()][logger.level];
|
||||
s += str;
|
||||
for (char c : str){
|
||||
if (c == '\n'){
|
||||
log(s, logger.level, logger.file, logger.line);
|
||||
s = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setThreadName(const std::string& name) {
|
||||
loggingThreadNames[std::this_thread::get_id()] = name;
|
||||
}
|
||||
|
||||
void flush() {
|
||||
std::cerr.flush();
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(BLT_ENABLE_LOGGING) || defined(BLT_DISABLE_LOGGING)
|
||||
#define BLT_LOG(format, level, ...)
|
||||
#define BLT_TRACE(format, ...)
|
||||
#define BLT_DEBUG(format, ...)
|
||||
#define BLT_INFO(format, ...)
|
||||
|
@ -117,12 +389,62 @@ namespace blt::logging {
|
|||
#define BLT_ERROR(format, ...)
|
||||
#define BLT_FATAL(format, ...)
|
||||
#else
|
||||
#define BLT_TRACE(format, ...) log_internal(format, blt::logging::BLT_TRACE, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_DEBUG(format, ...) log_internal(format, blt::logging::BLT_DEBUG, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_INFO(format, ...) log_internal(format, blt::logging::BLT_INFO, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_WARN(format, ...) log_internal(format, blt::logging::BLT_WARN, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_ERROR(format, ...) log_internal(format, blt::logging::BLT_ERROR, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_FATAL(format, ...) log_internal(format, blt::logging::BLT_FATAL, __FILE__, __LINE__, true, ##__VA_ARGS__)
|
||||
#define BLT_LOG(format, level, ...) log(format, level, __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
#ifndef BLT_ENABLE_TRACE
|
||||
#define BLT_TRACE(format, ...)
|
||||
#define BLT_TRACE0_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE1_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE2_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE3_STREAM blt::logging::empty_logger{}
|
||||
#define BLT_TRACE_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_TRACE(format, ...) BLT_LOG(format, blt::logging::log_level::TRACE, ##__VA_ARGS__)
|
||||
#define BLT_TRACE0_STREAM blt::logging::logger{blt::logging::log_level::TRACE0, __FILE__, __LINE__}
|
||||
#define BLT_TRACE1_STREAM blt::logging::logger{blt::logging::log_level::TRACE1, __FILE__, __LINE__}
|
||||
#define BLT_TRACE2_STREAM blt::logging::logger{blt::logging::log_level::TRACE2, __FILE__, __LINE__}
|
||||
#define BLT_TRACE3_STREAM blt::logging::logger{blt::logging::log_level::TRACE3, __FILE__, __LINE__}
|
||||
#define BLT_TRACE_STREAM blt::logging::logger{blt::logging::log_level::TRACE, __FILE__, __LINE__}
|
||||
#endif
|
||||
|
||||
#ifndef BLT_ENABLE_DEBUG
|
||||
#define BLT_DEBUG(format, ...)
|
||||
#define BLT_DEBUG_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_DEBUG(format, ...) BLT_LOG(format, blt::logging::log_level::DEBUG, ##__VA_ARGS__)
|
||||
#define BLT_DEBUG_STREAM blt::logging::logger{blt::logging::log_level::DEBUG, __FILE__, __LINE__}
|
||||
#endif
|
||||
|
||||
#ifndef BLT_ENABLE_INFO
|
||||
#define BLT_INFO(format, ...)
|
||||
#define BLT_INFO_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_INFO(format, ...) BLT_LOG(format, blt::logging::log_level::INFO, ##__VA_ARGS__)
|
||||
#define BLT_INFO_STREAM blt::logging::logger{blt::logging::log_level::INFO, __FILE__, __LINE__}
|
||||
#endif
|
||||
|
||||
#ifndef BLT_ENABLE_WARN
|
||||
#define BLT_WARN(format, ...)
|
||||
#define BLT_WARN_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_WARN(format, ...) BLT_LOG(format, blt::logging::log_level::WARN, ##__VA_ARGS__)
|
||||
#define BLT_WARN_STREAM blt::logging::logger{blt::logging::log_level::WARN, __FILE__, __LINE__}
|
||||
#endif
|
||||
|
||||
#ifndef BLT_ENABLE_ERROR
|
||||
#define BLT_ERROR(format, ...)
|
||||
#define BLT_ERROR_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_ERROR(format, ...) BLT_LOG(format, blt::logging::log_level::ERROR, ##__VA_ARGS__)
|
||||
#define BLT_ERROR_STREAM blt::logging::logger{blt::logging::log_level::ERROR, __FILE__, __LINE__}
|
||||
#endif
|
||||
|
||||
#ifndef BLT_ENABLE_FATAL
|
||||
#define BLT_FATAL(format, ...)
|
||||
#define BLT_FATAL_STREAM blt::logging::empty_logger{}
|
||||
#else
|
||||
#define BLT_FATAL(format, ...) BLT_LOG(format, blt::logging::log_level::FATAL, ##__VA_ARGS__)
|
||||
#define BLT_FATAL_STREAM blt::logging::logger{blt::logging::log_level::FATAL, __FILE__, __LINE__}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif //BLT_LOGGING_H
|
||||
#endif //BLT_TESTS_LOGGING2_H
|
||||
|
|
|
@ -7,7 +7,53 @@
|
|||
#ifndef BLT_TESTS_MEMORY_H
|
||||
#define BLT_TESTS_MEMORY_H
|
||||
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
|
||||
namespace blt {
|
||||
|
||||
template<typename V>
|
||||
struct ptr_iterator {
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = V;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
explicit ptr_iterator(V* v): _v(v) {}
|
||||
|
||||
reference operator*() const { return *_v; }
|
||||
pointer operator->() { return _v; }
|
||||
ptr_iterator& operator++() {
|
||||
_v++;
|
||||
return *this;
|
||||
}
|
||||
ptr_iterator& operator--() {
|
||||
_v--;
|
||||
return *this;
|
||||
}
|
||||
ptr_iterator operator++(int){
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
ptr_iterator operator--(int){
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
friend bool operator==(const ptr_iterator& a, const ptr_iterator& b) {
|
||||
return a._v == b._v;
|
||||
}
|
||||
friend bool operator!=(const ptr_iterator& a, const ptr_iterator& b) {
|
||||
return a._v != b._v;
|
||||
}
|
||||
|
||||
private:
|
||||
V* _v;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an encapsulation of a T array which will be automatically deleted when this object goes out of scope.
|
||||
* This is a simple buffer meant to be used only inside of a function and not moved around, with a few minor exceptions.
|
||||
|
@ -77,6 +123,51 @@ namespace blt {
|
|||
|
||||
~nullptr_initializer() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a hash-map like association between an enum key and any arbitrary value.
|
||||
* The storage is backed by a contiguous array for faster access.
|
||||
* @tparam K enum value
|
||||
* @tparam V associated value
|
||||
*/
|
||||
template<typename K, typename V>
|
||||
class enum_storage {
|
||||
private:
|
||||
V* _values;
|
||||
size_t _size = 0;
|
||||
public:
|
||||
enum_storage(std::initializer_list<std::pair<K, V>> init){
|
||||
for (auto& i : init)
|
||||
_size = std::max((size_t)i.first, _size);
|
||||
_values = new V[_size];
|
||||
for (auto& v : init)
|
||||
_values[(size_t)v.first] = v.second;
|
||||
}
|
||||
|
||||
inline V& operator[](size_t index){
|
||||
return _values[index];
|
||||
}
|
||||
|
||||
inline const V& operator[](size_t index) const {
|
||||
return _values[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] inline size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
ptr_iterator<V> begin(){
|
||||
return ptr_iterator{_values};
|
||||
}
|
||||
|
||||
ptr_iterator<V> end(){
|
||||
return ptr_iterator{&_values[_size]};
|
||||
}
|
||||
|
||||
~enum_storage(){
|
||||
delete[] _values;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BLT_TESTS_MEMORY_H
|
||||
|
|
|
@ -30,10 +30,12 @@ namespace blt::profiling {
|
|||
difference(difference), name(std::move(name)), total(total) {}
|
||||
};
|
||||
|
||||
inline void println(const std::vector<std::string>&& lines, logging::LOG_LEVEL level) {
|
||||
auto& logger = logging::getLoggerFromLevel(level);
|
||||
inline void println(const std::vector<std::string>&& lines, logging::log_level level) {
|
||||
for (const auto& line : lines)
|
||||
logger << line << "\n";
|
||||
BLT_INFO_STREAM << line << "\n";
|
||||
// auto& logger = logging::getLoggerFromLevel(level);
|
||||
// for (const auto& line : lines)
|
||||
// logger << line << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,7 +106,7 @@ namespace blt::profiling {
|
|||
}
|
||||
|
||||
void printProfile(
|
||||
const std::string& profileName, logging::LOG_LEVEL loggingLevel, bool averageHistory
|
||||
const std::string& profileName, logging::log_level loggingLevel, bool averageHistory
|
||||
) {
|
||||
auto& profile = profiles[profileName];
|
||||
const auto& intervals = profile.intervals;
|
||||
|
|
|
@ -1,243 +1,7 @@
|
|||
/*
|
||||
* Created by Brett on 23/01/23.
|
||||
* Created by Brett on 20/07/23.
|
||||
* Licensed under GNU General Public License V3.0
|
||||
* See LICENSE file for license detail
|
||||
*/
|
||||
#define BLT_LOGGING_IMPLEMENTATION
|
||||
#include <blt/std/logging.h>
|
||||
#include <blt/std/time.h>
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "blt/std/string.h"
|
||||
#include "blt/std/format.h"
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include <ios>
|
||||
#include <thread>
|
||||
#include <filesystem>
|
||||
#include <blt/std/memory.h>
|
||||
|
||||
// https://en.cppreference.com/w/cpp/utility/variadic
|
||||
// https://medium.com/swlh/variadic-functions-3419c287a0d2
|
||||
// https://publications.gbdirect.co.uk//c_book/chapter9/stdarg.html
|
||||
// https://cplusplus.com/reference/cstdio/printf/
|
||||
|
||||
|
||||
namespace blt::logging {
|
||||
|
||||
class LogFileWriter {
|
||||
private:
|
||||
std::string m_path;
|
||||
std::fstream* output;
|
||||
int currentLines = 0;
|
||||
static constexpr int MAX_LINES = 100000;
|
||||
public:
|
||||
explicit LogFileWriter(const std::string& path): m_path(path){
|
||||
auto currentTime = system::getTimeStringFS();
|
||||
output = new std::fstream(path + currentTime + ".log", std::ios::out | std::ios::app);
|
||||
if (!output->good()){
|
||||
throw std::runtime_error("Unable to open console filestream!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void writeLine(const std::string& line){
|
||||
if (!output->good()){
|
||||
std::cerr << "There has been an error in the logging file stream!\n";
|
||||
output->clear();
|
||||
}
|
||||
*output << line;
|
||||
currentLines++;
|
||||
if (currentLines > MAX_LINES){
|
||||
output->flush();
|
||||
output->close();
|
||||
currentLines = 0;
|
||||
auto currentTime = system::getTimeStringFS();
|
||||
delete(output);
|
||||
output = new std::fstream(m_path + currentTime + ".log");
|
||||
}
|
||||
}
|
||||
|
||||
~LogFileWriter() {
|
||||
delete(output);
|
||||
}
|
||||
};
|
||||
|
||||
void applyFormatting(const std::string& format, std::string& output, va_list& args){
|
||||
// args must be copied because they will be consumed by the first vsnprintf
|
||||
va_list args_copy;
|
||||
va_copy(args_copy, args);
|
||||
|
||||
auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1;
|
||||
// some compilers don't even allow you to do stack dynamic arrays. So always allocate on the heap.
|
||||
// originally if the buffer was small enough the buffer was allocated on the stack because it made no sense to make a heap object
|
||||
// that will be deleted a couple lines later.
|
||||
scoped_buffer<char> buffer{static_cast<unsigned long>(buffer_size)};
|
||||
vsnprintf(*buffer, buffer_size, format.c_str(), args);
|
||||
output = std::string(*buffer);
|
||||
|
||||
va_end(args_copy);
|
||||
}
|
||||
|
||||
const char* levelColors[6] = {
|
||||
"\033[97m",
|
||||
"\033[36m",
|
||||
"\033[92m",
|
||||
"\033[93m",
|
||||
"\033[91m",
|
||||
"\033[97;41m"
|
||||
};
|
||||
|
||||
const char* levelNames[6] = {
|
||||
"[BLT_TRACE]: ",
|
||||
"[BLT_DEBUG]: ",
|
||||
"[BLT_INFO]: ",
|
||||
"[BLT_WARN]: ",
|
||||
"[BLT_ERROR]: ",
|
||||
"[BLT_FATAL]: ",
|
||||
};
|
||||
|
||||
// by default everything is enabled
|
||||
LOG_PROPERTIES BLT_LOGGING_PROPERTIES {};
|
||||
LogFileWriter* writer = new LogFileWriter{"./"};
|
||||
|
||||
void init(LOG_PROPERTIES properties) {
|
||||
if (properties.m_logToFile && BLT_LOGGING_PROPERTIES.m_directory != properties.m_directory) {
|
||||
delete(writer);
|
||||
writer = new LogFileWriter{properties.m_directory};
|
||||
}
|
||||
if (properties.m_logToFile)
|
||||
std::filesystem::create_directory(properties.m_directory);
|
||||
BLT_LOGGING_PROPERTIES = properties;
|
||||
}
|
||||
|
||||
inline std::string filename(const std::string& path){
|
||||
if (BLT_LOGGING_PROPERTIES.m_logFullPath)
|
||||
return path;
|
||||
auto paths = blt::string::split(path, "/");
|
||||
auto final = paths[paths.size()-1];
|
||||
if (final == "/")
|
||||
return paths[paths.size()-2];
|
||||
return final;
|
||||
}
|
||||
|
||||
inline void log(const std::string& str, bool hasEndingLinefeed, LOG_LEVEL level, const char* file, int currentLine, int auto_line){
|
||||
if (level < BLT_LOGGING_PROPERTIES.minLevel)
|
||||
return;
|
||||
std::string outputString = system::getTimeStringLog();
|
||||
|
||||
if (BLT_LOGGING_PROPERTIES.m_logWithData && currentLine >= 0) {
|
||||
outputString += '[';
|
||||
outputString += filename(file);
|
||||
outputString += ':';
|
||||
outputString += std::to_string(currentLine);
|
||||
outputString += "] ";
|
||||
}
|
||||
|
||||
outputString += levelNames[level];
|
||||
outputString += str;
|
||||
|
||||
std::string fileString = outputString;
|
||||
|
||||
if (BLT_LOGGING_PROPERTIES.m_useColor) {
|
||||
outputString = levelColors[level] + outputString;
|
||||
outputString += "\033[0m";
|
||||
}
|
||||
|
||||
if (hasEndingLinefeed || auto_line) {
|
||||
outputString += "\n";
|
||||
fileString += "\n";
|
||||
}
|
||||
|
||||
if (BLT_LOGGING_PROPERTIES.m_logToConsole) {
|
||||
if (level > BLT_WARN)
|
||||
std::cerr << outputString;
|
||||
else
|
||||
std::cout << outputString;
|
||||
}
|
||||
|
||||
if (BLT_LOGGING_PROPERTIES.m_logToFile) {
|
||||
writer->writeLine(fileString);
|
||||
}
|
||||
}
|
||||
|
||||
void log_internal(const std::string& format, LOG_LEVEL level, const char* file, int currentLine, int auto_line, ...) {
|
||||
va_list args;
|
||||
va_start(args, auto_line);
|
||||
|
||||
std::string formattedString;
|
||||
applyFormatting(format, formattedString, args);
|
||||
|
||||
bool hasEndingLinefeed = false;
|
||||
|
||||
if (formattedString.length() > 0)
|
||||
hasEndingLinefeed = formattedString[formattedString.length() - 1] == '\n';
|
||||
|
||||
if (hasEndingLinefeed)
|
||||
formattedString = formattedString.substr(0, formattedString.length()-1);
|
||||
|
||||
log(formattedString, hasEndingLinefeed, level, file, currentLine, auto_line);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// stores an association between thread -> log level -> current line buffer
|
||||
std::unordered_map<std::thread::id, std::unordered_map<LOG_LEVEL, std::string>> thread_local_strings;
|
||||
|
||||
void logger::log_internal(const std::string& str) const {
|
||||
auto id = std::this_thread::get_id();
|
||||
auto th_str = thread_local_strings[id][level];
|
||||
th_str += str;
|
||||
|
||||
if (blt::string::contains(str, "\n")){
|
||||
// make sure new lines are properly formatted to prevent danging lines. Ie "[trace]: .... [debug]: ...."
|
||||
bool hasEndingLinefeed = str[str.length()-1] == '\n';
|
||||
if (level == BLT_NONE) {
|
||||
std::cout << th_str;
|
||||
} else
|
||||
logging::log(th_str, false, level, "", -1, !hasEndingLinefeed);
|
||||
thread_local_strings[id][level] = "";
|
||||
} else {
|
||||
thread_local_strings[id][level] = th_str;
|
||||
}
|
||||
}
|
||||
|
||||
void flushLogger_internal(std::thread::id id, LOG_LEVEL level){
|
||||
auto th_str = thread_local_strings[id][level];
|
||||
if (th_str.empty())
|
||||
return;
|
||||
bool hasEndingLinefeed = th_str[th_str.length() - 1] == '\n';
|
||||
logging::log(th_str, false, level, "", -1, !hasEndingLinefeed);
|
||||
thread_local_strings[id][level] = "";
|
||||
}
|
||||
|
||||
void logger::flush() const {
|
||||
for (const auto& id : thread_local_strings) {
|
||||
flushLogger_internal(id.first, level);
|
||||
}
|
||||
}
|
||||
|
||||
void logger::flush_all() {
|
||||
for (const auto& id : thread_local_strings) {
|
||||
for (const auto& level : thread_local_strings[id.first]){
|
||||
flushLogger_internal(id.first, level.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void flush() {
|
||||
logger::flush_all();
|
||||
std::cerr.flush();
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
void testLogging() {
|
||||
trace << "Trace Test!\n";
|
||||
debug << "Debug Test!\n";
|
||||
info << "Info Test!\n";
|
||||
warn << "Warn Test!\n";
|
||||
error << "Error Test!\n";
|
||||
fatal << "Fatal Test!\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -18,10 +18,10 @@ void run_logging() {
|
|||
BLT_ERROR("Hello World!\n");
|
||||
BLT_FATAL("Hello World!\n");
|
||||
|
||||
blt::logging::trace << "Hello! " << "Double stream insertion! " << 51 << 65 << " ";
|
||||
blt::logging::trace << "Same Line! ";
|
||||
blt::logging::trace << "Write the end!\n";
|
||||
blt::logging::trace << "Seeee\n Super\n";
|
||||
// blt::logging::trace << "Hello! " << "Double stream insertion! " << 51 << 65 << " ";
|
||||
// blt::logging::trace << "Same Line! ";
|
||||
// blt::logging::trace << "Write the end!\n";
|
||||
// blt::logging::trace << "Seeee\n Super\n";
|
||||
|
||||
std::string hello = "superSexyMax";
|
||||
std::cout << "String starts with: " << blt::string::contains(hello, "superSexyMaxE") << "\n";
|
||||
|
|
|
@ -19,10 +19,10 @@ int main() {
|
|||
|
||||
nbt_tests();
|
||||
|
||||
blt::logging::trace << "Test Output!\n";
|
||||
blt::logging::trace << 5;
|
||||
blt::logging::trace << 5;
|
||||
blt::logging::trace << 5;
|
||||
// blt::logging::trace << "Test Output!\n";
|
||||
// blt::logging::trace << 5;
|
||||
// blt::logging::trace << 5;
|
||||
// blt::logging::trace << 5;
|
||||
|
||||
blt::logging::flush();
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ static void runProfilingAndTableTests() {
|
|||
BLT_END_INTERVAL("Help", "UnderSet" + std::to_string(i));
|
||||
}
|
||||
|
||||
BLT_PRINT_PROFILE("Help", blt::logging::LOG_LEVEL::BLT_TRACE);
|
||||
BLT_PRINT_PROFILE("Help", blt::logging::log_level::TRACE);
|
||||
BLT_TRACE("");
|
||||
|
||||
blt::string::TableFormatter formatter;
|
||||
|
|
|
@ -140,9 +140,9 @@ static inline void test_queues() {
|
|||
fill_queues();
|
||||
random_access();
|
||||
|
||||
BLT_PRINT_PROFILE("Insert", blt::logging::LOG_LEVEL::BLT_INFO, true);
|
||||
BLT_PRINT_PROFILE("Access", blt::logging::LOG_LEVEL::BLT_INFO, true);
|
||||
BLT_PRINT_PROFILE("Random", blt::logging::LOG_LEVEL::BLT_INFO, true);
|
||||
BLT_PRINT_PROFILE("Insert", blt::logging::log_level::INFO, true);
|
||||
BLT_PRINT_PROFILE("Access", blt::logging::log_level::INFO, true);
|
||||
BLT_PRINT_PROFILE("Random", blt::logging::log_level::INFO, true);
|
||||
}
|
||||
|
||||
#endif //BLT_TESTS_QUEUE_TESTS_H
|
||||
|
|
Loading…
Reference in New Issue