Skip to content

Commit

Permalink
logger: Write logs in separate thread
Browse files Browse the repository at this point in the history
Conifgurable with logger.thread: yes
  • Loading branch information
shramov committed Dec 21, 2024
1 parent db846f6 commit 89418a0
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 32 deletions.
42 changes: 42 additions & 0 deletions src/logger/common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef _LOGGER_COMMON_H
#define _LOGGER_COMMON_H

#include "tll/logger.h"
#include "tll/logger/impl.h"
#include "tll/util/refptr.h"
#include "tll/util/time.h"

#include <mutex>

namespace tll::logger {

struct tll_logger_obj_t : tll::util::refbase_t<tll_logger_obj_t, 0>
{
const char * name = "";
void * obj = nullptr;
tll_logger_impl_t * impl = nullptr;

~tll_logger_obj_t()
{
if (obj && impl && impl->log_free)
impl->log_free(impl, name, obj);
}

auto log(tll::time_point ts, tll_logger_level_t level, std::string_view body)
{
return (*impl->log)(ts.time_since_epoch().count(), name, level, body.data(), body.size(), obj);
}
};

struct Logger : public tll_logger_t, public tll::util::refbase_t<Logger>
{
void destroy();

std::mutex lock;
std::string name;
tll::util::refptr_t<tll_logger_obj_t> impl;
};

} // namespace tll::logger

#endif//_LOGGER_COMMON_H
54 changes: 29 additions & 25 deletions src/logger/logger.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
#include "tll/logger.h"
#include "tll/logger/impl.h"
#include "tll/util/refptr.h"
#include "tll/util/size.h"
#include "tll/util/string.h"
#include "tll/util/time.h"

#include <atomic>
#include <map>
#include <mutex>
#include <set>
Expand All @@ -22,8 +22,9 @@
#include <stdio.h>
#include <stdarg.h>

#include "logger/common.h"
#include "logger/config.h"

#include "logger/thread.h"
#include "logger/util.h"

#ifdef WITH_SPDLOG
Expand All @@ -32,34 +33,14 @@

