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 static constexpr size_t upper_bound{48 + 1};
86 char head[upper_bound];
87 size_t hlen{strftime(head, sizeof(head), "%Y/%m/%d %H:%M:%S", &tm)};
88 const auto us{static_cast<int>(us_since_epoch(ts) % 1000000)};
89 hlen += snprintf(head + hlen, upper_bound - hlen, ".%06d %-6s [%d]: ", us, log_label(level), tid);
90 char tail{'\n'};
91 iovec iov[] = {
92 {head, hlen}, //
93 {msg.get(), size}, //
94 {&tail, 1} //
95 };
96
98 // The following lock was required to avoid interleaving.
99 lock_guard<mutex> lock{mutex_};
100 // Best effort given that this is the logger.
101 ignore = writev(fd, iov, sizeof(iov) / sizeof(iov[0]));
102 }
103 mutex mutex_;
105
106class SysLogger final : public Logger {
107 void do_write_log(WallTime /*ts*/, LogLevel level, int /*tid*/, LogMsgPtr&& msg,
108 size_t size) noexcept override
109 {
110 int prio;
111 switch (level) {
112 case LogLevel::None:
113 return;
114 case LogLevel::Crit:
115 prio = LOG_CRIT;
116 break;
117 case LogLevel::Error:
118 prio = LOG_ERR;
119 break;
120 case LogLevel::Warn:
122 break;
123 case LogLevel::Metric:
125 break;
126 case LogLevel::Notice:
128 break;
129 case LogLevel::Info:
130 prio = LOG_INFO;
131 break;
132 default:
133 prio = LOG_DEBUG;
134 }
135 syslog(prio, "%.*s", static_cast<int>(size), static_cast<const char*>(msg.get()));
136 }
138
139// Global log level and logger function.
142
144{
145 return level_.load(memory_order_acquire);
146}
147
148inline Logger& acquire_logger() noexcept
149{
150 return *logger_.load(memory_order_acquire);
151}
152
153} // namespace
154
156{
157 return null_logger_;
158}
159
161{
162 return std_logger_;
163}
164
166{
167 return sys_logger_;
168}
169
170const char* log_label(LogLevel level) noexcept
171{
172 return Labels[static_cast<int>(min(max(level, LogLevel::None), LogLevel::Debug))];
173}
174
179
181{
182 return level_.exchange(max(level, LogLevel{}), memory_order_acq_rel);
183}
184
186{
187 return acquire_logger();
188}
189
191{
192 return *logger_.exchange(&logger, memory_order_acq_rel);
193}
194
195void write_log(WallTime ts, LogLevel level, LogMsgPtr&& msg, std::size_t size) noexcept
196{
197 acquire_logger().write_log(ts, level, static_cast<int>(gettid()), std::move(msg), size);
198}
199
200Logger::~Logger() = default;
201
206
208{
209 write_all_messages();
210}
211
212void AsyncLogger::write_all_messages()
213{
214 Task t;
215 while (tq_.pop(t)) {
216 logger_.write_log(t.ts, t.level, t.tid, LogMsgPtr{t.msg}, t.size);
217 }
218}
219
221{
222 write_all_messages();
223 std::this_thread::sleep_for(50ms);
224
225 return (!tq_.empty() || !stop_);
226}
227
229{
230 stop_ = true;
231}
232
233void AsyncLogger::do_write_log(WallTime ts, LogLevel level, int tid, LogMsgPtr&& msg,
234 size_t size) noexcept
235{
236 // if the queue is full, skip the message
237 if (void* msg_ptr = msg.release();
238 !tq_.push(Task{.ts = ts, .level = level, .tid = tid, .msg = msg_ptr, .size = size})) {
240 }
241}
242
243} // namespace sys
244} // namespace toolbox
AsyncLogger(Logger &logger)
Definition Logger.cpp:202
void stop()
Interrupt and exit any inprogress call to run().
Definition Logger.cpp:228
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:170
constexpr std::int64_t us_since_epoch(std::chrono::time_point< ClockT, Duration > t) noexcept
Definition Time.hpp:181
Logger & set_logger(Logger &logger) noexcept
Set logger globally for all threads.
Definition Logger.cpp:190
Logger & get_logger() noexcept
Return current logger.
Definition Logger.cpp:185
void write_log(WallTime ts, LogLevel level, LogMsgPtr &&msg, std::size_t size) noexcept
Definition Logger.cpp:195
Logger & null_logger() noexcept
Null logger. This logger does nothing and is effectively /dev/null.
Definition Logger.cpp:155
WallClock::time_point WallTime
Definition Time.hpp:112
StoragePtr< MaxLogLine > LogMsgPtr
Definition Logger.hpp:52
LogLevel get_log_level() noexcept
Return current log level.
Definition Logger.cpp:175
Logger & std_logger() noexcept
Definition Logger.cpp:160
LogLevel set_log_level(LogLevel level) noexcept
Set log level globally for all threads.
Definition Logger.cpp:180
Logger & sys_logger() noexcept
System logger. This logger calls syslog().
Definition Logger.cpp:165
constexpr std::size_t size(const detail::Struct< detail::Member< TagsT, ValuesT >... > &s)
Definition Struct.hpp:98
constexpr auto bind() noexcept
Definition Slot.hpp:97
static constexpr std::time_t to_time_t(const time_point &tp) noexcept
Definition Time.hpp:97