Moved to boost logging

This commit is contained in:
Blue 2025-05-04 20:07:11 +03:00
parent a1ec9f731d
commit 58020fb3ba
Signed by: blue
GPG key ID: 9B203B252A63EE38
27 changed files with 531 additions and 294 deletions

View file

@ -4,14 +4,14 @@ project(jay
LANGUAGES CXX
)
# Build type options
option(JAY_ENABLE_ASAN "Enable Address Sanitizer" OFF)
option(JAY_ENABLE_UBSAN "Enable Undefined Behavior Sanitizer" OFF)
option(JAY_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
option(JAY_ENABLE_STRICT "Enable strict compiler warnings" OFF)
option(JAY_ENABLE_LTO "Enable Link Time Optimization" OFF)
# Set default build type if not specified
option(JAY_ENABLE_STATIC_ANALYSIS "Enable static analysis during build" OFF)
option(JAY_ENABLE_COVERAGE "Enable coverage reporting" OFF)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build (Debug/Release)" FORCE)
endif()
@ -21,6 +21,7 @@ message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID}")
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(
@ -29,7 +30,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
-Wpedantic
)
if(JAY_ENABLE_STRICT)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(
-Werror
-Wconversion
@ -40,10 +41,9 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
-Woverloaded-virtual
-Wsign-conversion
-Wnull-dereference
-fno-omit-frame-pointer
)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-O0 -g3 -ggdb)
endif()
@ -52,7 +52,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
endif()
if(JAY_ENABLE_ASAN)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()
@ -67,7 +67,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
endif()
endif()
# Link Time Optimization
if(JAY_ENABLE_LTO)
include(CheckIPOSupported)
check_ipo_supported(RESULT supported OUTPUT error)
@ -79,12 +78,29 @@ if(JAY_ENABLE_LTO)
endif()
endif()
if(JAY_ENABLE_STATIC_ANALYSIS)
find_program(CLANG_TIDY NAMES clang-tidy)
if(CLANG_TIDY)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY})
endif()
endif()
if(JAY_ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(--coverage)
add_link_options(--coverage)
endif()
endif()
cmake_policy(SET CMP0076 NEW)
cmake_policy(SET CMP0167 NEW) #FindBoost
find_package(PkgConfig REQUIRED)
pkg_search_module(GLOOX REQUIRED gloox)
find_package(Threads REQUIRED)
find_package(yaml-cpp REQUIRED)
find_package(Boost REQUIRED COMPONENTS log log_setup thread system)
pkg_search_module(GLOOX REQUIRED gloox)
pkg_check_modules(UUID REQUIRED uuid)

View file

@ -7,12 +7,14 @@ set(HEADERS
jay.h
)
add_executable(${EXEC_NAME} main.cpp jay.cpp)
add_executable(${EXEC_NAME} main.cpp jay.cpp
log/log.cpp)
add_subdirectory(component)
add_subdirectory(connection)
add_subdirectory(module)
add_subdirectory(shared)
add_subdirectory(log)
target_sources(${EXEC_NAME} PRIVATE ${SOURCES})
@ -23,6 +25,11 @@ target_link_libraries(${EXEC_NAME} PRIVATE
${GLOOX_LIBRARIES}
yaml-cpp
${UUID_LIBRARIES}
Boost::log
Boost::log_setup
Boost::thread
Boost::system
Threads::Threads
)
install(TARGETS ${EXEC_NAME} RUNTIME DESTINATION bin)

View file

@ -24,21 +24,21 @@ std::string Config::getFullJID() const {
return getBareJID() + "/" + getResource();
}
Shared::Logger::Level Config::getLogLevel() const {
std::string level = root["logLevel"].as<std::string>("info");
Log::Level Config::getLogLevel() const {
auto level = root["logLevel"].as<std::string>("info");
if (level == "trace")
return Shared::Logger::trace;
return Log::Level::trace;
else if (level == "debug")
return Shared::Logger::debug;
return Log::Level::debug;
else if (level == "info")
return Shared::Logger::info;
return Log::Level::info;
else if (level == "warn" || level == "warning")
return Shared::Logger::warning;
return Log::Level::warning;
else if (level == "error")
return Shared::Logger::error;
return Log::Level::error;
else
return Shared::Logger::info;
return Log::Level::info;
}
gloox::TLSPolicy Config::getTLSPolicy() const {

View file

@ -15,7 +15,7 @@
#include "shared/definitions.h"
#include "shared/result.h"
#include "shared/logger.h"
#include "log/logger.h"
class Config {
public:
@ -36,7 +36,7 @@ public:
std::string getPassword() const;
std::string getResource() const;
std::map<std::string, std::string> getActors() const;
Shared::Logger::Level getLogLevel() const;
Log::Level getLogLevel() const;
gloox::TLSPolicy getTLSPolicy() const;
std::vector<Module> getModules() const;
Shared::Responses getResponses() const;

View file

@ -16,7 +16,7 @@ Core::Core(const std::string& configPath):
void Core::send(const std::string& jid, const std::string& body) {
std::shared_ptr<Connection> cn = connection.lock();
if (!cn) {
logger.log(Shared::Logger::warning, "Couldn't send a message to " + jid + ", connection is not available", {"Core"});
logger.log(Log::Level::warning, "Couldn't send a message to " + jid + ", connection is not available", {"Core"});
return;
}
@ -55,7 +55,7 @@ void Core::setGroup(const std::string& jid, const std::string& group) {
void Core::publish(const std::string& service, const std::string& node, const std::string& title, const std::string& body) {
std::shared_ptr<Connection> cn = connection.lock();
if (!cn) {
logger.log(Shared::Logger::warning, "Couldn't publish to " + node + "@" + service + ", connection is not available", {"Core"});
logger.log(Log::Level::warning, "Couldn't publish to " + node + "@" + service + ", connection is not available", {"Core"});
return;
}
@ -64,7 +64,7 @@ void Core::publish(const std::string& service, const std::string& node, const st
void Core::initializeActors() {
for (const std::pair<const std::string, std::string>& pair : config.getActors()) {
logger.log(Shared::Logger::info, "registering actor " + pair.first + " as " + pair.second, {"Core"});
logger.log(Log::Level::info, "registering actor " + pair.first + " as " + pair.second, {"Core"});
router.registerActor(pair.first, pair.second);
}
}

View file

@ -6,7 +6,7 @@
#include <string>
#include <memory>
#include "shared/logger.h"
#include "log/logger.h"
#include "config.h"
#include "router.h"
@ -23,7 +23,7 @@ public:
public:
Config config;
Shared::Logger logger;
Log::Logger logger;
Router router;
private:

View file

@ -12,8 +12,8 @@
#include "shared/definitions.h"
Loop::Loop(const Shared::Logger& logger):
Shared::Loggable(logger, {"Loop"}),
Loop::Loop(const Log::Logger& logger):
Log::Loggable(logger, {"Loop"}),
wakePipe(),
handlers(),
handlersToAdd(),
@ -80,16 +80,15 @@ void Loop::stop() {
debug("stopping the loop");
wake();
}
void Loop::addDescriptor(int descriptor, const Callback& handler) {
void Loop::addDescriptor(int descriptor, Callback&& handler) {
std::lock_guard lock(mutex);
if (handlers.count(descriptor) != 0) {
if (handlers.contains(descriptor)) {
warn("an attempt to add descriptor " + std::to_string(descriptor) + " for the second time");
return;
}
if (!handlersToAdd.emplace(descriptor, handler).second) {
if (!handlersToAdd.emplace(descriptor, std::forward<Callback>(handler)).second) {
warn("an attempt to add descriptor " + std::to_string(descriptor) + " for the second time");
return;
}
@ -116,13 +115,13 @@ void Loop::removeDescriptor(int descriptor) {
wake();
}
void Loop::wake() {
void Loop::wake() const {
char w = 'w';
if (write(wakePipe[1], &w, 1) < 0 && errno != EAGAIN)
error("failed to wake up event loop: " + std::string(strerror(errno)));
}
void Loop::drain(fd_set* readfds) {
void Loop::drain(fd_set* readfds) const {
if (!FD_ISSET(wakePipe[0], readfds))
return;
@ -145,7 +144,7 @@ void Loop::drain(fd_set* readfds) {
}
}
void Loop::runCallbacks(fd_set* readfds) {
void Loop::runCallbacks(fd_set* readfds) const {
for (const std::pair<const int, Callback>& pair : handlers) {
if (!FD_ISSET(pair.first, readfds))
continue;
@ -160,7 +159,7 @@ void Loop::runCallbacks(fd_set* readfds) {
}
}
int Loop::setFDsAndFindMax(fd_set* readfds) {
int Loop::setFDsAndFindMax(fd_set* readfds) const {
FD_ZERO(readfds);
FD_SET(wakePipe[0], readfds);
@ -188,7 +187,7 @@ void Loop::syncHandlers() {
void Loop::registerInstance(Loop* loop) {
std::lock_guard lock(instanceMutex);
if (instances.size() == 0) {
if (instances.empty()) {
std::signal(SIGINT, signalHandler);
std::signal(SIGTERM, signalHandler);
}
@ -200,7 +199,7 @@ void Loop::unregisterInstance(Loop* loop) {
std::lock_guard lock(instanceMutex);
instances.erase(loop);
if (instances.size() == 0) {
if (instances.empty()) {
std::signal(SIGINT, SIG_DFL);
std::signal(SIGTERM, SIG_DFL);
}

View file

@ -11,27 +11,32 @@
#include <sys/select.h>
#include "shared/loggable.h"
#include "shared/logger.h"
#include "log/loggable.h"
#include "log/logger.h"
class Loop : Shared::Loggable {
class Loop : Log::Loggable {
public:
typedef std::function<void()> Callback;
typedef std::map<int, Callback> Handlers;
explicit Loop(const Shared::Logger& logger);
explicit Loop(const Log::Logger& logger);
Loop(const Loop&) = delete;
Loop& operator=(const Loop&) = delete;
Loop(Loop&&) noexcept = delete;
Loop& operator=(Loop&&) noexcept = delete;
~Loop();
void run();
void stop();
void addDescriptor(int descriptor, const Callback& handler);
void addDescriptor(int descriptor, Callback&& handler);
void removeDescriptor(int descriptor);
private:
void wake();
void drain(fd_set* readfds);
void runCallbacks(fd_set* readfds);
int setFDsAndFindMax(fd_set* readfds);
void wake() const;
void drain(fd_set* readfds) const;
void runCallbacks(fd_set* readfds) const;
int setFDsAndFindMax(fd_set* readfds) const;
void syncHandlers();
private:
@ -39,7 +44,7 @@ private:
Handlers handlers;
Handlers handlersToAdd;
std::set<int> descriptorsToRemove;
std::mutex mutex;
mutable std::mutex mutex;
std::atomic<bool> running;
private:

View file

@ -6,8 +6,8 @@
#include "module/module.h"
#include "connection/connection.h"
Router::Router(const Shared::Logger& logger) :
Shared::Loggable(logger, {"Router"}),
Router::Router(const Log::Logger& logger) :
Log::Loggable(logger, {"Router"}),
modules(),
actors(),
defaultGroup("Stranger"),

View file

@ -11,8 +11,8 @@
#include <random>
#include <exception>
#include "log/loggable.h"
#include "shared/result.h"
#include "shared/loggable.h"
#include "shared/utils.h"
#include "actor.h"
@ -22,9 +22,9 @@ namespace Module {
class Connection;
class Router : private Shared::Loggable {
class Router : private Log::Loggable {
public:
Router(const Shared::Logger& logger);
Router(const Log::Logger& logger);
void registerModule(const std::shared_ptr<Module::Module>& module);
void registerActor(const std::string& key, const std::string& group);

View file

@ -6,7 +6,7 @@
#include "gloox/connectiontcpclient.h"
Connection::Connection(const std::shared_ptr<Core>& core):
Shared::Loggable(core->logger, {"Connection"}),
Log::Loggable(core->logger, {"Connection"}),
state(initial),
core(core),
gloox(),
@ -25,7 +25,7 @@ void Connection::initialize() {
gloox->registerConnectionListener(this);
gloox->registerMessageHandler(this);
::gloox::LogLevel level = Shared::Logger::convert(core->config.getLogLevel());
::gloox::LogLevel level = Log::convertGloox(core->config.getLogLevel());
gloox->logInstance().registerLogHandler(level, gloox::LogAreaAll, &core->logger);
gloox::Disco* disco = gloox->disco();

View file

@ -14,12 +14,12 @@
#include <gloox/pubsubitem.h>
#include <gloox/pubsubresulthandler.h>
#include "shared/loggable.h"
#include "log/loggable.h"
#include "shared/utils.h"
#include "component/core.h"
class Connection:
private Shared::Loggable,
private Log::Loggable,
public gloox::ConnectionListener,
public gloox::MessageHandler,
public gloox::PubSub::ResultHandler

View file

@ -45,7 +45,7 @@ void Jay::run() {
initialize();
int fd = connection->connect();
loop.addDescriptor(fd, std::bind(&Connection::processMessages, connection.get()));
loop.addDescriptor(fd, [conn = connection.get()] { conn->processMessages(); });
loop.run();

13
src/log/CMakeLists.txt Normal file
View file

@ -0,0 +1,13 @@
set(SOURCES
log.cpp
logger.cpp
loggable.cpp
)
set(HEADERS
log.h
logger.h
loggable.h
)
target_sources(${EXEC_NAME} PRIVATE ${SOURCES})

100
src/log/log.cpp Normal file
View file

@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "log.h"
static constexpr std::string_view gray = "\033[90m";
static constexpr std::string_view blue = "\033[34m";
static constexpr std::string_view green = "\033[32m";
static constexpr std::string_view yellow = "\033[33m";
static constexpr std::string_view red = "\033[31m";
static constexpr std::string_view magenta = "\033[35m";
gloox::LogLevel Log::convertGloox(const Level level) {
switch (level) {
case Level::trace:
return gloox::LogLevelDebug;
case Level::debug:
return gloox::LogLevelWarning;
case Level::info:
return gloox::LogLevelWarning;
case Level::warning:
return gloox::LogLevelWarning;
case Level::error:
return gloox::LogLevelError;
case Level::fatal:
return gloox::LogLevelError;
}
return gloox::LogLevelWarning;
}
Log::Level Log::convertGloox(const gloox::LogLevel level) {
switch (level) {
case gloox::LogLevelDebug:
return Level::trace;
case gloox::LogLevelError:
return Level::error;
case gloox::LogLevelWarning:
return Level::warning;
};
return Level::warning;
}
boost::log::trivial::severity_level Log::convertBoost(const Level level) {
switch (level) {
case Level::trace:
return boost::log::trivial::trace;
case Level::debug:
return boost::log::trivial::debug;
case Level::info:
return boost::log::trivial::info;
case Level::warning:
return boost::log::trivial::warning;
case Level::error:
return boost::log::trivial::error;
case Level::fatal:
return boost::log::trivial::fatal;
}
return boost::log::trivial::error;
}
Log::Level Log::convertBoost(const boost::log::trivial::severity_level level) {
switch (level) {
case boost::log::trivial::trace:
return Level::trace;
case boost::log::trivial::debug:
return Level::debug;
case boost::log::trivial::info:
return Level::info;
case boost::log::trivial::warning:
return Level::warning;
case boost::log::trivial::error:
return Level::error;
case boost::log::trivial::fatal:
return Level::fatal;
}
return Level::error;
}
std::string_view Log::levelColor(const Level level) {
switch (level) {
case Level::trace:
return gray;
case Level::debug:
return blue;
case Level::info:
return green;
case Level::warning:
return yellow;
case Level::error:
return red;
case Level::fatal:
return magenta;
}
return red;
}

30
src/log/log.h Normal file
View file

@ -0,0 +1,30 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <string_view>
#include <gloox/gloox.h>
#include <boost/log/trivial.hpp>
namespace Log {
enum class Level {
trace,
debug,
info,
warning,
error,
fatal
};
gloox::LogLevel convertGloox(Level level);
Level convertGloox(gloox::LogLevel level);
boost::log::trivial::severity_level convertBoost(Level level);
Level convertBoost(boost::log::trivial::severity_level level);
std::string_view levelColor(Level level);
}

33
src/log/loggable.cpp Normal file
View file

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "loggable.h"
Log::Loggable::Loggable(const Logger& _logger, const std::vector<std::string>& _domain) :
logger(_logger),
domain(_domain)
{}
void Log::Loggable::trace(const std::string& message) const {
logger.log(Level::trace, message, domain);
}
void Log::Loggable::debug(const std::string& message) const {
logger.log(Level::debug, message, domain);
}
void Log::Loggable::info(const std::string& message) const {
logger.log(Level::info, message, domain);
}
void Log::Loggable::warn(const std::string& message) const {
logger.log(Level::warning, message, domain);
}
void Log::Loggable::error(const std::string& message) const {
logger.log(Level::error, message, domain);
}
void Log::Loggable::fatal(const std::string& message) const {
logger.log(Level::fatal, message, domain);
}

View file

@ -8,7 +8,7 @@
#include "logger.h"
namespace Shared {
namespace Log {
class Loggable {
public:

191
src/log/logger.cpp Normal file
View file

@ -0,0 +1,191 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "logger.h"
#include <shared/definitions.h>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/attributes/scoped_attribute.hpp>
#include <boost/log/support/date_time.hpp>
#include <iostream>
#include <chrono>
static constexpr std::string_view clear = "\033[0m";
static constexpr std::string_view bold = "\033[1m";
struct GlooxTag {
gloox::LogArea bit;
const char* label;
const char* color;
};
static constexpr GlooxTag glooxTags[] = {
{ gloox::LogAreaClassParser, "Parser", nullptr },
{ gloox::LogAreaClassConnectionTCPBase, "Connection", nullptr },
{ gloox::LogAreaClassClient, "Client", nullptr },
{ gloox::LogAreaClassClientbase, "Client Base", nullptr },
{ gloox::LogAreaClassComponent, "Component", nullptr },
{ gloox::LogAreaClassDns, "DNS", nullptr },
{ gloox::LogAreaClassConnectionHTTPProxy,"HTTP Proxy", nullptr },
{ gloox::LogAreaClassConnectionSOCKS5Proxy, "SOCKS5 Proxy", nullptr },
{ gloox::LogAreaClassConnectionTCPClient,"TCP Client", nullptr },
{ gloox::LogAreaClassConnectionTCPServer,"TCP Server", nullptr },
{ gloox::LogAreaClassS5BManager, "SOCKS5 Bytestream Manager", nullptr },
{ gloox::LogAreaClassSOCKS5Bytestream, "SOCKS5 Bytestream", nullptr },
{ gloox::LogAreaLinkLocalManager, "Link Local Manager", nullptr },
{ gloox::LogAreaXmlIncoming, "XML IN", "\033[1;36m" },
{ gloox::LogAreaXmlOutgoing, "XML OUT", "\033[1;35m" },
{ gloox::LogAreaUser, "USER", nullptr },
};
BOOST_LOG_ATTRIBUTE_KEYWORD(channel_attr, "Channel", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp_attr, "TimeStamp", boost::posix_time::ptime)
Log::Logger::Logger(Level level):
running(true),
mutex(),
condition(),
thread(),
level(level),
sink(boost::make_shared<Sink>()),
logger(),
queue(8192)
{
auto backend = sink->locked_backend();
boost::shared_ptr<std::ostream> const stream(&std::clog, Shared::noDelete);
backend->add_stream(stream);
backend->auto_flush(true);
const boost::log::filter severityFilter = boost::log::trivial::severity >= convertBoost(level);
sink->set_filter(severityFilter);
sink->set_formatter(&Logger::formatRecord);
boost::log::core::get()->add_sink(sink);
logger.add_attribute("Timestamp", boost::log::attributes::local_clock());
thread = std::thread(&Logger::processLogQueue, this);
}
Log::Logger::~Logger() {
running.store(false, std::memory_order_release);
condition.notify_one();
if (thread.joinable())
thread.join();
sink->stop();
sink->flush();
boost::log::core::get()->remove_sink(sink);
};
void Log::Logger::handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) {
const Level lvl = convertGloox(level);
if (lvl < Logger::level)
return;
LogEntry entry;
entry.level = lvl;
entry.source = LogEntry::GlooxSource(area);
entry.message = message;
if (!queue.push(std::move(entry)))
std::cerr << "Log queue full, message dropped\n";
else
condition.notify_one();
}
std::vector<std::string> Log::Logger::convertTags(gloox::LogArea area) {
std::vector<std::string> tags;
for (const auto& tag : glooxTags) {
if (area & tag.bit)
tags.emplace_back(tag.label);
}
return tags;
}
std::string Log::Logger::formatDomain(const std::vector<std::string>& domain) {
std::ostringstream tagStream;
for (size_t i = 0; i < domain.size(); ++i) {
if (i > 0)
tagStream << ' ';
tagStream << domain[i];
}
return tagStream.str();
}
void Log::Logger::processLogQueue() {
LogEntry entry;
std::unique_lock<std::mutex> lock(mutex);
while (running.load(std::memory_order_relaxed)) {
while (queue.pop(entry)) {
lock.unlock();
logEntry(entry);
lock.lock();
}
condition.wait(lock, [this]() {
return !running.load(std::memory_order_relaxed) || !queue.empty();
});
}
while (queue.pop(entry))
logEntry(entry);
}
void Log::Logger::logEntry(const LogEntry& entry) {
std::visit(
[this, &entry]<typename S>(const S& source) {
if constexpr (std::is_same_v<std::decay_t<S>, LogEntry::GlooxSource>) {
BOOST_LOG_SCOPED_LOGGER_ATTR(logger, "Channel",
boost::log::attributes::make_constant(formatDomain(convertTags(source.area))));
BOOST_LOG_SEV(logger, convertBoost(entry.level)) << entry.message;
} else {
BOOST_LOG_SCOPED_LOGGER_ATTR(logger, "Channel",
boost::log::attributes::make_constant(formatDomain(source.domain)));
BOOST_LOG_SEV(logger, convertBoost(entry.level)) << entry.message;
}
}, entry.source);
}
void Log::Logger::log(Level lvl, const std::string& message, const std::vector<std::string>& domain) const {
if (lvl < level)
return;
LogEntry entry;
entry.level = lvl;
entry.source = LogEntry::DirectSource(domain);
entry.message = message;
if (!queue.push(std::move(entry)))
std::cerr << "Log queue full, message dropped\n";
else
condition.notify_one();
}
void Log::Logger::formatRecord(boost::log::record_view const& rec, boost::log::formatting_ostream& out) {
auto ts = rec[timestamp_attr];
auto lvl = rec[boost::log::trivial::severity];
auto msg = rec[boost::log::expressions::smessage];
if (ts)
out << boost::posix_time::to_simple_string(ts.get()) << " ";
out << bold << levelColor(convertBoost(lvl.get())) << "[" << lvl.get() << "]" << clear;
auto ch = rec[channel_attr].get_ptr();
if (ch && !ch->empty())
out << " [" << *ch << "]";
out << "\t" << msg.get() << clear;
}

70
src/log/logger.h Normal file
View file

@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <vector>
#include <string>
#include <atomic>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <variant>
#include <gloox/loghandler.h>
#include <gloox/logsink.h>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/lockfree/spsc_queue.hpp>
#include "log.h"
namespace Log {
class Logger: public gloox::LogHandler {
public:
explicit Logger(Level level = Level::info);
~Logger() override;
void handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) override;
void log(Level level, const std::string& message, const std::vector<std::string>& domain = {}) const;
private:
typedef boost::log::sinks::asynchronous_sink<boost::log::sinks::text_ostream_backend> Sink;
typedef boost::log::sources::severity_logger<boost::log::trivial::severity_level> BoostLogger;
struct LogEntry {
struct GlooxSource {
gloox::LogArea area;
};
struct DirectSource {
std::vector<std::string> domain;
};
Level level;
std::string message;
std::chrono::system_clock::time_point timestamp;
std::variant<GlooxSource, DirectSource> source;
};
std::atomic<bool> running;
std::mutex mutex;
mutable std::condition_variable condition;
std::thread thread;
Level level;
boost::shared_ptr<Sink> sink;
mutable BoostLogger logger;
mutable boost::lockfree::spsc_queue<LogEntry> queue;
private:
static void formatRecord(boost::log::record_view const& rec, boost::log::formatting_ostream& out);
static std::vector<std::string> convertTags(gloox::LogArea area);
static std::string formatDomain(const std::vector<std::string>& domain);
void processLogQueue();
void logEntry(const LogEntry& entry);
};
}

View file

@ -8,7 +8,7 @@
#include "gloox/message.h"
Module::Module::Module(const std::shared_ptr<Core>& core, const Config::Module& conf, const std::string& name):
Shared::Loggable(core->logger, {"Module", name, conf.alias}),
Log::Loggable(core->logger, {"Module", name, conf.alias}),
name(name),
alias(conf.alias),
core(core),

View file

@ -12,14 +12,14 @@
#include "shared/definitions.h"
#include "shared/result.h"
#include "shared/loggable.h"
#include "log/loggable.h"
#include "component/core.h"
#include "component/actor.h"
#include "component/config.h"
namespace Module {
class Module : protected Shared::Loggable {
class Module : protected Log::Loggable {
protected:
Module(const std::shared_ptr<Core>& core, const Config::Module& conf, const std::string& name);

View file

@ -1,12 +1,8 @@
set(SOURCES
logger.cpp
loggable.cpp
utils.cpp
)
set(HEADERS
logger.h
loggable.h
definitions.h
result.h
utils.h

View file

@ -10,6 +10,11 @@
#define UNUSED(x) (void)(x)
namespace Shared {
struct NullDeleter {
void operator()(const void*) const noexcept {}
};
void inline noDelete(const void*) noexcept {};
typedef std::vector<std::string> Strings;
typedef std::map<std::string, Strings> Permissions;
typedef std::map<std::string, std::string> VC;

View file

@ -1,33 +0,0 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "loggable.h"
Shared::Loggable::Loggable(const Logger& _logger, const std::vector<std::string>& _domain) :
logger(_logger),
domain(_domain)
{}
void Shared::Loggable::trace(const std::string& message) const {
logger.log(Logger::trace, message, domain);
}
void Shared::Loggable::debug(const std::string& message) const {
logger.log(Logger::debug, message, domain);
}
void Shared::Loggable::info(const std::string& message) const {
logger.log(Logger::info, message, domain);
}
void Shared::Loggable::warn(const std::string& message) const {
logger.log(Logger::warning, message, domain);
}
void Shared::Loggable::error(const std::string& message) const {
logger.log(Logger::error, message, domain);
}
void Shared::Loggable::fatal(const std::string& message) const {
logger.log(Logger::fatal, message, domain);
}

View file

@ -1,156 +0,0 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "logger.h"
#include <iostream>
#include <chrono>
static constexpr std::string_view logLevelMap[] = {
"TRACE",
"DEBUG",
"INFO",
"WARNING",
"ERROR",
"FATAL",
};
static constexpr std::string_view colorMap[] = {
"\033[90m", // TRACE (Gray)
"\033[34m", // DEBUG (Blue)
"\033[32m", // INFO (Green)
"\033[33m", // WARNING (Yellow)
"\033[31m", // ERROR (Red)
"\033[35m", // FATAL (Magenta)
};
static constexpr std::string_view clear = "\033[0m";
static constexpr std::string_view bold = "\033[1m";
void writeTimestamp() {
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
std::time_t time_now = std::chrono::system_clock::to_time_t(now);
std::tm local_time = *std::localtime(&time_now);
std::chrono::milliseconds millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
std::cout << "\033[90m" << std::put_time(&local_time, "%Y-%m-%d %H:%M:%S");
std::cout << "." << std::setfill('0') << std::setw(3) << millis.count();
std::cout << clear << ' ';
}
constexpr std::string_view getLogLevel(gloox::LogLevel level) {
return (level >= 0 && level < 3) ? logLevelMap[Shared::Logger::convert(level)] : "UNKNOWN";
}
constexpr std::string_view getColor(gloox::LogLevel level) {
return (level >= 0 && level < 3) ? colorMap[Shared::Logger::convert(level)] : "";
}
void writeTags(gloox::LogArea area) {
if (area & gloox::LogAreaClassParser)
std::cout << " [Parser]";
if (area & gloox::LogAreaClassConnectionTCPBase)
std::cout << " [Connection]";
if (area & gloox::LogAreaClassClient)
std::cout << " [Client]";
if (area & gloox::LogAreaClassClientbase)
std::cout << " [Client Base]";
if (area & gloox::LogAreaClassComponent)
std::cout << " [Component]";
if (area & gloox::LogAreaClassDns)
std::cout << " [DNS]";
if (area & gloox::LogAreaClassConnectionHTTPProxy)
std::cout << " [HTTP Proxy]";
if (area & gloox::LogAreaClassConnectionSOCKS5Proxy)
std::cout << " [SOCKS5 Proxy]";
if (area & gloox::LogAreaClassConnectionTCPClient)
std::cout << " [TCP Client]";
if (area & gloox::LogAreaClassConnectionTCPServer)
std::cout << " [TCP Server]";
if (area & gloox::LogAreaClassS5BManager)
std::cout << " [SOCKS5 Bytestream Manager]";
if (area & gloox::LogAreaClassSOCKS5Bytestream)
std::cout << " [SOCKS5 Bytestream]";
if (area & gloox::LogAreaLinkLocalManager)
std::cout << " [Link Local Manager]";
if (area & gloox::LogAreaXmlIncoming)
std::cout << " \033[1;36m[XML IN]";
if (area & gloox::LogAreaXmlOutgoing)
std::cout << " \033[1;35m[XML OUT]";
if (area & gloox::LogAreaUser)
std::cout << " [USER]";
if (area == gloox::LogAreaClassDns)
std::cout << '\t';
}
Shared::Logger::Level Shared::Logger::convert(gloox::LogLevel level) {
switch (level) {
case gloox::LogLevelDebug:
return trace;
case gloox::LogLevelError:
return error;
case gloox::LogLevelWarning:
return warning;
};
return warning;
}
gloox::LogLevel Shared::Logger::convert(Level level) {
switch (level) {
case trace:
return gloox::LogLevelDebug;
case debug:
return gloox::LogLevelWarning;
case info:
return gloox::LogLevelWarning;
case warning:
return gloox::LogLevelWarning;
case error:
return gloox::LogLevelError;
case fatal:
return gloox::LogLevelError;
}
return gloox::LogLevelWarning;
}
Shared::Logger::Logger(Level level):
level(level)
{}
Shared::Logger::~Logger() = default;
void Shared::Logger::handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) {
writeTimestamp();
std::cout << getColor(level) << bold << '[' << getLogLevel(level) << ']' << clear << bold;
writeTags(area);
std::cout << clear << '\t' << message << clear << std::endl;
}
void Shared::Logger::log(Level lvl, const std::string& message, const std::vector<std::string>& domain) const {
if (lvl < level)
return;
writeTimestamp();
std::cout << colorMap[lvl] << bold << '[' << logLevelMap[lvl] << ']' << clear;
if (!domain.empty()) {
std::cout << ' ' << bold << '[';
bool first = true;
for (const std::string& tag : domain) {
if (first)
first = false;
else
std::cout << ' ';
std::cout << tag;
}
std::cout << ']' << clear;
}
std::cout << '\t' << message << clear << std::endl;
}

View file

@ -1,39 +0,0 @@
// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <vector>
#include <gloox/loghandler.h>
#include <gloox/logsink.h>
namespace Shared {
class Logger: public gloox::LogHandler {
public:
enum Level {
trace,
debug,
info,
warning,
error,
fatal
};
public:
explicit Logger(Level level = info);
~Logger() override;
void handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) override;
void log(Level level, const std::string& message, const std::vector<std::string>& domain = {}) const;
static gloox::LogLevel convert(Level level);
static Level convert(gloox::LogLevel level);
private:
Level level;
};
}