nostalgia/deps/ox/src/ox/std/trace.hpp

291 lines
7.3 KiB
C++

/*
* Copyright 2015 - 2024 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#ifdef OX_USE_STDLIB
#include <array>
#endif
#include "bstring.hpp"
#include "def.hpp"
#include "fmt.hpp"
#include "hashmap.hpp"
#include "string.hpp"
#include "typetraits.hpp"
#include "units.hpp"
extern "C" {
void oxTraceInitHook();
void oxTraceHook(const char *file, int line, const char *ch, const char *msg);
}
namespace ox::trace {
enum class MsgId: char {
Init = 2,
TraceEvent = 1,
Json = '{',
};
struct TraceMsgRcv {
static constexpr auto TypeName = "net.drinkingtea.ox.trace.TraceMsg";
static constexpr auto TypeVersion = 1;
BasicString<50> file{""};
int line = 0;
uint64_t time = 0;
BasicString<50> ch{""};
BasicString<100> msg;
};
template<typename T>
constexpr Error model(T *io, ox::CommonPtrWith<TraceMsgRcv> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TraceMsgRcv>());
oxReturnError(io->field("file", &obj->file));
oxReturnError(io->field("line", &obj->line));
oxReturnError(io->field("time", &obj->time));
oxReturnError(io->field("ch", &obj->ch));
oxReturnError(io->field("msg", &obj->msg));
return {};
}
struct TraceMsg {
static constexpr auto TypeName = "net.drinkingtea.ox.trace.TraceMsg";
static constexpr auto TypeVersion = 1;
const char *file = "";
int line = 0;
uint64_t time = 0;
const char *ch = "";
BasicString<100> msg;
};
template<typename T>
constexpr Error model(T *io, ox::CommonPtrWith<TraceMsg> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<TraceMsg>());
oxReturnError(io->fieldCString("file", &obj->file));
oxReturnError(io->field("line", &obj->line));
oxReturnError(io->field("time", &obj->time));
oxReturnError(io->fieldCString("ch", &obj->ch));
oxReturnError(io->field("msg", &obj->msg));
return {};
}
struct InitTraceMsgRcv {
static constexpr auto TypeName = "net.drinkingtea.ox.trace.InitTraceMsg";
static constexpr auto TypeVersion = 1;
ox::String appName;
};
template<typename T>
constexpr Error model(T *io, ox::CommonPtrWith<InitTraceMsgRcv> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<InitTraceMsgRcv>());
oxReturnError(io->field("appName", &obj->appName));
return {};
}
struct InitTraceMsg {
static constexpr auto TypeName = "net.drinkingtea.ox.trace.InitTraceMsg";
static constexpr auto TypeVersion = 2;
ox::BasicString<128> appName;
};
template<typename T>
constexpr Error model(T *io, ox::CommonPtrWith<InitTraceMsg> auto *obj) noexcept {
oxReturnError(io->template setTypeInfo<InitTraceMsg>());
oxReturnError(io->field("appName", &obj->appName));
return {};
}
class Logger {
public:
constexpr virtual ~Logger() noexcept = default;
virtual ox::Error send(const TraceMsg&) noexcept = 0;
virtual ox::Error sendInit(const InitTraceMsg&) noexcept = 0;
};
/**
* @param logger pointer to the new logger, does NOT take ownership
*/
void setLogger(Logger *logger) noexcept;
void send(const TraceMsg &msg) noexcept;
class OutStream {
protected:
const char *m_delimiter = " ";
TraceMsg m_msg;
public:
constexpr OutStream(const char *file, int line, const char *ch, const StringView &msg) noexcept {
m_msg.file = file;
m_msg.line = line;
m_msg.ch = ch;
m_msg.msg = BasicString<100>(msg);
}
constexpr OutStream(const char *file, int line, const char *ch, const char *msg = "") noexcept {
m_msg.file = file;
m_msg.line = line;
m_msg.ch = ch;
m_msg.msg = msg;
}
template<std::size_t fmtSegmentCnt, typename ...Args>
constexpr OutStream(const char *file, int line, const char *ch, detail::Fmt<fmtSegmentCnt> fmtSegments, Args const&...elements) noexcept {
static_assert(sizeof...(elements) == fmtSegmentCnt - 1, "Wrong number of trace arguments for format.");
m_msg.file = file;
m_msg.line = line;
m_msg.ch = ch;
const auto &firstSegment = fmtSegments.segments[0];
std::ignore = m_msg.msg.append(firstSegment.str, firstSegment.length);
//const detail::FmtArg elements[sizeof...(args)] = {args...};
for (size_t i = 0; auto const&e : std::initializer_list<detail::FmtArg>{elements...}) {
m_msg.msg += e.out;
const auto &s = fmtSegments.segments[i + 1];
std::ignore = m_msg.msg.append(s.str, s.length);
++i;
}
}
inline ~OutStream() noexcept {
oxTraceHook(m_msg.file, m_msg.line, m_msg.ch, m_msg.msg.c_str());
send(m_msg);
}
constexpr OutStream &operator<<(Integer_c auto v) noexcept;
constexpr OutStream &operator<<(char v) noexcept {
if (m_msg.msg.len()) {
m_msg.msg += m_delimiter;
}
m_msg.msg += v;
return *this;
}
constexpr OutStream &operator<<(const char *v) noexcept {
if (m_msg.msg.len()) {
m_msg.msg += m_delimiter;
}
m_msg.msg += v;
return *this;
}
template<std::size_t sz>
constexpr OutStream &operator<<(const BString<sz> &v) noexcept {
return operator<<(v.c_str());
}
template<std::size_t sz>
constexpr OutStream &operator<<(const BasicString<sz> &v) noexcept {
return operator<<(v.c_str());
}
constexpr OutStream &operator<<(const Error &err) noexcept {
return appendSignedInt(static_cast<int64_t>(err));
}
/**
* del sets the delimiter between log segments.
*/
constexpr OutStream &del(const char *delimiter) noexcept {
m_delimiter = delimiter;
return *this;
}
private:
constexpr OutStream &appendSignedInt(int64_t v) noexcept {
if (m_msg.msg.len()) {
m_msg.msg += m_delimiter;
}
m_msg.msg += static_cast<int64_t>(v);
return *this;
}
constexpr OutStream &appendUnsignedInt(uint64_t v) noexcept {
if (m_msg.msg.len()) {
m_msg.msg += m_delimiter;
}
m_msg.msg += static_cast<int64_t>(v);
return *this;
}
};
constexpr OutStream &OutStream::operator<<(Integer_c auto v) noexcept {
if (m_msg.msg.len()) {
m_msg.msg += m_delimiter;
}
m_msg.msg += v;
return *this;
}
class NullStream {
public:
constexpr NullStream(const char*, int, const char*, const StringView&) noexcept {
}
constexpr NullStream(const char*, int, const char*, const char* = "") noexcept {
}
template<std::size_t fmtSegmentCnt, typename ...Args>
constexpr NullStream(const char*, int, const char*, detail::Fmt<fmtSegmentCnt>, Args const&...elements) noexcept {
static_assert(sizeof...(elements) == fmtSegmentCnt - 1, "Wrong number of trace arguments for format.");
}
template<typename T>
constexpr const NullStream &operator<<(const T&) const noexcept {
return *this;
}
constexpr const NullStream &del(const char*) const noexcept {
return *this;
}
};
#if defined(DEBUG)
using TraceStream = OutStream;
#else
using TraceStream = NullStream;
#endif
inline void logError(const char *file, int line, const char *fmt, const Error &err) noexcept {
if (err) {
TraceStream trc(file, line, "ox::error");
if (err.file != nullptr) {
trc << "Error: (" << err.file << ":" << err.line << "):";
} else {
trc << "Error:";
}
trc << ox::sfmt<BasicString<100>>(fmt, static_cast<uint64_t>(err));
}
}
inline void logError(const char *file, int line, const Error &err) noexcept {
if (err) {
TraceStream trc(file, line, "ox::error");
trc << "Error:" << err;
if (err.file != nullptr) {
trc << "(" << err.file << ":" << err.line << ")";
}
}
}
void init();
void init(Logger *logger);
}