291 lines
7.3 KiB
C++
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);
|
|
|
|
}
|