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 struct S {
42 pid_t tid = {};
43 bool init_done = false;
44 };
45
46 thread_local S s{};
47
48 // 'tid' cannot be set during initialisation of struct S -- because
49 // the C++ standard doesn't guarantee which thread initialises it.
50 if (!s.init_done) [[unlikely]] {
51 s.tid = syscall(SYS_gettid);
52 s.init_done = true;
53 }
54
55 return s.tid;
56}
57#else
58inline pid_t gettid()
59{
60 return getpid();
61}
62#endif
63
64class NullLogger final : public Logger {
65 void do_write_log(WallTime /*ts*/, LogLevel /*level*/, int /*tid*/, LogMsgPtr&& /*msg*/,
66 size_t /*size*/) noexcept override
67 {
68 }
70
71class StdLogger final : public Logger {
72 void do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg,
73 size_t size) noexcept override
74 {
75 const auto t{WallClock::to_time_t(ts)};
76 tm tm;
77 localtime_r(&t, &tm);
78
79 // The following format has an upper-bound of 49 characters:
80 // "%Y/%m/%d %H:%M:%S.%06d %-6s [%d]: "
81 //
82 // Example:
83 // 2022/03/14 00:00:00.000000 NOTICE [0123456789]: msg...
84 // <---------------------------------------------->
85 char head[48 + 1];
86 size_t hlen{strftime(head, sizeof(head), "%Y/%m/%d %H:%M:%S", &tm)};
87 const auto us{static_cast<int>(us_since_epoch(ts) % 1000000)};
88 hlen += sprintf(head + hlen, ".%06d %-6s [%d]: ", us, log_label(level), tid);
89 char tail{'\n'};
90 iovec iov[] = {
91 {head, hlen}, //
92 {msg.get(), size}, //
93 {&tail, 1} //
94 };
95
97 // The following lock was required to avoid interleaving.
98 lock_guard<mutex> lock{mutex_};
99 // Best effort given that this is the logger.
100 ignore = writev(fd, iov, sizeof(iov) / sizeof(iov[0]));
101 }
102 mutex mutex_;
104
105class SysLogger final : public Logger {
106 void do_write_log(WallTime /*ts*/, LogLevel level, int /*tid*/, LogMsgPtr&& msg,
107 size_t size) noexcept override
108 {
109 int prio;
110 switch (level) {
111 case LogLevel::None:
112 return;
113 case LogLevel::Crit:
114 prio = LOG_CRIT;
115 break;
116 case LogLevel::Error:
117 prio = LOG_ERR;
118 break;
119 case LogLevel::Warn:
121 break;
122 case LogLevel::Metric:
124 break;
125 case LogLevel::Notice:
127 break;
128 case LogLevel::Info:
129 prio = LOG_INFO;
130 break;
131 default:
132 prio = LOG_DEBUG;
133 }
134 syslog(prio, "%.*s", static_cast<int>(size), static_cast<const char*>(msg.get()));
135 }
137
138// Global log level and logger function.
141
143{
144 return level_.load(memory_order_acquire);
145}
146
147inline Logger& acquire_logger() noexcept
148{
149 return *logger_.load(memory_order_acquire);
150}
151
152} // namespace
153
155{
156 return null_logger_;
157}
158
160{
161 return std_logger_;
162}
163
165{
166 return sys_logger_;
167}
168
169const char* log_label(LogLevel level) noexcept
170{
171 return Labels[static_cast<int>(min(max(level, LogLevel::None), LogLevel::Debug))];
172}
173
178
180{
181 return level_.exchange(max(level, LogLevel{}), memory_order_acq_rel);
182}
183
185{
186 return acquire_logger();
187}
188
190{
191 return *logger_.exchange(&logger, memory_order_acq_rel);
192}
193
194void write_log(WallTime ts, LogLevel level, LogMsgPtr&& msg, std::size_t size) noexcept
195{
196 acquire_logger().write_log(ts, level, static_cast<int>(gettid()), std::move(msg), size);
197}
198
199Logger::~Logger() = default;
200
205
207{
208 write_all_messages();
209}
210
211void AsyncLogger::write_all_messages()
212{
213 Task t;
214 while (tq_.pop(t)) {
215 logger_.write_log(t.ts, t.level, t.tid, LogMsgPtr{t.msg}, t.size);
216 }
217}
218
220{
221 write_all_messages();
222 std::this_thread::sleep_for(50ms);
223
224 return (!tq_.empty() || !stop_);
225}
226
228{
229 stop_ = true;
230}
231
232void AsyncLogger::do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg,
233 size_t size) noexcept
234{
235 // if the queue is full, skip the message
236 if (void* msg_ptr = msg.release();
237 !tq_.push(Task{.ts = ts, .level = level, .tid = tid, .msg = msg_ptr, .size = size})) {
239 }
240}
241
242} // namespace sys
243} // namespace toolbox
AsyncLogger(Logger &logger)
Definition Logger.cpp:201
void stop()
Interrupt and exit any inprogress call to run().
Definition Logger.cpp:227
void write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr &&msg, std::size_t size) noexcept
Definition Logger.hpp:110
STL namespace.
int64_t min(const Histogram &h) noexcept
Definition Utility.cpp:37
int64_t max(const Histogram &h) noexcept
Definition Utility.cpp:42
const char * log_label(LogLevel level) noexcept
Return log label for given log level.
Definition Logger.cpp:169
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:180
Logger & set_logger(Logger &logger) noexcept
Set logger globally for all threads.
Definition Logger.cpp:189
Logger & get_logger() noexcept
Return current logger.
Definition Logger.cpp:184
void write_log(WallTime ts, LogLevel level, LogMsgPtr &&msg, std::size_t size) noexcept
Definition Logger.cpp:194
Logger & null_logger() noexcept
Null logger. This logger does nothing and is effectively /dev/null.
Definition Logger.cpp:154
WallClock::time_point WallTime
Definition Time.hpp:111
StoragePtr< MaxLogLine > LogMsgPtr
Definition Logger.hpp:52
LogLevel get_log_level() noexcept
Return current log level.
Definition Logger.cpp:174
Logger & std_logger() noexcept
Definition Logger.cpp:159
LogLevel set_log_level(LogLevel level) noexcept
Set log level globally for all threads.
Definition Logger.cpp:179
Logger & sys_logger() noexcept
System logger. This logger calls syslog().
Definition Logger.cpp:164
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