namespace tll::logger {

struct tll_logger_obj_t : tll::util::refbase_t<tll_logger_obj_t, 0>
{
const char * name = "";
void * obj = nullptr;
tll_logger_impl_t * impl = nullptr;

~tll_logger_obj_t()
{
if (obj && impl && impl->log_free)
impl->log_free(impl, name, obj);
}
};

struct Logger : public tll_logger_t, public tll::util::refbase_t<Logger>
{
void destroy();

std::mutex lock;
std::string name;
tll::util::refptr_t<tll_logger_obj_t> impl;
};

struct logger_context_t
{
std::shared_mutex lock;
typedef std::unique_lock<std::shared_mutex> wlock_t;
typedef std::shared_lock<std::shared_mutex> rlock_t;

std::unique_ptr<Thread> _thread;

std::map<std::string_view, Logger *, std::less<>> _loggers;
std::map<std::string, tll_logger_level_t, std::less<>> _levels_prefix;
std::map<std::string, tll_logger_level_t, std::less<>> _levels;
Expand All @@ -68,6 +49,12 @@ struct logger_context_t
static tll_logger_impl_t stdio;
tll_logger_impl_t * impl = &stdio;

~logger_context_t()
{
// Wait for thread to finish if it was spawned
_thread.reset();
}

Logger * init(std::string_view name)
{
{
Expand Down Expand Up @@ -213,6 +200,20 @@ struct logger_context_t

int configure(const tll::ConstConfig &cfg)
{
auto thread = cfg.getT<bool>("thread");
if (!thread) { // Missing or invalid value, do nothing
} else if (*thread) {
auto size = cfg.getT<tll::util::Size>("ring-size").value_or(128 * 1024);
std::unique_ptr<Thread> tmp { new Thread() };
if (tmp->init(size))
return 0;

std::swap(_thread, tmp);
if (tmp)
tmp->stop();
} else if (!*thread)
_thread.reset();

if (auto levels = cfg.sub("levels"); levels) {
std::set<std::string> skip;
for (auto & [k, v] : levels->browse("**", true)) {
Expand Down Expand Up @@ -320,7 +321,10 @@ int tll_logger_log(tll_logger_t * l, tll_logger_level_t level, const char * buf,

auto ts = tll::time::now();

return (*impl->impl->log)(ts.time_since_epoch().count(), impl->name, level, buf, len, impl->obj);
if (tll::logger::context._thread) {
return tll::logger::context._thread->push(log, ts, level, {buf, len});
} else
return log->impl->log(ts, level, {buf, len});
}

tll_logger_buf_t * tll_logger_tls_buf()
Expand Down
128 changes: 128 additions & 0 deletions src/logger/thread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#ifndef _LOGGER_THREAD_H
#define _LOGGER_THREAD_H

#include "tll/cppring.h"
#include "tll/logger.h"
#include "tll/util/time.h"

#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>

#include <cstdlib>
#include <mutex>
#include <thread>

#include "logger/common.h"

namespace tll::logger {

struct Header
{
uint16_t level = TLL_LOGGER_DEBUG;
tll::logger::Logger * logger = nullptr;
tll::time_point timestamp;
};

struct Thread
{
std::mutex _lock;
std::unique_ptr<tll::Ring> _ring;
int _fd = -1;
bool _stop = false;
std::thread _thread;

tll::Logger _log { "tll.logger.thread" };

~Thread()
{
stop();

if (_thread.joinable())
_thread.join();
_thread = {};

if (_fd != -1)
close(_fd);
_fd = -1;
}

int init(size_t size)
{
_fd = eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK | EFD_CLOEXEC);
if (_fd == -1)
return EINVAL;
_ring = tll::Ring::allocate(size);
if (!_ring)
return EINVAL;

_thread = std::thread(&Thread::run, this);
return 0;
}

void stop()
{
_stop = true;
eventfd_write(_fd, 1);
}

void run()
{
pollfd pfd = { .fd = _fd, .events = POLLIN };
_log.debug("Logger thread started");
while (!_stop || !_ring->empty()) {
if (_ring->empty())
poll(&pfd, 1, 1000); // Return code is not important here, used as wakeable sleep
step();
}
_log.debug("Logger thread finished");
}

void step()
{
const void * data;
size_t size;
if (_ring->read(&data, &size))
return;

eventfd_t v;
if (eventfd_read(_fd, &v) != 0 && errno == EAGAIN)
return; // Try again, so ring and efd count are in sync

if (size < sizeof(Header))
return _log.error("Invalid data header, too small: {} < minimal {}", size, sizeof(Header));

auto header = (const Header *) data;

std::string_view body((const char *) (header + 1), size - sizeof(Header));

{
std::unique_lock<std::mutex> lck(header->logger->lock);
header->logger->impl->log(header->timestamp, (tll_logger_level_t) header->level, body);
}
header->logger->unref();
_ring->shift();
}

int push(Logger * log, tll::time_point ts, tll_logger_level_t level, std::string_view body)
{
std::unique_lock<std::mutex> lock(_lock);
void * data;
if (_ring->write_begin(&data, sizeof(Header) + body.size())) {
_lock.unlock();
return log->impl->log(ts, level, body);
}
auto header = (Header *) data;
header->logger = log->ref();
header->timestamp = ts;
header->level = level;
memcpy(header + 1, body.data(), body.size());
_ring->write_end(data, sizeof(Header) + body.size());
eventfd_write(_fd, 1);
return 0;
}
};

} // namespace tll::logger

#endif//_LOGGER_THREAD_H
2 changes: 1 addition & 1 deletion src/logger/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#define _LOGGER_UTIL_H

#include "tll/logger.h"
#include "tll/util/conv.h"
#include "tll/conv/base.h"

constexpr bool icmp(char a, char b)
{
Expand Down
Loading

0 comments on commit 89418a0

Please sign in to comment.