Toolbox snapshot
The Reactive C++ Toolbox
Loading...
Searching...
No Matches
Logger.cpp
Go to the documentation of this file.
1// The Reactive C++ Toolbox.
2// Copyright (C) 2013-2019 Swirly Cloud Limited
3// Copyright (C) 2022 Reactive Markets Limited
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17#include "Logger.hpp"
18
19#include <atomic>
20#include <mutex>
21
22#include <syslog.h>
23
24#include <sys/uio.h> // writev()
25
26#if defined(__linux__)
27#include <sys/syscall.h>
28#endif
29
30namespace toolbox {
31inline namespace sys {
32using namespace std;
33namespace {
34
35const char* Labels[] = {"NONE", "CRIT", "ERROR", "WARN", "METRIC", "NOTICE", "INFO", "DEBUG"};
36
37// The gettid() function is a Linux-specific function call.
38#if defined(__linux__)
39inline pid_t gettid()
40{
41 return syscall(SYS_gettid);
42}
43#else
44inline pid_t gettid()
45{
46 return getpid();
47}
48#endif
49
50class NullLogger final : public Logger {
51 void do_write_log(WallTime /*ts*/, LogLevel /*level*/, int /*tid*/, LogMsgPtr&& /*msg*/,
52 size_t /*size*/) noexcept override
53 {
54 }
56
57class StdLogger final : public Logger {
58 void do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg,
59 size_t size) noexcept override
60 {
61 const auto t{WallClock::to_time_t(ts)};
62 tm tm;
63 localtime_r(&t, &tm);
64
65 // The following format has an upper-bound of 49 characters:
66 // "%Y/%m/%d %H:%M:%S.%06d %-6s [%d]: "
67 //
68 // Example:
69 // 2022/03/14 00:00:00.000000 NOTICE [0123456789]: msg...
70 // <---------------------------------------------->
71 char head[48 + 1];
72 size_t hlen{strftime(head, sizeof(head), "%Y/%m/%d %H:%M:%S", &tm)};
73 const auto us{static_cast<int>(us_since_epoch(ts) % 1000000)};
74 hlen += sprintf(head + hlen, ".%06d %-6s [%d]: ", us, log_label(level), tid);
75 char tail{'\n'};
76 iovec iov[] = {
77 {head, hlen}, //
78 {msg.get(), size}, //
79 {&tail, 1} //
80 };
81
83 // The following lock was required to avoid interleaving.
84 lock_guard<mutex> lock{mutex_};
85 // Best effort given that this is the logger.
86 ignore = writev(fd, iov, sizeof(iov) / sizeof(iov[0]));
87 }
88 mutex mutex_;
90
91class SysLogger final : public Logger {
92 void do_write_log(WallTime /*ts*/, LogLevel level, int /*tid*/, LogMsgPtr&& msg,
93 size_t size) noexcept override
94 {
95 int prio;
96 switch (level) {
97 case LogLevel::None:
98 return;
99 case LogLevel::Crit:
100 prio = LOG_CRIT;
101 break;
102 case LogLevel::Error:
103 prio = LOG_ERR;
104 break;
105 case LogLevel::Warn:
107 break;
108 case LogLevel::Metric:
110 break;
111 case LogLevel::Notice:
113 break;
114 case LogLevel::Info:
115 prio = LOG_INFO;
116 break;
117 default:
118 prio = LOG_DEBUG;
119 }
120 syslog(prio, "%.*s", static_cast<int>(size), static_cast<const char*>(msg.get()));
121 }
123
124// Global log level and logger function.
127
129{
130 return level_.load(memory_order_acquire);
131}
132
133inline Logger& acquire_logger() noexcept
134{
135 return *logger_.load(memory_order_acquire);
136}
137
138} // namespace
139
141{
142 return null_logger_;
143}
144
146{
147 return std_logger_;
148}
149
151{
152 return sys_logger_;
153}
154
155const char* log_label(LogLevel level) noexcept
156{
157 return Labels[static_cast<int>(min(max(level, LogLevel::None), LogLevel::Debug))];
158}
159
164
166{
167 return level_.exchange(max(level, LogLevel{}), memory_order_acq_rel);
168}
169
171{
172 return acquire_logger();
173}
174
176{
177 return *logger_.exchange(&logger, memory_order_acq_rel);
178}
179
180void write_log(WallTime ts, LogLevel level, LogMsgPtr&& msg, std::size_t size) noexcept
181{
182 acquire_logger().write_log(ts, level, static_cast<int>(gettid()), std::move(msg), size);
183}
184
185Logger::~Logger() = default;
186
191
193{
194 write_all_messages();
195}
196
197void AsyncLogger::write_all_messages()
198{
199 Task t;
200 while (tq_.pop(t)) {
201 logger_.write_log(t.ts, t.level, t.tid, LogMsgPtr{t.msg}, t.size);
202 }
203}
204
206{
207 write_all_messages();
208 std::this_thread::sleep_for(50ms);
209
210 return (!tq_.empty() || !stop_);
211}
212
214{
215 stop_ = true;
216}
217
218void AsyncLogger::do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg,
219 size_t size) noexcept
220{
221 // if the queue is full, skip the message
222 if (void* msg_ptr = msg.release();
223 !tq_.push(Task{.ts = ts, .level = level, .tid = tid, .msg = msg_ptr, .size = size})) {
225 }
226}
227
228} // namespace sys
229} // namespace toolbox
AsyncLogger(Logger &logger)
Definition Logger.cpp:187
void stop()
Interrupt and exit any inprogress call to run().
Definition Logger.cpp:213
void write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr &&msg, std::size_t size) noexcept
Definition Logger.hpp:109
STL namespace.
int64_t min(const Histogram &h) noexcept
Definition Utility.cpp:41
int64_t max(const Histogram &h) noexcept
Definition Utility.cpp:46
const char * log_label(LogLevel level) noexcept
Return log label for given log level.
Definition Logger.cpp:155
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:183
Logger & set_logger(Logger &logger) noexcept
Set logger globally for all threads.
Definition Logger.cpp:175
Logger & get_logger() noexcept
Return current logger.
Definition Logger.cpp:170
void write_log(WallTime ts, LogLevel level, LogMsgPtr &&msg, std::size_t size) noexcept
Definition Logger.cpp:180
Logger & null_logger() noexcept
Null logger. This logger does nothing and is effectively /dev/null.
Definition Logger.cpp:140
WallClock::time_point WallTime
Definition Time.hpp:111
StoragePtr< MaxLogLine > LogMsgPtr
Definition Logger.hpp:51
LogLevel get_log_level() noexcept
Return current log level.
Definition Logger.cpp:160
Logger & std_logger() noexcept
Definition Logger.cpp:145
LogLevel set_log_level(LogLevel level) noexcept
Set log level globally for all threads.
Definition Logger.cpp:165
Logger & sys_logger() noexcept
System logger. This logger calls syslog().
Definition Logger.cpp:150
constexpr std::size_t size(const detail::Struct< detail::Member< TagsT, ValuesT >... > &s)
Definition Struct.hpp:98
constexpr auto bind() noexcept
Definition Slot.hpp:92
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:96