flatten 20260225
This commit is contained in:
68
tjp/core/log/Color.cpp
Executable file
68
tjp/core/log/Color.cpp
Executable file
@@ -0,0 +1,68 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#if defined(TJP_CORE_HEADER_ONLY)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <iosfwd>
|
||||
#include "Color.hpp"
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool isColorAvailable(std::ostream &o)
|
||||
{
|
||||
return o.iword(SupportsColorFlag::id());
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::ostream &operator <<(std::ostream &o, const Color &color)
|
||||
{
|
||||
if (isColorAvailable(o))
|
||||
o << color.value;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::ostream &operator <<(std::ostream &o, const Colored<const char *> &s)
|
||||
{
|
||||
if (!s.v)
|
||||
return o;
|
||||
|
||||
if (!isColorAvailable(o))
|
||||
return o << s.v;
|
||||
|
||||
auto sum = 0;
|
||||
for (auto i=0; i<128; ++i)
|
||||
{
|
||||
if (s.v[i] == 0)
|
||||
break;
|
||||
sum += s.v[i];
|
||||
}
|
||||
|
||||
auto code = sum % 128 + 1;
|
||||
|
||||
o
|
||||
<< "\033[38;5;" << code << "m"
|
||||
<< s.v
|
||||
<< "\033[0m";
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::ostream &operator <<(std::ostream &o, const ColoredStory &v)
|
||||
{
|
||||
if (v.story)
|
||||
o << "[" << color(v.story) << "] ";
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
95
tjp/core/log/Color.hpp
Executable file
95
tjp/core/log/Color.hpp
Executable file
@@ -0,0 +1,95 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../header_only/compile.h"
|
||||
#include <ios>
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
struct ColoredStory
|
||||
{
|
||||
const char *story;
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
struct Colored
|
||||
{
|
||||
const T &v;
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
struct SupportsColorFlag_
|
||||
{
|
||||
static inline int id_ = -1;
|
||||
} ;
|
||||
|
||||
struct SupportsColorFlag : SupportsColorFlag_<void>
|
||||
{
|
||||
static int id()
|
||||
{
|
||||
if (id_ < 0)
|
||||
id_ = std::ios_base::xalloc();
|
||||
|
||||
return id_;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
Colored<T> color(const T &v) { return { v }; }
|
||||
|
||||
std::ostream &operator <<(std::ostream &o, const ColoredStory &v);
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const Colored<T> &);
|
||||
|
||||
template<>
|
||||
std::ostream &operator <<(std::ostream &o, const Colored<const char *> &);
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
|
||||
#define xLogColorStory(story) tjp::core::log::detail::ColoredStory { story }
|
||||
|
||||
struct Color
|
||||
{
|
||||
const char *value;
|
||||
|
||||
Color(const char *value_) :
|
||||
value(value_)
|
||||
{}
|
||||
};
|
||||
|
||||
std::ostream &operator <<(std::ostream &o, const Color &color);
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace tjp::core::log {
|
||||
|
||||
using detail::Color;
|
||||
using detail::color;
|
||||
|
||||
//the following are UBUNTU/LINUX, and MacOS ONLY terminal color codes.
|
||||
const Color RESET ="\033[0m";
|
||||
const Color BLACK = "\033[30m";
|
||||
const Color RED = "\033[31m";
|
||||
const Color GREEN = "\033[32m";
|
||||
const Color YELLOW = "\033[33m";
|
||||
const Color BLUE = "\033[34m";
|
||||
const Color MAGENTA = "\033[35m";
|
||||
const Color CYAN = "\033[36m";
|
||||
const Color WHITE = "\033[37m";
|
||||
const Color BOLDBLACK = "\033[1m\033[30m";
|
||||
const Color BOLDRED = "\033[1m\033[31m";
|
||||
const Color BOLDGREEN = "\033[1m\033[32m";
|
||||
const Color BOLDYELLOW = "\033[1m\033[33m";
|
||||
const Color BOLDBLUE = "\033[1m\033[34m";
|
||||
const Color BOLDMAGENTA = "\033[1m\033[35m";
|
||||
const Color BOLDCYAN = "\033[1m\033[36m";
|
||||
const Color BOLDWHITE = "\033[1m\033[37m";
|
||||
|
||||
}
|
||||
|
||||
#include "Color.inl"
|
||||
21
tjp/core/log/Color.inl
Executable file
21
tjp/core/log/Color.inl
Executable file
@@ -0,0 +1,21 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include "Log.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const Colored<T> &v)
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << v.v;
|
||||
o << color(s.str().c_str());
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
244
tjp/core/log/Detail.cpp
Executable file
244
tjp/core/log/Detail.cpp
Executable file
@@ -0,0 +1,244 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#if defined(TJP_CORE_HEADER_ONLY)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "Detail.hpp"
|
||||
|
||||
#include "Util.inl"
|
||||
#include "../io/memstream.hpp"
|
||||
#include "../algorithm/set_has.hpp"
|
||||
#include "../algorithm/unused.hpp"
|
||||
#include "../string/as_string_view.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool operator==(const Story &lhs, const Story &rhs)
|
||||
{
|
||||
return
|
||||
lhs.hash == rhs.hash &&
|
||||
lhs.name == rhs.name;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool operator<(const Story &lhs, const Story &rhs)
|
||||
{
|
||||
if (lhs.hash == rhs.hash)
|
||||
return lhs.name < rhs.name;
|
||||
|
||||
return lhs.hash < rhs.hash;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Manager::Manager() :
|
||||
file(this),
|
||||
termSupportsColor(detectIfColorIsAvailable())
|
||||
{
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Manager &getManager()
|
||||
{
|
||||
static Manager manager;
|
||||
return manager;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Stories::clear()
|
||||
{
|
||||
all = false;
|
||||
names.clear();
|
||||
activated.clear();
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Stories::activate(const Story &s)
|
||||
{
|
||||
if (s.name == "*")
|
||||
{
|
||||
all = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!set_has(activated, s))
|
||||
{
|
||||
names.push_back(String(s.name));
|
||||
|
||||
Story s_ = s;
|
||||
s_.name = names.back();
|
||||
activated.insert (s_);
|
||||
}
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool Stories::isActivated (const Story &s)
|
||||
{
|
||||
return all || set_has(activated, s);
|
||||
}
|
||||
|
||||
// ---------------
|
||||
|
||||
struct File::I {
|
||||
std::ofstream file;
|
||||
} ;
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
File::File(Manager *manager_) :
|
||||
manager(manager_)
|
||||
{
|
||||
i = new I();
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
File::~File()
|
||||
{
|
||||
delete i;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
Settings &File::getSettings()
|
||||
{
|
||||
return manager->settings;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void File::start()
|
||||
{
|
||||
if (i->file.is_open())
|
||||
i->file.close();
|
||||
|
||||
auto &settings = getSettings();
|
||||
|
||||
i->file.open(
|
||||
settings.LogFileName,
|
||||
settings.AppendToLogFile ?
|
||||
std::ios::app :
|
||||
std::ios::out
|
||||
);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void File::write(const StringView &s)
|
||||
{
|
||||
if (!i->file)
|
||||
return;
|
||||
|
||||
sizeWritten += s.size();
|
||||
i->file << s;
|
||||
i->file.flush();
|
||||
|
||||
checkRotate();
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void File::checkRotate()
|
||||
{
|
||||
auto &settings = getSettings();
|
||||
|
||||
if (settings.RotateSize != 0 && sizeWritten > settings.RotateSize)
|
||||
rotate();
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void File::rotate()
|
||||
{
|
||||
char data[256];
|
||||
GetTimeStamp(data, std::size(data), '_');
|
||||
rotateTo(getSettings().LogFileName + "." + data);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void File::rotateTo(const std::string_view &name_)
|
||||
{
|
||||
i->file.close();
|
||||
|
||||
auto name = std::string(name_);
|
||||
remove(name.c_str());
|
||||
rename(getSettings().LogFileName.c_str(), name.c_str());
|
||||
|
||||
sizeWritten = 0;
|
||||
start();
|
||||
}
|
||||
|
||||
// -------
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Manager::writeToConsole (const StringView &s)
|
||||
{
|
||||
auto lock = std::lock_guard<Mutex>(mutex);
|
||||
|
||||
char data[256];
|
||||
core::io::omemstream_ out;
|
||||
out << "XD " << __GetLogStamp__(data, termSupportsColor) << s;
|
||||
|
||||
auto externalConsolePrint = external.console;
|
||||
if (externalConsolePrint)
|
||||
return externalConsolePrint(core::as_string_view(out.data));
|
||||
|
||||
fwrite((void *)out.data.data(), 1, out.data.size(), stdout);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Manager::writeToDebugger (const StringView &s)
|
||||
{
|
||||
auto lock = std::lock_guard<Mutex>(mutex);
|
||||
|
||||
char data[256];
|
||||
unused(data);
|
||||
|
||||
auto externalDebuggerPrint = external.debugger;
|
||||
if (externalDebuggerPrint)
|
||||
{
|
||||
#ifdef SYS_ANDROID
|
||||
core::io::omemstream_ out_;
|
||||
out_ << __GetLogStamp__(data, false) << s;
|
||||
auto out = as_string_view(out_.data);
|
||||
#else
|
||||
auto &out = s;
|
||||
#endif
|
||||
externalDebuggerPrint(out);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
OutputDebugString (__GetLogStamp__(data, false));
|
||||
OutputDebugString (s.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Manager::writeToLog (const StringView &s)
|
||||
{
|
||||
auto externalLogPrint = external.log;
|
||||
bool willWrite = externalLogPrint || settings.LogToFileActivated;
|
||||
if (willWrite)
|
||||
{
|
||||
char data[256];
|
||||
core::io::omemstream_ out;
|
||||
out << __GetLogStamp__(data, false) << s;
|
||||
auto outs = as_string_view(out.data);
|
||||
|
||||
if (externalLogPrint)
|
||||
externalLogPrint(outs);
|
||||
|
||||
if (settings.LogToFileActivated)
|
||||
file.write(outs);
|
||||
|
||||
#ifdef USE_OSX_LOG
|
||||
// Define a custom log category
|
||||
static os_log_t os_log_ = os_log_create("com.timprepscius.crown", "std");
|
||||
|
||||
// Log a message with default log level
|
||||
os_log(os_log_, "%s", s.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
10
tjp/core/log/Detail.h
Executable file
10
tjp/core/log/Detail.h
Executable file
@@ -0,0 +1,10 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
} // namespace
|
||||
|
||||
126
tjp/core/log/Detail.hpp
Executable file
126
tjp/core/log/Detail.hpp
Executable file
@@ -0,0 +1,126 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../header_only/compile.h"
|
||||
|
||||
#include "../string/String.hpp"
|
||||
#include "../string/StringView.hpp"
|
||||
#include "../types/Types.h"
|
||||
|
||||
#include "../containers/List.hpp"
|
||||
#include "../containers/Set.hpp"
|
||||
|
||||
#include "../const_hash/Hash.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
struct Manager;
|
||||
|
||||
struct Story
|
||||
{
|
||||
using Hash = u32;
|
||||
Hash hash;
|
||||
StringView name;
|
||||
|
||||
constexpr Story(const char *s) :
|
||||
hash(hash_compile<Hash>(s)),
|
||||
name(s)
|
||||
{}
|
||||
} ;
|
||||
|
||||
struct Settings
|
||||
{
|
||||
String LogFileName = "log.txt";
|
||||
bool AppendToLogFile = false;
|
||||
bool LogToFileActivated = false;
|
||||
bool LogToDebuggerActivated = false;
|
||||
bool LogToConsoleActivated = true;
|
||||
bool LogInExecutableDirectory = false;
|
||||
|
||||
// zero for never rotate
|
||||
u64 RotateSize = 10 * (1024 * 1024);
|
||||
} ;
|
||||
|
||||
typedef void (*TimPrepsciusCoreLogExternalPrintPtr)(const StringView &);
|
||||
|
||||
|
||||
struct External
|
||||
{
|
||||
TimPrepsciusCoreLogExternalPrintPtr debugger = nullptr;
|
||||
TimPrepsciusCoreLogExternalPrintPtr log = nullptr;
|
||||
TimPrepsciusCoreLogExternalPrintPtr console = nullptr;
|
||||
} ;
|
||||
|
||||
struct Stories
|
||||
{
|
||||
bool all = false;
|
||||
|
||||
Set<Story> activated;
|
||||
List<String> names;
|
||||
|
||||
void clear();
|
||||
void activate(const Story &s);
|
||||
bool isActivated (const Story &s);
|
||||
} ;
|
||||
|
||||
struct File
|
||||
{
|
||||
struct I;
|
||||
|
||||
Manager *manager;
|
||||
Settings &getSettings();
|
||||
|
||||
File (Manager *);
|
||||
~File();
|
||||
|
||||
I *i;
|
||||
u64 sizeWritten = 0;
|
||||
|
||||
void start();
|
||||
void write(const StringView &s);
|
||||
|
||||
void checkRotate();
|
||||
void rotate();
|
||||
void rotateTo(const std::string_view &name);
|
||||
} ;
|
||||
|
||||
struct Manager
|
||||
{
|
||||
using Mutex = std::mutex;
|
||||
Mutex mutex;
|
||||
|
||||
External external;
|
||||
Settings settings;
|
||||
Stories stories;
|
||||
File file;
|
||||
|
||||
bool termSupportsColor = false;
|
||||
|
||||
Manager();
|
||||
|
||||
void start();
|
||||
void rotate();
|
||||
void rotateTo(const StringView &);
|
||||
|
||||
void writeToLog(const StringView &);
|
||||
void writeToDebugger(const StringView &);
|
||||
void writeToConsole(const StringView &);
|
||||
|
||||
template<typename F>
|
||||
auto with_lock(F &&f)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(mutex);
|
||||
f(*this);
|
||||
}
|
||||
} ;
|
||||
|
||||
Manager &getManager();
|
||||
|
||||
std::string methodName(const char *prettyFunction);
|
||||
|
||||
} // namespace
|
||||
14
tjp/core/log/Log+Precision.h
Executable file
14
tjp/core/log/Log+Precision.h
Executable file
@@ -0,0 +1,14 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Debug and Error logging utility methods and macros used throughout
|
||||
* the application
|
||||
*/
|
||||
|
||||
#include <iomanip>
|
||||
|
||||
#define LOG_PRECISION(x) std::fixed << std::setprecision(x)
|
||||
171
tjp/core/log/Log.cpp
Executable file
171
tjp/core/log/Log.cpp
Executable file
@@ -0,0 +1,171 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#if defined(TJP_CORE_HEADER_ONLY)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
#include <fstream>
|
||||
|
||||
#include "../system/System.h"
|
||||
|
||||
#if defined(SYS_IOS) || defined(SYS_MAC)
|
||||
// #define USE_OSX_LOG
|
||||
#endif
|
||||
|
||||
#if defined(USE_OSX_LOG)
|
||||
#include <os/log.h>
|
||||
#endif
|
||||
|
||||
namespace tjp::core::log {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void ActivateStory (const Story &s)
|
||||
{
|
||||
getManager().stories.activate(s);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalDebugPrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
manager.external.debugger = f;
|
||||
});
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
manager.external.log = f;
|
||||
});
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
manager.external.console = f;
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Start ()
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
if (manager.settings.LogToFileActivated)
|
||||
manager.file.start();
|
||||
});
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void RotateTo(const std::string_view &fileName)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
if (manager.settings.LogToFileActivated)
|
||||
manager.file.rotateTo(fileName);
|
||||
});
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Rotate()
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
if (manager.settings.LogToFileActivated)
|
||||
manager.file.rotate();
|
||||
});
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void ActivateStory_(const Story &s)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
manager.stories.activate(s);
|
||||
});
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void ActivateStories (const std::string &filename)
|
||||
{
|
||||
std::ifstream in(filename.c_str());
|
||||
|
||||
if (!in)
|
||||
return;
|
||||
|
||||
while (!in.eof())
|
||||
{
|
||||
char line[80];
|
||||
in.getline(line, 80);
|
||||
line[79]='\0';
|
||||
|
||||
for (int i=0;i<80; i++)
|
||||
{
|
||||
if (line[i] == '\r' || line[i]=='\n' || line[i]=='#' || line[i] =='\0')
|
||||
{
|
||||
line[i] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (line[0] == 0)
|
||||
continue;
|
||||
|
||||
xLogActivateStory(line);
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Initialize (const Settings &settings_)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
manager.settings = settings_;
|
||||
});
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void Initialize (const std::string &fileName)
|
||||
{
|
||||
getManager().with_lock([&](auto &manager) {
|
||||
auto &settings = manager.settings;
|
||||
|
||||
settings.LogFileName = fileName;
|
||||
settings.LogToFileActivated = !fileName.empty();
|
||||
settings.LogToConsoleActivated = !settings.LogToFileActivated;
|
||||
settings.LogToDebuggerActivated = false;
|
||||
|
||||
settings.LogInExecutableDirectory = false;
|
||||
});
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalDebuggerPrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
tjp::core::log::setExternalDebugPrint(f);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
tjp::core::log::setExternalLogPrint(f);
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f)
|
||||
{
|
||||
tjp::core::log::setExternalConsolePrint(f);
|
||||
}
|
||||
242
tjp/core/log/Log.h
Executable file
242
tjp/core/log/Log.h
Executable file
@@ -0,0 +1,242 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../header_only/compile.h"
|
||||
|
||||
#include "../debug/Debug.h"
|
||||
#include "Detail.hpp"
|
||||
#include "Color.hpp"
|
||||
|
||||
#include "../io/memstream.hpp"
|
||||
#include "../string/as_string_view.hpp"
|
||||
|
||||
typedef void (*TimPrepsciusCoreLogExternalPrintPtr)(const std::string_view &);
|
||||
void setExternalDebugPrint(TimPrepsciusCoreLogExternalPrintPtr f);
|
||||
void setExternalLogPrint(TimPrepsciusCoreLogExternalPrintPtr f);
|
||||
void setExternalConsolePrint(TimPrepsciusCoreLogExternalPrintPtr f);
|
||||
|
||||
namespace tjp::core::log {
|
||||
|
||||
using detail::Settings;
|
||||
using detail::Story;
|
||||
using detail::getManager;
|
||||
|
||||
#define xLogManager() tjp::core::log::getManager()
|
||||
|
||||
void Initialize(const std::string &fileName = {});
|
||||
void Initialize(const Settings &);
|
||||
#define xLogInitialize(...) tjp::core::log::Initialize(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* the functions actually write strings
|
||||
*/
|
||||
void WriteToLog (const std::string &);
|
||||
void WriteToDebugger (const std::string &);
|
||||
void WriteToConsole (const std::string &);
|
||||
|
||||
/**
|
||||
* functions to turn on/off logs and query their state
|
||||
*/
|
||||
|
||||
void ActivateStory (const Story &s);
|
||||
void RotateTo (const std::string_view &rotateTo);
|
||||
void Rotate ();
|
||||
void Start();
|
||||
void Stop ();
|
||||
|
||||
void ActivateStories (const std::string &fileName);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUG)
|
||||
#ifndef LOG_DEBUG
|
||||
#define LOG_DEBUG
|
||||
#endif
|
||||
#else
|
||||
#ifndef LOG_DEBUG
|
||||
// #define LOG_DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define LOG_RELEASE
|
||||
#define LOG_ERROR
|
||||
|
||||
#ifdef TESTING
|
||||
#define LOG_TEST
|
||||
#endif
|
||||
|
||||
#if defined(LOG_DEBUG) || defined(LOG_RELEASE)
|
||||
#define xLogActivateStories(s) tjp::core::log::ActivateStories(s)
|
||||
|
||||
#define xLogActivateStory(s) \
|
||||
tjp::core::log::ActivateStory(s); \
|
||||
sLogRelease(s, "logging activated")
|
||||
|
||||
#define xLogStart() tjp::core::log::Start()
|
||||
#define xLogStop() tjp::core::log::Stop()
|
||||
#else
|
||||
#define xLogActivateStories(s)
|
||||
#define xLogActivateStory(x)
|
||||
#define xLogStoryActivated(x)
|
||||
#define xLogStart(x)
|
||||
#define xLogStop(x)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#define xLogDebugStreamName "Debug"
|
||||
#define xLogReleaseStreamName "Release"
|
||||
#define xLogErrorStreamName "Error"
|
||||
#define xLogSettings tjp::core::log::Settings::global
|
||||
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
#define xLogMethodName(x) tjp::core::log::detail::methodName(x)
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
#define xLogNoStory "_"
|
||||
#define xLogAllStories "*"
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
#ifdef LOG_DEBUG
|
||||
#define xFormatDebugger(p,n,story,...) __FILE__ << "(" << __LINE__ << ") : " << xLogColorStory(story) << n << " " << __VA_ARGS__ << std::endl
|
||||
#else
|
||||
#define xFormatDebugger(p,n,story,...) story << n << " " << __VA_ARGS__ << std::endl
|
||||
#endif
|
||||
|
||||
#define xFormatLog(p,n,story,...) "[" << story << "] " << n << " " << __VA_ARGS__ << std::endl
|
||||
#define xFormatConsole(p,n,story,...) xLogColorStory(story) << n << " " << __VA_ARGS__ << std::endl
|
||||
|
||||
#define xLogTo(useColor, where, ...) \
|
||||
{ \
|
||||
::tjp::core::io::omemstream_ _out; \
|
||||
if (useColor) \
|
||||
_out.iword(::tjp::core::log::detail::SupportsColorFlag::id()) = 1; \
|
||||
_out << __VA_ARGS__; \
|
||||
where(::tjp::core::as_string_view(_out.data)); \
|
||||
}
|
||||
|
||||
#define _xLog__(manager, n_,condition,story,...) \
|
||||
{ \
|
||||
if (condition) \
|
||||
{ \
|
||||
auto n = xLogMethodName(n_); \
|
||||
if (manager.settings.LogToDebuggerActivated) \
|
||||
xLogTo(false, manager.writeToDebugger, xFormatDebugger(xLogDebugStreamName,n,story,__VA_ARGS__)) \
|
||||
if (manager.settings.LogToFileActivated) \
|
||||
xLogTo(false, manager.writeToLog, xFormatLog(xLogDebugStreamName,n,story,__VA_ARGS__)); \
|
||||
if (manager.settings.LogToConsoleActivated) \
|
||||
xLogTo(manager.termSupportsColor, manager.writeToConsole, xFormatConsole(xLogDebugStreamName,n,story,__VA_ARGS__)); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define _xLog_(n_,condition,story,...) \
|
||||
{ \
|
||||
auto &manager = xLogManager(); \
|
||||
_xLog__(manager, n_,condition,story, __VA_ARGS__); \
|
||||
} \
|
||||
|
||||
#define _xLog(n_,condition,story,...) \
|
||||
{ \
|
||||
auto &manager = xLogManager(); \
|
||||
if (manager.stories.isActivated(story)) \
|
||||
_xLog__(manager, n_,condition,story, __VA_ARGS__); \
|
||||
} \
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
#define _xLogDebug(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
|
||||
#define _xLogTest(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define LOG_USE_FUNCTION_NAMES
|
||||
#else
|
||||
// #define LOG_USE_FUNCTION_NAMES
|
||||
#endif
|
||||
|
||||
#ifdef LOG_USE_FUNCTION_NAMES
|
||||
#define _xLogRelease(condition,story,...) _xLog(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
|
||||
#define _xLogError(condition,story,...) _xLog_(__PRETTY_FUNCTION__,condition,story,__VA_ARGS__)
|
||||
#else
|
||||
#define _xLogRelease(condition,story,...) _xLog(nullptr,condition,story,__VA_ARGS__)
|
||||
#define _xLogError(condition,story,...) _xLog_(nullptr,condition,story,__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
#ifdef LOG_DEBUG
|
||||
#define xLogDebug(...) _xLogDebug(true,xLogNoStory,__VA_ARGS__)
|
||||
#define xLogDebugIf(condition,...) _xLogDebug(condition,xLogNoStory,__VA_ARGS__)
|
||||
|
||||
#define sLogDebug(story,...) _xLogDebug(true,story,__VA_ARGS__)
|
||||
#define sLogDebugIf(condition,story,...) _xLogDebug(condition,story,__VA_ARGS__)
|
||||
|
||||
#define fLogDebug(x) xLogDebug(x)
|
||||
|
||||
#define LOG_DEBUG_ONLY(...) __VA_ARGS__
|
||||
#else
|
||||
#define xLogDebug(...)
|
||||
#define xLogDebugIf(...)
|
||||
#define sLogDebug(...)
|
||||
#define sLogDebugIf(...)
|
||||
#define fLogDebug(...)
|
||||
|
||||
#define LOG_DEBUG_ONLY(...)
|
||||
#endif
|
||||
|
||||
#ifdef LOG_RELEASE
|
||||
#define xLogRelease(...) _xLogRelease(true,xLogNoStory,__VA_ARGS__)
|
||||
#define xLogReleaseIf(condition,...) _xLogRelease(condition,xLogNoStory,__VA_ARGS__)
|
||||
|
||||
#define sLogRelease(story,...) _xLogRelease(true,story,__VA_ARGS__)
|
||||
#define sLogReleaseIf(condition,story,...) _xLogRelease(condition,story,__VA_ARGS__)
|
||||
#else
|
||||
#define xLogRelease(...)
|
||||
#define xLogReleaseIf(...)
|
||||
#define sLogRelease(...)
|
||||
#define sLogReleaseIf(...)
|
||||
#endif
|
||||
|
||||
#ifdef LOG_TEST
|
||||
#define xLogTest(x) _xLogTest(true,xLogNoStory,x)
|
||||
#define xLogTestIf(condition,x) _xLogTest(condition,xLogNoStory,x)
|
||||
|
||||
#define sLogTest(story,x) _xLogTest(true,story,x)
|
||||
#define sLogTestIf(condition,story,x) _xLogTest(condition,story,x)
|
||||
#else
|
||||
#define xLogTest(...)
|
||||
#define xLogTestIf(...)
|
||||
#define sLogTest(...)
|
||||
#define sLogTestIf(...)
|
||||
#endif
|
||||
|
||||
#ifdef LOG_ERROR
|
||||
#define xLogError(...) _xLogError(true,xLogNoStory,__VA_ARGS__)
|
||||
#define sLogError(story,...) _xLogError(true,story,__VA_ARGS__)
|
||||
#define sLogErrorIf(condition,story,...) _xLogError(condition,story,__VA_ARGS__)
|
||||
#else
|
||||
#define xLogError(...)
|
||||
#define sLogError(...)
|
||||
#define sLogErrorIf(...)
|
||||
#endif
|
||||
|
||||
// --------------------------------------
|
||||
|
||||
#define nLogDebug(...)
|
||||
#define nLogRelease(...)
|
||||
#define nLogError(...)
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef TJP_CORE_HEADER_ONLY_COMPILE
|
||||
#include "Detail.cpp"
|
||||
#include "Color.cpp"
|
||||
#include "Log.cpp"
|
||||
#endif
|
||||
|
||||
192
tjp/core/log/LogOf.h
Executable file
192
tjp/core/log/LogOf.h
Executable file
@@ -0,0 +1,192 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../type_traits/is_iterable.hpp"
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include "demangle.h"
|
||||
|
||||
#include "../types/Types+IO.h"
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
template<typename T>
|
||||
struct LogOf
|
||||
{
|
||||
const T &t;
|
||||
explicit LogOf(const T &t_) : t(t_) {}
|
||||
|
||||
std::ostream &operator()(std::ostream &o) const
|
||||
{
|
||||
return o << t;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
struct LogOfV
|
||||
{
|
||||
T t;
|
||||
explicit LogOfV(T t_) : t(t_) {}
|
||||
|
||||
std::ostream &operator()(std::ostream &o) const
|
||||
{
|
||||
return o << t;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
auto logOfV(T t) { return LogOfV<T>(t); }
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const LogOf<T> &v)
|
||||
{
|
||||
return v(o);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const LogOfV<T> &v)
|
||||
{
|
||||
return v(o);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value, T>::type* = nullptr>
|
||||
auto logOf(const T &t);
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value, T>::type* = nullptr>
|
||||
auto logOf(const T &t);
|
||||
|
||||
|
||||
inline
|
||||
auto logOf(const std::string &t) { return t; }
|
||||
|
||||
inline
|
||||
auto logOf(bool v) { return v ? "true" : "false"; }
|
||||
|
||||
template<typename A, typename B>
|
||||
auto logOf(const std::pair<A,B> &t) {
|
||||
std::ostringstream o;
|
||||
o << "(" << logOf(t.first) << "," << logOf(t.second) << ")";
|
||||
return o.str();
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value, T>::type*>
|
||||
auto logOf(const T &t) { return LogOf<T>(t); }
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value, T>::type*>
|
||||
auto logOf(const T &t) {
|
||||
std::ostringstream o;
|
||||
o << "[";
|
||||
auto first = true;
|
||||
for (auto v : t)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
o << ",";
|
||||
}
|
||||
|
||||
o << logOf(v);
|
||||
first = false;
|
||||
}
|
||||
o << "]";
|
||||
|
||||
return o.str();
|
||||
}
|
||||
|
||||
inline
|
||||
auto logOf(const std::string_view &v)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct NullOrValue
|
||||
{
|
||||
const T *t;
|
||||
explicit NullOrValue(const T *t_) : t(t_) {}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const NullOrValue<T> &v)
|
||||
{
|
||||
if (v.t)
|
||||
return o << logOf(*v.t);
|
||||
|
||||
return o << "null";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto logOfRef(T *t)
|
||||
{
|
||||
return NullOrValue<T>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct NullOrPtrValue
|
||||
{
|
||||
const T *t;
|
||||
explicit NullOrPtrValue(const T *t_) : t(t_) {}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const NullOrPtrValue<T> &v)
|
||||
{
|
||||
if (v.t)
|
||||
return o << "[" << v.t << "] " << logOf(*v.t);
|
||||
|
||||
return o << "null";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto logPtrValue(T *t)
|
||||
{
|
||||
return NullOrPtrValue<T>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto logPtr(T *t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct SpaceBefore
|
||||
{
|
||||
const T &t;
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
std::ostream &operator <<(std::ostream &o, const SpaceBefore<T> &v)
|
||||
{
|
||||
return o << " " << logOf(v.t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
SpaceBefore<T> _logOf(const T &t)
|
||||
{
|
||||
return SpaceBefore<T> { t };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto logOfDispatch(T &&t)
|
||||
{
|
||||
return logOf(std::forward<T>(t));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto logOfDispatchV(T t)
|
||||
{
|
||||
return logOfV(t);
|
||||
}
|
||||
|
||||
#define logOfThis(x) "[" << tjp::core::logPtr(x) << "] "
|
||||
#define logVar(x) #x << " " << tjp::core::logOfDispatch(x) << " "
|
||||
#define logVarV(x) #x << " " << tjp::core::logOfDispatchV(x) << " "
|
||||
#define logType(x) #x << " " << tjp::core::type_id<x>().name() << " "
|
||||
#define logLabel(l) l << " "
|
||||
#define logLabelVar(l, x) l << " " << tjp::core::logOfDispatch(x) << " "
|
||||
#define logLabelVarV(l, x) l << " " << tjp::core::logOfDispatchV(x) << " "
|
||||
|
||||
} // namespace
|
||||
170
tjp/core/log/Util.inl
Executable file
170
tjp/core/log/Util.inl
Executable file
@@ -0,0 +1,170 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../algorithm/set_has.hpp"
|
||||
#include "../algorithm/is_in.hpp"
|
||||
|
||||
namespace tjp::core::log::detail {
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
long timeSinceEpochMillisec()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return duration_cast<microseconds>(system_clock::now().time_since_epoch()).count() % 1000000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
bool detectIfColorIsAvailable()
|
||||
{
|
||||
// is the stdout redirected, if so no color
|
||||
if (getenv("FORCE_COLOR"))
|
||||
return true;
|
||||
|
||||
if (!isatty(fileno(stdout)))
|
||||
return false;
|
||||
|
||||
const char* term = getenv("TERM");
|
||||
if (!term)
|
||||
return false;
|
||||
|
||||
// Some terminals support color
|
||||
std::string_view term_(term);
|
||||
if (term_ == "dumb")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
using ThreadID = uint64_t;
|
||||
ThreadID getCurrentThreadID();
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
ThreadID getCurrentThreadID ()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetCurrentThreadId();
|
||||
#else
|
||||
return (ThreadID)pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
char * GetTimeStamp (char *data, int size, char marker=0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
__time64_t ltime;
|
||||
_time64( <ime );
|
||||
|
||||
char *timestr = _ctime64(<ime);
|
||||
// auto threadID = getCurrentThreadID();
|
||||
|
||||
#else
|
||||
std::time_t t = std::time(nullptr);
|
||||
char timestr[128];
|
||||
std::strftime(timestr, size, "%Y%m%d_%H:%M:%S", std::localtime(&t));
|
||||
// auto threadID = getCurrentThreadID();
|
||||
#endif
|
||||
|
||||
snprintf (data, size, "%s.%06ld", timestr, timeSinceEpochMillisec());
|
||||
|
||||
if (marker)
|
||||
for (auto *c=data; *c; ++c)
|
||||
if (*c == ':' || *c == '.')
|
||||
*c = marker;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
char * __GetLogStamp__ (char *data, bool colorize)
|
||||
{
|
||||
char timeStamp[128];
|
||||
GetTimeStamp(timeStamp, std::size(timeStamp));
|
||||
|
||||
auto threadID = getCurrentThreadID();
|
||||
auto dataSize = 256;
|
||||
|
||||
if (colorize)
|
||||
{
|
||||
// all of the threadIDs are divisible by 128, so we use 127
|
||||
auto color = (int)threadID % 127 + 1;
|
||||
#if defined(SYS_LINUX) || defined(SYS_ANDROID)
|
||||
snprintf (data, dataSize, "%s \033[38;5;%dm%lx\033[0m | ", timeStamp, color, threadID);
|
||||
#else
|
||||
snprintf (data, dataSize, "%s \033[38;5;%dm%llx\033[0m | ", timeStamp, color, threadID);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(SYS_LINUX) || defined(SYS_ANDROID)
|
||||
snprintf (data, dataSize, "%s %lx | ", timeStamp, threadID);
|
||||
#else
|
||||
snprintf (data, dataSize, "%s %llx | ", timeStamp, threadID);
|
||||
#endif
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
TJP_CORE_HEADER_ONLY_INLINE
|
||||
std::string methodName(const char *prettyFunction_)
|
||||
{
|
||||
if (!prettyFunction_)
|
||||
return "[]";
|
||||
|
||||
std::string_view prettyFunction(prettyFunction_);
|
||||
auto end = prettyFunction.find('(');
|
||||
if (end == std::string_view::npos)
|
||||
end = 0;
|
||||
auto begin = end;
|
||||
|
||||
auto stack = 0;
|
||||
decltype(begin) skipEnd = 0;
|
||||
decltype(begin) skipBegin = 0;
|
||||
for (begin = end; begin > 0; begin--)
|
||||
{
|
||||
if (prettyFunction[begin] == '>')
|
||||
{
|
||||
if (stack == 0)
|
||||
skipEnd = begin;
|
||||
stack++;
|
||||
}
|
||||
if (prettyFunction[begin] == '<')
|
||||
{
|
||||
stack--;
|
||||
if (stack == 0)
|
||||
skipBegin = begin;
|
||||
}
|
||||
|
||||
if (prettyFunction[begin] == ' ' && stack == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
while (begin < end && is_in(prettyFunction[begin], ' ', '&', '*'))
|
||||
begin = begin+1;
|
||||
|
||||
std::string part;
|
||||
|
||||
if (skipBegin != skipEnd)
|
||||
part = std::string(prettyFunction.substr(begin,skipBegin-begin+1)) + std::string(prettyFunction.substr(skipEnd, end-skipEnd));
|
||||
else
|
||||
part = prettyFunction.substr(begin,end-begin);
|
||||
|
||||
if (part.size() > 30)
|
||||
return part.substr(part.size() - 30);
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
96
tjp/core/log/_tests/LogOf.cpp
Executable file
96
tjp/core/log/_tests/LogOf.cpp
Executable file
@@ -0,0 +1,96 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/log/LogOf.h>
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
#include <tjp/core/string/String.hpp>
|
||||
#include <tjp/core/containers/Map.hpp>
|
||||
#include <tjp/core/containers/Vector.hpp>
|
||||
#include <tjp/core/containers/Optional+IO.hpp>
|
||||
|
||||
namespace tjp::core::log {
|
||||
namespace {
|
||||
|
||||
struct S {
|
||||
int i = 0;
|
||||
};
|
||||
|
||||
std::ostream &operator <<(std::ostream &o, const S &s)
|
||||
{
|
||||
return o << "S: " << s.i;
|
||||
}
|
||||
|
||||
SCENARIO("core::log::LogOf")
|
||||
{
|
||||
GIVEN("a struct")
|
||||
{
|
||||
S x { 42 };
|
||||
|
||||
WHEN("serialize with logOf")
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << logOf(x);
|
||||
String str = s.str();
|
||||
|
||||
REQUIRE(str == "S: 42");
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a vector")
|
||||
{
|
||||
Vector<int> x = { 1, 2, 3, 4 };
|
||||
|
||||
WHEN("serialize with logOf")
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << logOf(x);
|
||||
String str = s.str();
|
||||
|
||||
REQUIRE(str == "[1,2,3,4]");
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a map")
|
||||
{
|
||||
std::map<int, int> x = { {1,1}, {2,2}, {3,3}, {4,4} };
|
||||
|
||||
WHEN("serialize with logOf")
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << logOf(x);
|
||||
String str = s.str();
|
||||
|
||||
REQUIRE(str == "[(1,1),(2,2),(3,3),(4,4)]");
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("an optional")
|
||||
{
|
||||
std::optional<int> x;
|
||||
|
||||
WHEN("serialize with logOf")
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << logOf(x);
|
||||
String str = s.str();
|
||||
|
||||
REQUIRE(str == "null");
|
||||
}
|
||||
|
||||
WHEN("serialize value with logOf")
|
||||
{
|
||||
x = 1;
|
||||
|
||||
std::ostringstream s;
|
||||
s << logOf(x);
|
||||
String str = s.str();
|
||||
|
||||
REQUIRE(str == "1");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
16
tjp/core/log/demangle.cpp
Executable file
16
tjp/core/log/demangle.cpp
Executable file
@@ -0,0 +1,16 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <cxxabi.h>
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
char * demangle(const char *mangled)
|
||||
{
|
||||
int status;
|
||||
char * demangled = abi::__cxa_demangle(mangled,0,0,&status);
|
||||
return demangled;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
11
tjp/core/log/demangle.h
Executable file
11
tjp/core/log/demangle.h
Executable file
@@ -0,0 +1,11 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core {
|
||||
|
||||
char *demangle(const char *mangled);
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user