Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved.
3 : : * Author: Thun Lu <thun.lu@zohomail.cn>
4 : : * Repo: https://github.com/thun-res/vlink
5 : : * _ __ __ _ __
6 : : * | | / / / / (_) ____ / /__
7 : : * | | / / / / / / / __ \ / //_/
8 : : * | |/ / / /___ / / / / / / / ,<
9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10 : : *
11 : : * Licensed under the Apache License, Version 2.0 (the "License");
12 : : * you may not use this file except in compliance with the License.
13 : : * You may obtain a copy of the License at
14 : : *
15 : : * http://www.apache.org/licenses/LICENSE-2.0
16 : : *
17 : : * Unless required by applicable law or agreed to in writing, software
18 : : * distributed under the License is distributed on an "AS IS" BASIS,
19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 : : * See the License for the specific language governing permissions and
21 : : * limitations under the License.
22 : : */
23 : :
24 : : #include "./base/logger.h"
25 : :
26 : : #include <atomic>
27 : : #include <charconv>
28 : : #include <chrono>
29 : : #include <cstdio>
30 : : #include <filesystem>
31 : : #include <fstream>
32 : : #include <iostream>
33 : : #include <limits>
34 : : #include <memory>
35 : : #include <mutex>
36 : : #include <optional>
37 : : #include <shared_mutex>
38 : : #include <string>
39 : : #include <utility>
40 : : #include <vector>
41 : :
42 : : #include "./base/cached_timestamp.h"
43 : : #include "./base/logger_plugin_interface.h"
44 : : #include "./base/memory_pool.h"
45 : : #include "./base/utils.h"
46 : : #include "./vlink/version.h"
47 : :
48 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
49 : : #include "./base/logger_backend.h"
50 : : #elif defined(__ANDROID__)
51 : : #include <android/log.h>
52 : : #elif defined(__QNX__)
53 : : #include <process.h>
54 : : #include <sys/slog2.h>
55 : : #elif defined(__linux__)
56 : : #include <linux/kernel.h>
57 : : #define VLINK_KMSG_DEV_PATH "/dev/kmsg"
58 : : #endif
59 : :
60 : : namespace vlink {
61 : :
62 : : [[maybe_unused]] static constexpr size_t kDefaultWriteDepth = 1024L * 8;
63 : : [[maybe_unused]] static constexpr size_t kDefaultLogMaxSize = 1024L * 1024L * 10U;
64 : : [[maybe_unused]] static constexpr size_t kDefaultLogMaxCount = 10U;
65 : : [[maybe_unused]] static constexpr int kDefaultLogFlushDelay = 500;
66 : :
67 : 106 : [[maybe_unused]] static std::string get_current_date(bool use_utc = false) {
68 : 106 : auto now = std::chrono::system_clock::now();
69 : 106 : std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
70 : 106 : std::tm now_tm{};
71 : :
72 : : #if defined(_WIN32)
73 : :
74 : : if (use_utc) {
75 : : gmtime_s(&now_tm, &now_time_t);
76 : : } else {
77 : : localtime_s(&now_tm, &now_time_t);
78 : : }
79 : : #else
80 : :
81 [ + + ]: 106 : if (use_utc) {
82 : 1 : gmtime_r(&now_time_t, &now_tm);
83 : : } else {
84 : 105 : localtime_r(&now_time_t, &now_tm);
85 : : }
86 : : #endif
87 : :
88 : : char buffer[32];
89 : 106 : std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", &now_tm);
90 [ + - ]: 106 : return std::string(buffer);
91 : : }
92 : :
93 : 84 : [[maybe_unused]] static constexpr std::string_view get_log_level_str(Logger::Level level) {
94 [ + + + + : 84 : switch (level) {
+ + - - ]
95 : 1 : case Logger::kTrace:
96 : 1 : return "TRACE";
97 : 4 : case Logger::kDebug:
98 : 4 : return "DEBUG";
99 : 69 : case Logger::kInfo:
100 : 69 : return "INFO ";
101 : 4 : case Logger::kWarn:
102 : 4 : return "WARN ";
103 : 4 : case Logger::kError:
104 : 4 : return "ERROR";
105 : 2 : case Logger::kFatal:
106 : 2 : return "FATAL";
107 : : // LCOV_EXCL_START GCOVR_EXCL_START
108 : : case Logger::kOff:
109 : : return "EMPTY";
110 : : default:
111 : : return "EMPTY";
112 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
113 : : }
114 : : }
115 : :
116 : 350 : [[maybe_unused]] static int get_log_level(const std::string& key) {
117 [ + - ]: 700 : std::string str_value = Utils::get_env(key);
118 : :
119 [ + + ]: 350 : if (str_value.empty()) {
120 : 316 : return -1;
121 : : }
122 : :
123 [ + + + - : 34 : if (str_value == "Trace" || str_value == "TRACE" || str_value == "trace") {
- + + + ]
124 : 6 : return Logger::kTrace;
125 [ + - + + : 28 : } else if (str_value == "Debug" || str_value == "DEBUG" || str_value == "debug") {
- + + + ]
126 : 1 : return Logger::kDebug;
127 [ + + + - : 27 : } else if (str_value == "Info" || str_value == "INFO" || str_value == "info") {
+ + + + ]
128 : 5 : return Logger::kInfo;
129 [ + + + - : 22 : } else if (str_value == "Warn" || str_value == "WARN" || str_value == "warn") {
- + + + ]
130 : 1 : return Logger::kWarn;
131 [ + - + + : 21 : } else if (str_value == "Error" || str_value == "ERROR" || str_value == "error") {
- + + + ]
132 : 1 : return Logger::kError;
133 [ + - + - : 20 : } else if (str_value == "Fatal" || str_value == "FATAL" || str_value == "fatal") {
+ + + + ]
134 : 1 : return Logger::kFatal;
135 [ + + + - : 19 : } else if (str_value == "Off" || str_value == "OFF" || str_value == "off") {
- + + + ]
136 : 16 : return Logger::kOff;
137 : : }
138 : :
139 : 3 : int value = -1;
140 : :
141 [ + - ]: 3 : auto [p, error] = std::from_chars(str_value.data(), str_value.data() + str_value.size(), value);
142 : :
143 [ + + ]: 3 : if VUNLIKELY (error != std::errc()) {
144 : 1 : return -1;
145 : : }
146 : :
147 [ + - + + : 2 : if VUNLIKELY (value < 0 || value > Logger::kOff) {
+ + ]
148 : 1 : return Logger::kOff;
149 : : }
150 : :
151 : 1 : return value;
152 : 350 : }
153 : :
154 : 84 : [[maybe_unused]] static std::string_view get_thread_id_str() {
155 : : thread_local char buffer[32];
156 : : thread_local bool initialized = false;
157 : :
158 [ + + ]: 84 : if VUNLIKELY (!initialized) {
159 [ + - ]: 2 : auto [p, ec] = std::to_chars(buffer, buffer + sizeof(buffer) - 1, Utils::get_native_thread_id());
160 : :
161 [ + - ]: 2 : if (ec == std::errc()) {
162 : 2 : *p = '\0';
163 : : } else {
164 : : std::snprintf( // LCOV_EXCL_LINE GCOVR_EXCL_LINE
165 : : buffer, sizeof(buffer), "%llu",
166 : 0 : static_cast<unsigned long long>(Utils::get_native_thread_id())); // NOLINT(runtime/int, google-runtime-int)
167 : : // // LCOV_EXCL_LINE GCOVR_EXCL_LINE
168 : : }
169 : :
170 : 2 : initialized = true;
171 : : }
172 : :
173 : 84 : return buffer;
174 : : }
175 : :
176 : 229071 : static bool& is_logging_on_current_thread() noexcept {
177 : : static thread_local bool is_logging{false};
178 : 229071 : return is_logging;
179 : : }
180 : :
181 : 115 : static bool& is_in_handler_on_current_thread() noexcept {
182 : : static thread_local bool is_in_handler{false};
183 : 115 : return is_in_handler;
184 : : }
185 : :
186 : : static thread_local bool logger_stream_retired = false;
187 : :
188 : : struct LoggerStreamState final {
189 : : CachedTimestamp timestamp;
190 : : std::string console_buffer;
191 : : FastStream stream;
192 : : std::vector<std::unique_ptr<FastStream>> nested_streams;
193 : : size_t depth{0};
194 : : };
195 : :
196 : 142 : static void retire_logger_stream_state(LoggerStreamState* state) noexcept {
197 [ + - ]: 142 : delete state;
198 : 142 : logger_stream_retired = true;
199 : 142 : }
200 : :
201 : 9896 : static LoggerStreamState& get_logger_stream_state() noexcept {
202 : : // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
203 : 144 : thread_local std::unique_ptr<LoggerStreamState, void (*)(LoggerStreamState*)> state(new LoggerStreamState,
204 [ + + ]: 10040 : retire_logger_stream_state);
205 : 9896 : return *state;
206 : : }
207 : :
208 : 0 : static bool& is_plugin_logging_on_current_thread() noexcept {
209 : : static thread_local bool is_plugin_logging{false};
210 : 0 : return is_plugin_logging;
211 : : }
212 : :
213 : 1006 : [[maybe_unused]] static std::mutex& get_print_mtx() {
214 : : static std::mutex print_mtx;
215 : 1006 : return print_mtx;
216 : : }
217 : :
218 : : template <uint8_t PrefixSizeT, uint8_t SuffixSizeT>
219 : 740 : [[maybe_unused]] static void print_with_color(const char (&prefix)[PrefixSizeT], std::string_view log,
220 : : const char (&suffix)[SuffixSizeT], FILE* file, bool in_order,
221 : : bool force_flush) {
222 : 740 : std::unique_lock lock(get_print_mtx(), std::defer_lock);
223 : :
224 [ + + ]: 740 : if (in_order) {
225 [ + - ]: 669 : lock.lock();
226 : : }
227 : :
228 : : if constexpr (PrefixSizeT > 1) {
229 [ + - ]: 734 : std::fwrite(prefix, sizeof(char), PrefixSizeT - 1, file);
230 : : }
231 : :
232 [ + - ]: 740 : if VLIKELY (!log.empty()) {
233 [ + - ]: 740 : std::fwrite(log.data(), sizeof(char), log.size(), file);
234 : : }
235 : :
236 : : if constexpr (SuffixSizeT > 1) {
237 [ + - ]: 740 : std::fwrite(suffix, sizeof(char), SuffixSizeT - 1, file);
238 : : }
239 : :
240 [ + + + - ]: 740 : if (in_order && force_flush) {
241 [ + - ]: 669 : std::fflush(file);
242 : : }
243 : 740 : }
244 : :
245 : : // LoggerGlobal
246 : : struct LoggerGlobal final { // NOLINT(clang-analyzer-optin.performance.Padding)
247 : : std::atomic_bool is_busy{false};
248 : : std::atomic_bool is_initialized{false};
249 : : std::atomic_bool is_initializing{false};
250 : : std::atomic_bool is_stopping{false};
251 : :
252 : : std::string app_name;
253 : : std::string log_path;
254 : : std::string version_log;
255 : : std::atomic<int> console_level{Logger::kDebug};
256 : : std::atomic<int> file_level{Logger::kDebug};
257 : : std::atomic_bool console_in_order{true};
258 : : std::atomic_bool console_level_by_user{false};
259 : : std::atomic_bool file_level_by_user{false};
260 : : std::atomic_bool console_format_enable{false};
261 : : std::atomic_bool utc_enable{false};
262 : : std::mutex level_mtx;
263 : : std::atomic_bool has_console_callback{false};
264 : : std::atomic_bool has_file_callback{false};
265 : : Logger::Callback console_callback;
266 : : Logger::Callback file_callback;
267 : : mutable std::shared_mutex callback_mtx;
268 : : std::atomic_bool has_pending_console_callback{false};
269 : : std::atomic_bool has_pending_file_callback{false};
270 : : Logger::Callback pending_console_callback;
271 : : Logger::Callback pending_file_callback;
272 : : std::mutex pending_mtx;
273 : : std::atomic<std::ios_base::fmtflags> stream_flags{std::ios_base::dec | std::ios_base::skipws};
274 : : std::atomic<int> stream_precision{6};
275 : : std::atomic<int> stream_width{0};
276 : :
277 : 230097 : static LoggerGlobal& get() {
278 [ + + + - : 230097 : static LoggerGlobal instance;
+ - - - ]
279 : 230097 : return instance;
280 : : }
281 : :
282 : : private:
283 : 118 : LoggerGlobal() {
284 [ + - ]: 118 : MemoryPool::global_instance();
285 : 118 : get_print_mtx();
286 : 118 : }
287 : : };
288 : :
289 : 40 : static void install_console_handler(Logger::Callback&& callback) noexcept {
290 : 40 : auto& global_instance = LoggerGlobal::get();
291 : 40 : Logger::Callback retired;
292 : 40 : Logger::Callback retired_pending;
293 : :
294 : : {
295 : 40 : std::unique_lock lock(global_instance.callback_mtx);
296 : :
297 : : {
298 : 40 : std::lock_guard pending_lock(global_instance.pending_mtx);
299 : :
300 [ - + ]: 40 : if (global_instance.has_pending_console_callback.exchange(false, std::memory_order_acq_rel)) {
301 : 0 : retired_pending = std::move(global_instance.pending_console_callback);
302 : : }
303 : 40 : }
304 : :
305 : 40 : retired = std::move(global_instance.console_callback);
306 : 40 : global_instance.console_callback = std::move(callback);
307 : 40 : global_instance.has_console_callback.store(static_cast<bool>(global_instance.console_callback),
308 : : std::memory_order_release);
309 : 40 : }
310 : 40 : }
311 : :
312 : 9 : static void install_file_handler(Logger::Callback&& callback) noexcept {
313 : 9 : auto& global_instance = LoggerGlobal::get();
314 : 9 : Logger::Callback retired;
315 : 9 : Logger::Callback retired_pending;
316 : :
317 : : {
318 : 9 : std::unique_lock lock(global_instance.callback_mtx);
319 : :
320 : : {
321 : 9 : std::lock_guard pending_lock(global_instance.pending_mtx);
322 : :
323 [ - + ]: 9 : if (global_instance.has_pending_file_callback.exchange(false, std::memory_order_acq_rel)) {
324 : 0 : retired_pending = std::move(global_instance.pending_file_callback);
325 : : }
326 : 9 : }
327 : :
328 : 9 : retired = std::move(global_instance.file_callback);
329 : 9 : global_instance.file_callback = std::move(callback);
330 : 9 : global_instance.has_file_callback.store(static_cast<bool>(global_instance.file_callback),
331 : : std::memory_order_release);
332 : 9 : }
333 : 9 : }
334 : :
335 : 55 : static void install_pending_handlers() noexcept {
336 : 55 : auto& global_instance = LoggerGlobal::get();
337 : :
338 [ + + + - : 55 : if VLIKELY (!global_instance.has_pending_console_callback.load(std::memory_order_acquire) &&
+ + ]
339 : : !global_instance.has_pending_file_callback.load(std::memory_order_acquire)) {
340 : 51 : return;
341 : : }
342 : :
343 [ - + ]: 4 : if VUNLIKELY (is_in_handler_on_current_thread()) {
344 : 0 : return;
345 : : }
346 : :
347 : 4 : Logger::Callback retired_console;
348 : 4 : Logger::Callback retired_file;
349 : :
350 : : {
351 : 4 : std::unique_lock callback_lock(global_instance.callback_mtx);
352 : 4 : std::lock_guard pending_lock(global_instance.pending_mtx);
353 : :
354 [ + - ]: 4 : if (global_instance.has_pending_console_callback.exchange(false, std::memory_order_acq_rel)) {
355 : 4 : retired_console = std::move(global_instance.console_callback);
356 : 4 : global_instance.console_callback = std::move(global_instance.pending_console_callback);
357 : 4 : global_instance.has_console_callback.store(static_cast<bool>(global_instance.console_callback),
358 : : std::memory_order_release);
359 : : }
360 : :
361 [ + + ]: 4 : if (global_instance.has_pending_file_callback.exchange(false, std::memory_order_acq_rel)) {
362 : 3 : retired_file = std::move(global_instance.file_callback);
363 : 3 : global_instance.file_callback = std::move(global_instance.pending_file_callback);
364 : 3 : global_instance.has_file_callback.store(static_cast<bool>(global_instance.file_callback),
365 : : std::memory_order_release);
366 : : }
367 : 4 : }
368 : 4 : }
369 : :
370 : : // Logger::Impl
371 : : struct Logger::Impl final { // NOLINT(clang-analyzer-optin.performance.Padding)
372 : : enum class PluginInitState : uint8_t {
373 : : kPending,
374 : : kInitializing,
375 : : kComplete,
376 : : };
377 : :
378 : : struct PluginFinalizer final {
379 : : Logger& instance;
380 : :
381 : 0 : ~PluginFinalizer() {
382 : 0 : auto& global_instance = LoggerGlobal::get();
383 : :
384 : 0 : global_instance.is_stopping.store(true, std::memory_order_release);
385 : :
386 : : #if defined(_WIN32)
387 : : if (Utils::is_terminating()) {
388 : : return;
389 : : }
390 : : #endif
391 : :
392 : 0 : instance.impl_->is_enable_file_channel.store(false, std::memory_order_release);
393 : 0 : instance.impl_->interface->flush();
394 : 0 : instance.impl_->interface.reset();
395 : 0 : instance.impl_->plugin.clear();
396 : 0 : }
397 : : };
398 : :
399 : : std::atomic_bool disk_emergency{false};
400 : : std::atomic_bool is_enable_file_channel{false};
401 : : std::once_flag file_init;
402 : : std::atomic_bool file_ready{false};
403 : :
404 : : std::atomic<PluginInitState> plugin_init_state{PluginInitState::kPending};
405 : : std::string plugin_name;
406 : : Plugin plugin;
407 : : std::shared_ptr<LoggerPluginInterface> interface;
408 : :
409 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
410 : : std::unique_ptr<LoggerBackend> backend;
411 : : #elif defined(__ANDROID__)
412 : : #elif defined(__QNX__)
413 : : slog2_buffer_t slog2_buffer{nullptr};
414 : : #elif defined(__linux__)
415 : : std::ofstream kmsg_dev;
416 : : std::mutex file_mtx;
417 : : #endif
418 : : };
419 : :
420 : : // Logger
421 : 57 : void Logger::init(const std::string& app_name, const std::string& log_path) noexcept {
422 [ + + + - ]: 57 : static auto& global_instance = LoggerGlobal::get();
423 : :
424 [ + - ]: 57 : if (!app_name.empty()) {
425 : 57 : global_instance.app_name = app_name;
426 : :
427 [ + + ]: 57 : if (!log_path.empty()) {
428 : 7 : global_instance.log_path = log_path;
429 : : }
430 : : }
431 : :
432 : 57 : Logger::get();
433 : 57 : }
434 : :
435 : 6548 : Logger& Logger::get() noexcept {
436 [ + + + - ]: 6548 : static Logger instance;
437 : :
438 [ + + + - : 6668 : if VUNLIKELY (!instance.impl_->file_ready.load(std::memory_order_acquire) && !is_logging_on_current_thread() &&
+ + + - +
+ + + +
+ ]
439 : : !LoggerGlobal::get().is_busy.load(std::memory_order_acquire) &&
440 : : LoggerGlobal::get().file_level.load(std::memory_order_acquire) < kOff) {
441 : 106 : std::call_once(instance.impl_->file_init, [&] {
442 : 106 : auto& global_instance = LoggerGlobal::get();
443 : 106 : auto& logging = is_logging_on_current_thread();
444 : 106 : const bool previous = logging;
445 : :
446 : 106 : global_instance.is_busy.store(true, std::memory_order_release);
447 : 106 : logging = true;
448 : 106 : instance.initialize_file_channel();
449 : :
450 [ + + + + ]: 211 : if (instance.impl_->is_enable_file_channel.load(std::memory_order_acquire) &&
451 [ + - ]: 210 : kInfo >= global_instance.file_level.load(std::memory_order_acquire)) {
452 : 105 : instance.write_to_file(kInfo, global_instance.version_log);
453 : : }
454 : :
455 : 106 : logging = previous;
456 : 106 : global_instance.is_busy.store(false, std::memory_order_release);
457 : 106 : });
458 : :
459 : 106 : instance.initialize_plugin();
460 : :
461 [ + - ]: 106 : if (instance.impl_->plugin_init_state.load(std::memory_order_acquire) != Impl::PluginInitState::kInitializing) {
462 : 106 : instance.impl_->file_ready.store(true, std::memory_order_release);
463 : : }
464 : : }
465 : :
466 : 6547 : return instance;
467 : : }
468 : :
469 : 150 : void Logger::flush() noexcept {
470 : 150 : auto& global_instance = LoggerGlobal::get();
471 : :
472 [ + + - + : 150 : if VUNLIKELY (is_logging_on_current_thread() || global_instance.is_initializing.load(std::memory_order_acquire) ||
+ + - + +
+ ]
473 : : global_instance.is_stopping.load(std::memory_order_acquire)) {
474 : 2 : return;
475 : : }
476 : :
477 : 148 : Logger& instance = Logger::get();
478 : :
479 : : {
480 : 148 : std::lock_guard lock(get_print_mtx());
481 : 148 : std::fflush(stdout);
482 : 148 : std::fflush(stderr);
483 : 148 : }
484 : :
485 [ + + ]: 148 : if (!instance.impl_->is_enable_file_channel.load(std::memory_order_acquire)) {
486 : 1 : return;
487 : : }
488 : :
489 [ - + ]: 147 : if VUNLIKELY (instance.impl_->interface) {
490 : 0 : auto& is_plugin_logging = is_plugin_logging_on_current_thread();
491 : :
492 [ # # ]: 0 : if VUNLIKELY (is_plugin_logging) {
493 : 0 : return;
494 : : }
495 : :
496 : 0 : is_plugin_logging = true;
497 : 0 : instance.impl_->interface->flush();
498 : 0 : is_plugin_logging = false;
499 : :
500 : 0 : return;
501 : : }
502 : :
503 [ + + ]: 147 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
504 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
505 : : }
506 : :
507 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
508 : 146 : instance.impl_->backend->flush();
509 : : #endif
510 : : }
511 : :
512 : 44 : void Logger::register_console_handler(Callback&& callback) noexcept {
513 : 44 : auto& global_instance = LoggerGlobal::get();
514 : :
515 [ + + ]: 44 : if VUNLIKELY (is_in_handler_on_current_thread()) {
516 : 4 : std::lock_guard lock(global_instance.pending_mtx);
517 : :
518 : 4 : global_instance.pending_console_callback = std::move(callback);
519 : 4 : global_instance.has_pending_console_callback.store(true, std::memory_order_release);
520 : :
521 : 4 : return;
522 : 4 : }
523 : :
524 : 40 : install_console_handler(std::move(callback));
525 : : }
526 : :
527 : 12 : void Logger::register_file_handler(Callback&& callback) noexcept {
528 : 12 : auto& global_instance = LoggerGlobal::get();
529 : :
530 [ + + ]: 12 : if VUNLIKELY (is_in_handler_on_current_thread()) {
531 : 3 : std::lock_guard lock(global_instance.pending_mtx);
532 : :
533 : 3 : global_instance.pending_file_callback = std::move(callback);
534 : 3 : global_instance.has_pending_file_callback.store(true, std::memory_order_release);
535 : :
536 : 3 : return;
537 : 3 : }
538 : :
539 : 9 : install_file_handler(std::move(callback));
540 : : }
541 : :
542 : 52 : void Logger::set_console_level(Level level) noexcept {
543 : 52 : auto& global_instance = LoggerGlobal::get();
544 : 52 : std::lock_guard lock(global_instance.level_mtx);
545 : :
546 : 52 : global_instance.console_level.store(level, std::memory_order_release);
547 : 52 : global_instance.console_level_by_user.store(true, std::memory_order_release);
548 : 52 : }
549 : :
550 : 28 : void Logger::set_file_level(Level level) noexcept {
551 : 28 : auto& global_instance = LoggerGlobal::get();
552 : 28 : std::lock_guard lock(global_instance.level_mtx);
553 : :
554 : 28 : global_instance.file_level.store(level, std::memory_order_release);
555 : 28 : global_instance.file_level_by_user.store(true, std::memory_order_release);
556 : 28 : }
557 : :
558 : 2 : void Logger::set_console_fmt_enable(bool enable) noexcept {
559 : 2 : LoggerGlobal::get().console_format_enable.store(enable, std::memory_order_release);
560 : 2 : }
561 : :
562 : 17 : Logger::Level Logger::get_console_level() noexcept {
563 : 34 : return static_cast<Logger::Level>(LoggerGlobal::get().console_level.load(std::memory_order_acquire));
564 : : }
565 : :
566 : 8 : Logger::Level Logger::get_file_level() noexcept {
567 : 16 : return static_cast<Logger::Level>(LoggerGlobal::get().file_level.load(std::memory_order_acquire));
568 : : }
569 : :
570 : 3 : bool Logger::get_console_fmt_enable() noexcept {
571 : 3 : return LoggerGlobal::get().console_format_enable.load(std::memory_order_acquire);
572 : : }
573 : :
574 : 6 : void Logger::set_stream_flag(std::ios_base::fmtflags flags) noexcept {
575 : 6 : LoggerGlobal::get().stream_flags.store(flags, std::memory_order_release);
576 : 6 : }
577 : :
578 : 2 : void Logger::set_stream_precision(int precision) noexcept {
579 : 2 : LoggerGlobal::get().stream_precision.store(precision, std::memory_order_release);
580 : 2 : }
581 : :
582 : 5 : void Logger::set_stream_width(int width) noexcept {
583 : 5 : LoggerGlobal::get().stream_width.store(width, std::memory_order_release);
584 : 5 : }
585 : :
586 : 2 : std::ios_base::fmtflags Logger::get_stream_flag() noexcept {
587 : 2 : return LoggerGlobal::get().stream_flags.load(std::memory_order_acquire);
588 : : }
589 : :
590 : 2 : int Logger::get_stream_precision() noexcept {
591 : 4 : return LoggerGlobal::get().stream_precision.load(std::memory_order_acquire);
592 : : }
593 : :
594 : 4 : int Logger::get_stream_width() noexcept { return LoggerGlobal::get().stream_width.load(std::memory_order_acquire); }
595 : :
596 : 3 : void Logger::enable_backtrace(size_t size) noexcept {
597 : 3 : Logger& instance = Logger::get();
598 : :
599 [ - + ]: 3 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
600 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
601 : : }
602 : :
603 [ - + ]: 3 : if VUNLIKELY (!instance.impl_->is_enable_file_channel.load(std::memory_order_acquire)) {
604 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
605 : : }
606 : :
607 [ - + ]: 3 : if VUNLIKELY (instance.impl_->interface) {
608 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
609 : : }
610 : :
611 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
612 : 3 : instance.impl_->backend->enable_backtrace(size);
613 : :
614 : : #else
615 : : (void)size;
616 : : #endif
617 : : }
618 : :
619 : 3 : void Logger::disable_backtrace() noexcept {
620 : 3 : Logger& instance = Logger::get();
621 : :
622 [ - + ]: 3 : if VUNLIKELY (!instance.impl_->is_enable_file_channel.load(std::memory_order_acquire)) {
623 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
624 : : }
625 : :
626 [ - + ]: 3 : if VUNLIKELY (instance.impl_->interface) {
627 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
628 : : }
629 : :
630 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
631 [ + - ]: 3 : if (instance.impl_->backend) {
632 : 3 : instance.impl_->backend->disable_backtrace();
633 : : }
634 : : #else
635 : : (void)instance;
636 : : #endif
637 : : }
638 : :
639 : 2 : void Logger::dump_backtrace() noexcept {
640 : 2 : Logger& instance = Logger::get();
641 : :
642 [ - + ]: 2 : if VUNLIKELY (!instance.impl_->is_enable_file_channel.load(std::memory_order_acquire)) {
643 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
644 : : }
645 : :
646 [ - + ]: 2 : if VUNLIKELY (instance.impl_->interface) {
647 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
648 : : }
649 : :
650 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
651 : 4 : LoggerBackend::ConsoleWriter console_writer = [](Level level, std::string_view log) {
652 [ + - ]: 8 : if (level >= LoggerGlobal::get().console_level.load(std::memory_order_acquire)) {
653 : 4 : write_to_console(level, log, true);
654 : : }
655 : 2 : };
656 [ + - ]: 2 : if (instance.impl_->backend) {
657 : 2 : instance.impl_->backend->dump_backtrace(console_writer);
658 : : }
659 : : #else
660 : : (void)instance;
661 : : #endif
662 : 2 : }
663 : :
664 : 1 : bool Logger::is_busy() noexcept { return LoggerGlobal::get().is_busy.load(std::memory_order_acquire); }
665 : :
666 : 219178 : bool Logger::is_writable(Level level) noexcept {
667 [ - + ]: 219178 : if VUNLIKELY (level >= kOff) {
668 : 0 : return false;
669 : : }
670 : :
671 [ + + ]: 219178 : if VUNLIKELY (level == kFatal) {
672 : 141 : return true;
673 : : }
674 : :
675 : 219037 : return can_log(level);
676 : : }
677 : :
678 : 214145 : bool Logger::try_acquire_periodic_log(Level level, int64_t interval_ms,
679 : : std::atomic<uint64_t>& last_log_time_ns) noexcept {
680 [ + + ]: 214145 : if VUNLIKELY (level >= kFatal) {
681 : 1 : return false;
682 : : }
683 : :
684 [ + + ]: 214144 : if (!is_writable(level)) {
685 : 1 : return false;
686 : : }
687 : :
688 [ + + ]: 214143 : if VUNLIKELY (interval_ms <= 0) {
689 : 3 : return true;
690 : : }
691 : :
692 : 214140 : constexpr uint64_t kNanosecondsPerMillisecond = 1000U * 1000U;
693 : 214140 : const auto unsigned_interval_ms = static_cast<uint64_t>(interval_ms);
694 : 214140 : const auto interval_ns = unsigned_interval_ms > std::numeric_limits<uint64_t>::max() / kNanosecondsPerMillisecond
695 [ - + ]: 214140 : ? std::numeric_limits<uint64_t>::max()
696 : 214140 : : unsigned_interval_ms * kNanosecondsPerMillisecond;
697 [ + + + - ]: 214140 : static const auto kStartTime = std::chrono::steady_clock::now();
698 : : const auto now_ns =
699 : 214140 : static_cast<uint64_t>(
700 : 214140 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - kStartTime).count()) +
701 : 214140 : 1U;
702 : 428280 : auto last_ns = last_log_time_ns.load(std::memory_order_relaxed);
703 : :
704 : : for (;;) {
705 [ + + ]: 214140 : if VLIKELY (last_ns != 0U) {
706 [ + + ]: 214130 : if VUNLIKELY (last_ns >= now_ns) {
707 : 1 : return false;
708 : : }
709 : :
710 [ + + ]: 214129 : if VLIKELY (now_ns - last_ns < interval_ns) {
711 : 214128 : return false;
712 : : }
713 : : }
714 : :
715 [ + - ]: 22 : if VLIKELY (last_log_time_ns.compare_exchange_weak(last_ns, now_ns, std::memory_order_relaxed,
716 : : std::memory_order_relaxed)) {
717 : 11 : return true;
718 : : }
719 : : }
720 : : }
721 : :
722 : 118 : Logger::Logger() noexcept {
723 [ + - + - ]: 118 : static auto& global_instance = LoggerGlobal::get();
724 : 118 : global_instance.is_initializing.store(true, std::memory_order_release);
725 : :
726 : 118 : auto& is_logging = is_logging_on_current_thread();
727 : 118 : const bool was_logging = is_logging;
728 : :
729 : 118 : is_logging = true;
730 : 118 : impl_ = std::make_unique<Impl>();
731 : 118 : global_instance.is_busy.store(true, std::memory_order_release);
732 : :
733 : 118 : int common_level = get_log_level("VLINK_LOG_LEVEL");
734 : :
735 : : {
736 : 118 : std::lock_guard lock(global_instance.level_mtx);
737 : :
738 [ + + ]: 118 : if (!global_instance.console_level_by_user.load(std::memory_order_acquire)) {
739 : 116 : int console_level = get_log_level("VLINK_LOG_CONSOLE_LEVEL");
740 : :
741 [ + + ]: 116 : if (console_level >= 0) {
742 : 16 : global_instance.console_level.store(console_level, std::memory_order_release);
743 [ + + ]: 100 : } else if (common_level >= 0) {
744 : 1 : global_instance.console_level.store(common_level, std::memory_order_release);
745 : : }
746 : : }
747 : :
748 [ + + ]: 118 : if (!global_instance.file_level_by_user.load(std::memory_order_acquire)) {
749 : 116 : int file_level = get_log_level("VLINK_LOG_FILE_LEVEL");
750 : :
751 [ + + ]: 116 : if (file_level >= 0) {
752 : 13 : global_instance.file_level.store(file_level, std::memory_order_release);
753 [ + + ]: 103 : } else if (common_level >= 0) {
754 : 4 : global_instance.file_level.store(common_level, std::memory_order_release);
755 : : }
756 : : }
757 : 118 : }
758 : :
759 : : {
760 : 236 : std::string enable_console_unorder = Utils::get_env("VLINK_LOG_CONSOLE_UNORDER");
761 : 118 : global_instance.console_in_order.store(enable_console_unorder != "1", std::memory_order_release);
762 : :
763 : 236 : std::string utc_enable_str = Utils::get_env("VLINK_LOG_UTC_ENABLE");
764 : 118 : global_instance.utc_enable.store(utc_enable_str == "1", std::memory_order_release);
765 : :
766 [ + + ]: 118 : if (global_instance.app_name.empty()) {
767 : 111 : global_instance.app_name = Utils::get_app_name();
768 : :
769 [ - + ]: 111 : if VUNLIKELY (global_instance.app_name.empty()) {
770 : : global_instance.app_name = "unknown"; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
771 : : std::cerr << "Can not get app name for logger!" << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
772 : : }
773 : : }
774 : :
775 : 236 : std::string console_format = Utils::get_env("VLINK_LOG_CONSOLE_FMT");
776 : 118 : global_instance.console_format_enable.store(console_format == "1", std::memory_order_release);
777 : 118 : }
778 : :
779 : 118 : is_logging = was_logging;
780 : 118 : global_instance.is_busy.store(false, std::memory_order_release);
781 : 118 : global_instance.is_initializing.store(false, std::memory_order_release);
782 : 118 : global_instance.is_initialized.store(true, std::memory_order_release);
783 : 118 : }
784 : :
785 : 223 : Logger::~Logger() noexcept {
786 : 118 : auto& global_instance = LoggerGlobal::get();
787 : 118 : global_instance.is_stopping.store(true, std::memory_order_release);
788 : :
789 : : #if defined(_WIN32)
790 : : if (Utils::is_terminating()) {
791 : : (void)impl_.release();
792 : : return;
793 : : }
794 : : #endif
795 : :
796 [ + + ]: 118 : if (!impl_->is_enable_file_channel.exchange(false, std::memory_order_acq_rel)) {
797 : 13 : return;
798 : : }
799 : :
800 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
801 : 105 : impl_->backend.reset();
802 : :
803 : : #elif defined(__ANDROID__)
804 : : #elif defined(__QNX__)
805 : : #elif defined(__linux__)
806 : :
807 : : if (impl_->kmsg_dev.is_open()) {
808 : : impl_->kmsg_dev.close();
809 : : }
810 : :
811 : : #endif
812 [ + + ]: 118 : }
813 : :
814 : 223774 : bool Logger::can_log(Level level) noexcept {
815 : 223774 : auto& global_instance = LoggerGlobal::get();
816 : :
817 [ + + ]: 223774 : if VUNLIKELY (!global_instance.is_initialized.load(std::memory_order_acquire)) {
818 [ + - + - : 82 : if (is_logging_on_current_thread() || global_instance.is_initializing.load(std::memory_order_acquire) ||
- + - + ]
819 : 41 : global_instance.is_stopping.load(std::memory_order_acquire)) {
820 : 0 : return false;
821 : : }
822 : :
823 : 41 : Logger::get();
824 : : }
825 : :
826 [ + # + + : 455897 : if (level != kFatal && level < global_instance.console_level.load(std::memory_order_acquire) &&
+ + ]
827 [ + + ]: 16700 : level < global_instance.file_level.load(std::memory_order_acquire)) {
828 : 171 : return false;
829 : : }
830 : :
831 [ + + - + : 223602 : if VUNLIKELY (is_logging_on_current_thread() || global_instance.is_stopping.load(std::memory_order_acquire)) {
+ + ]
832 : 3 : return false;
833 : : }
834 : :
835 : 223600 : return true;
836 : : }
837 : :
838 : 4878 : void Logger::write(Level level, std::string_view log) noexcept {
839 : 4878 : auto& global_instance = LoggerGlobal::get();
840 : :
841 [ + + - + : 4878 : if VUNLIKELY (is_logging_on_current_thread() || global_instance.is_initializing.load(std::memory_order_acquire) ||
+ + - + +
+ ]
842 : : global_instance.is_stopping.load(std::memory_order_acquire)) {
843 : 2 : return;
844 : : }
845 : :
846 : 4876 : Logger& instance = Logger::get();
847 : :
848 [ + + ]: 9752 : if (level >= global_instance.console_level.load(std::memory_order_acquire)) {
849 : 785 : write_to_console(level, log);
850 : : }
851 : :
852 [ + + ]: 9752 : if (level >= global_instance.file_level.load(std::memory_order_acquire)) {
853 : 4812 : instance.write_to_file(level, log);
854 : : }
855 : : }
856 : :
857 : 4864 : FastStream& Logger::get_local_stream() noexcept {
858 [ + + + - ]: 4864 : static auto& global_instance = LoggerGlobal::get();
859 : : FastStream* selected;
860 : :
861 [ - + ]: 4864 : if VUNLIKELY (logger_stream_retired) {
862 : : // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new)
863 : 0 : selected = new FastStream;
864 : : } else {
865 : 4864 : auto& state = get_logger_stream_state();
866 : :
867 [ + + ]: 4864 : if VUNLIKELY (state.depth > state.nested_streams.size()) {
868 : 1 : state.nested_streams.emplace_back(std::make_unique<FastStream>());
869 : : }
870 : :
871 [ + + ]: 4864 : selected = state.depth == 0 ? &state.stream : state.nested_streams[state.depth - 1].get();
872 : 4864 : ++state.depth;
873 : : }
874 : :
875 : 4864 : auto& stream = *selected;
876 : :
877 : 4864 : stream.reset();
878 : :
879 : 4864 : auto flags = global_instance.stream_flags.load(std::memory_order_acquire);
880 : :
881 [ + + ]: 4864 : if VUNLIKELY (stream.flags() != flags) {
882 : 4 : stream.flags(flags);
883 : : }
884 : :
885 : 4864 : auto precision = global_instance.stream_precision.load(std::memory_order_acquire);
886 : :
887 [ + + ]: 4864 : if VUNLIKELY (stream.precision() != precision) {
888 : 3 : stream.precision(precision);
889 : : }
890 : :
891 : 4864 : auto width = global_instance.stream_width.load(std::memory_order_acquire);
892 : :
893 [ + + ]: 4864 : if VUNLIKELY (stream.width() != width) {
894 : 1 : stream.width(width);
895 : : }
896 : :
897 : 4864 : return stream;
898 : : }
899 : :
900 : 4864 : void Logger::release_local_stream(FastStream* stream) noexcept {
901 [ - + ]: 4864 : if VUNLIKELY (logger_stream_retired) {
902 [ # # ]: 0 : delete stream;
903 : : } else {
904 : 4864 : --get_logger_stream_state().depth;
905 : : }
906 : 4864 : }
907 : :
908 : 106 : void Logger::initialize_file_channel() noexcept {
909 : 106 : auto& global_instance = LoggerGlobal::get();
910 : 106 : global_instance.version_log.reserve(128);
911 : :
912 : 106 : global_instance.version_log.append("***** ");
913 : 106 : global_instance.version_log.append("[PNAME: ");
914 : 106 : global_instance.version_log.append(global_instance.app_name);
915 : 106 : global_instance.version_log.append("] ");
916 : :
917 : 106 : global_instance.version_log.append("[PID: ");
918 : 106 : global_instance.version_log.append(Utils::get_pid_str());
919 : 106 : global_instance.version_log.append("] ");
920 : :
921 [ + + ]: 106 : if (global_instance.utc_enable.load(std::memory_order_acquire)) {
922 : 1 : global_instance.version_log.append("[DATE (UTC): ");
923 : 1 : global_instance.version_log.append(get_current_date(true));
924 : 1 : global_instance.version_log.append("] ");
925 : : } else {
926 : 105 : global_instance.version_log.append("[DATE: ");
927 : 105 : global_instance.version_log.append(get_current_date(false));
928 : 105 : global_instance.version_log.append("] ");
929 : : }
930 : :
931 : 106 : global_instance.version_log.append("[VERSION: ");
932 : 106 : global_instance.version_log.append(VLINK_VERSION);
933 : 106 : global_instance.version_log.append("] ");
934 : 106 : global_instance.version_log.append("*****");
935 : :
936 : 106 : impl_->plugin_name = Utils::get_env("VLINK_LOG_PLUGIN");
937 : :
938 [ + + ]: 106 : if (!impl_->plugin_name.empty()) {
939 : 1 : return;
940 : : }
941 : :
942 [ + + ]: 105 : if (global_instance.log_path.empty()) {
943 : 198 : std::string log_dir = Utils::get_env("VLINK_LOG_DIR");
944 : :
945 [ + - ]: 99 : if (log_dir.empty()) {
946 : 99 : log_dir = Utils::get_tmp_dir() + "/" + "vlink-log";
947 [ # # ]: 0 : } else if (log_dir.back() == '/') {
948 : 0 : log_dir.pop_back();
949 : : }
950 : :
951 : : try {
952 [ + - + - : 99 : if (!std::filesystem::exists(log_dir)) {
+ + ]
953 [ + - + - ]: 1 : std::filesystem::create_directories(log_dir);
954 : : }
955 : : } catch (std::exception&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
956 : : log_dir = Utils::get_tmp_dir(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
957 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
958 : :
959 : 198 : std::string log_pid_enable = Utils::get_env("VLINK_LOG_PID_ENABLE");
960 : :
961 : 99 : global_instance.log_path = log_dir + "/" + global_instance.app_name;
962 : :
963 [ - + ]: 99 : if (log_pid_enable == "1") {
964 : 0 : global_instance.log_path += "/" + Utils::get_pid_str();
965 : : }
966 : 99 : }
967 : :
968 : 105 : size_t log_max_size = kDefaultLogMaxSize;
969 : 105 : size_t log_max_count = kDefaultLogMaxCount;
970 : 105 : int log_flush_delay_ms = kDefaultLogFlushDelay;
971 : :
972 : : {
973 : 210 : std::string log_max_size_str = Utils::get_env("VLINK_LOG_MAX_SIZE");
974 : 210 : std::string log_max_count_str = Utils::get_env("VLINK_LOG_MAX_COUNT");
975 : 210 : std::string log_flush_delay_str = Utils::get_env("VLINK_LOG_FLUSH_DELAY");
976 : :
977 [ + + ]: 105 : if (!log_max_size_str.empty()) {
978 : 4 : std::from_chars(log_max_size_str.data(), log_max_size_str.data() + log_max_size_str.size(), log_max_size);
979 : : }
980 : :
981 [ + + ]: 105 : if (!log_max_count_str.empty()) {
982 : 4 : std::from_chars(log_max_count_str.data(), log_max_count_str.data() + log_max_count_str.size(), log_max_count);
983 : : }
984 : :
985 [ + + ]: 105 : if (!log_flush_delay_str.empty()) {
986 : 4 : std::from_chars(log_flush_delay_str.data(), log_flush_delay_str.data() + log_flush_delay_str.size(),
987 : : log_flush_delay_ms);
988 : : }
989 : 105 : }
990 : :
991 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
992 : 210 : std::string log_strategy = Utils::get_env("VLINK_LOG_STORE_STRATEGY");
993 : 210 : std::string log_append = Utils::get_env("VLINK_LOG_OPEN_APPEND");
994 : 210 : std::string log_block = Utils::get_env("VLINK_LOG_BLOCK_SYNC");
995 : 210 : std::string log_depth = Utils::get_env("VLINK_LOG_WRITE_DEPTH");
996 : :
997 : 105 : size_t log_write_depth = kDefaultWriteDepth;
998 : :
999 [ + + ]: 105 : if (!log_depth.empty()) {
1000 : 4 : std::from_chars(log_depth.data(), log_depth.data() + log_depth.size(), log_write_depth);
1001 : : }
1002 : :
1003 : 105 : LoggerBackend::Config config;
1004 : 105 : config.app_name = global_instance.app_name;
1005 : 105 : config.log_path = global_instance.log_path;
1006 : 105 : config.max_file_size = log_max_size;
1007 : 105 : config.max_files = log_max_count;
1008 : 105 : config.queue_size = log_write_depth;
1009 : 105 : config.flush_interval_ms = log_flush_delay_ms > 0 ? static_cast<uint32_t>(log_flush_delay_ms) : 0U;
1010 : 105 : config.fixed_filename = log_strategy == "1";
1011 : 105 : config.append = log_append == "1";
1012 : 105 : config.block_when_full = log_block == "1";
1013 : 105 : config.use_utc = global_instance.utc_enable.load(std::memory_order_acquire);
1014 : :
1015 : : try {
1016 [ + - ]: 210 : impl_->backend = std::make_unique<LoggerBackend>(std::move(config), [this](std::string_view message) {
1017 : 1 : impl_->disk_emergency.store(true, std::memory_order_release);
1018 : 1 : std::cerr << "VLink logger disk emergency: " << message << std::endl;
1019 : 105 : });
1020 [ - - ]: 0 : } catch (const std::exception& error) {
1021 : 0 : impl_->disk_emergency.store(true, std::memory_order_release);
1022 : 0 : std::cerr << "VLink logger disk emergency: " << error.what() << std::endl;
1023 : 0 : }
1024 : :
1025 : : #elif defined(__ANDROID__)
1026 : : #elif defined(__QNX__)
1027 : : slog2_buffer_set_config_t buffer_cfg;
1028 : :
1029 : : buffer_cfg.num_buffers = 1;
1030 : : buffer_cfg.buffer_set_name = "vlink-log";
1031 : : buffer_cfg.verbosity_level = SLOG2_DEBUG2;
1032 : : buffer_cfg.buffer_config[0].buffer_name = global_instance.app_name.c_str();
1033 : : buffer_cfg.buffer_config[0].num_pages = 32;
1034 : : buffer_cfg.max_retries = 3;
1035 : :
1036 : : if VUNLIKELY (slog2_register(&buffer_cfg, &impl_->slog2_buffer, 0) != 0) {
1037 : : impl_->disk_emergency.store(true, std::memory_order_release);
1038 : : std::cerr << "Failed to register slog2 buffer" << std::endl;
1039 : : } else {
1040 : : slog2_set_default_buffer(impl_->slog2_buffer);
1041 : : }
1042 : :
1043 : : #elif defined(__linux__)
1044 : : impl_->kmsg_dev.open(VLINK_KMSG_DEV_PATH, std::ofstream::out | std::ofstream::app);
1045 : :
1046 : : std::error_code ec(errno, std::generic_category());
1047 : :
1048 : : if VUNLIKELY (!impl_->kmsg_dev.is_open()) {
1049 : : impl_->disk_emergency.store(true, std::memory_order_release);
1050 : : std::cerr << "Failed to open " << VLINK_KMSG_DEV_PATH << ": " << ec.message() << std::endl;
1051 : :
1052 : : return;
1053 : : }
1054 : :
1055 : : #endif
1056 : :
1057 : 105 : impl_->is_enable_file_channel.store(true, std::memory_order_release);
1058 : 105 : }
1059 : :
1060 : 106 : void Logger::initialize_plugin() noexcept {
1061 [ + + + - : 106 : if VUNLIKELY (!impl_->plugin_name.empty() &&
+ + ]
1062 : : impl_->plugin_init_state.load(std::memory_order_acquire) != Impl::PluginInitState::kComplete) {
1063 : 1 : auto expected = Impl::PluginInitState::kPending;
1064 : :
1065 [ + - ]: 1 : if (impl_->plugin_init_state.compare_exchange_strong(expected, Impl::PluginInitState::kInitializing,
1066 : : std::memory_order_acq_rel, std::memory_order_acquire)) {
1067 : 1 : auto& global_instance = LoggerGlobal::get();
1068 : 1 : bool plugin_inited = false;
1069 : :
1070 : 1 : global_instance.is_busy.store(true, std::memory_order_release);
1071 : 1 : impl_->plugin.set_log_level(kOff);
1072 : 1 : impl_->interface = impl_->plugin.load<LoggerPluginInterface>(impl_->plugin_name, 1, 0);
1073 : :
1074 [ - + ]: 1 : if (impl_->interface) {
1075 : 0 : plugin_inited = impl_->interface->init(global_instance.app_name);
1076 : : }
1077 : :
1078 [ - + ]: 1 : if (plugin_inited) {
1079 [ # # # # ]: 0 : static Logger::Impl::PluginFinalizer plugin_finalizer{*this};
1080 : : (void)plugin_finalizer;
1081 : :
1082 : 0 : impl_->is_enable_file_channel.store(true, std::memory_order_release);
1083 : 0 : std::cout << "Successfully loaded plugin for env 'VLINK_LOG_PLUGIN', libname: " << impl_->plugin_name
1084 : 0 : << std::endl;
1085 : :
1086 [ # # ]: 0 : if (kInfo >= global_instance.file_level.load(std::memory_order_acquire)) {
1087 : 0 : write_to_file(kInfo, global_instance.version_log);
1088 : : }
1089 : : } else {
1090 : 1 : impl_->interface.reset();
1091 : 1 : impl_->plugin.clear();
1092 : 1 : std::cerr << "Failed to load plugin for env 'VLINK_LOG_PLUGIN', libname: " << impl_->plugin_name << std::endl;
1093 : : }
1094 : :
1095 : 1 : global_instance.is_busy.store(false, std::memory_order_release);
1096 : 1 : impl_->plugin_init_state.store(Impl::PluginInitState::kComplete, std::memory_order_release);
1097 : : }
1098 : : }
1099 : 106 : }
1100 : :
1101 : 789 : void Logger::write_to_console(Level level, std::string_view log, bool formatted) noexcept {
1102 [ + + + - ]: 789 : static auto& global_instance = LoggerGlobal::get();
1103 : :
1104 [ + + ]: 789 : if VUNLIKELY (global_instance.has_console_callback.load(std::memory_order_acquire)) {
1105 : 49 : std::shared_lock callback_lock(global_instance.callback_mtx);
1106 : :
1107 [ + - ]: 49 : if (global_instance.console_callback) {
1108 : 49 : auto& is_logging = is_logging_on_current_thread();
1109 : 49 : auto& is_in_handler = is_in_handler_on_current_thread();
1110 : 49 : const bool was_logging = is_logging;
1111 : 49 : const bool was_in_handler = is_in_handler;
1112 : :
1113 : 49 : is_logging = true;
1114 : 49 : is_in_handler = true;
1115 : :
1116 : : try {
1117 [ + + ]: 49 : global_instance.console_callback(level, log);
1118 [ + - ]: 1 : } catch (const std::exception& error) {
1119 : 1 : std::cerr << "VLink console logger handler failed: " << error.what() << std::endl;
1120 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1121 : : std::cerr << "VLink console logger handler failed" << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1122 : 0 : }
1123 : :
1124 : 49 : is_in_handler = was_in_handler;
1125 : 49 : is_logging = was_logging;
1126 : :
1127 : 49 : callback_lock.unlock();
1128 : 49 : install_pending_handlers();
1129 : :
1130 : 49 : return;
1131 : : }
1132 [ - + ]: 49 : }
1133 : :
1134 [ + + + + : 740 : if VUNLIKELY (!formatted && global_instance.console_format_enable.load(std::memory_order_acquire)) {
+ + ]
1135 : 84 : std::string fallback_log;
1136 : 84 : std::optional<CachedTimestamp> fallback_timestamp;
1137 : :
1138 [ - + ]: 84 : auto& fmt_log = logger_stream_retired ? fallback_log : get_logger_stream_state().console_buffer;
1139 [ - + ]: 84 : auto& timestamp = logger_stream_retired ? fallback_timestamp.emplace() : get_logger_stream_state().timestamp;
1140 : :
1141 : 84 : fmt_log.clear();
1142 : :
1143 : 84 : auto tid_str = get_thread_id_str();
1144 : :
1145 [ + + ]: 84 : if VUNLIKELY (global_instance.utc_enable.load(std::memory_order_acquire)) {
1146 : 67 : fmt_log.append(timestamp.get("%02d-%02d %02d:%02d:%02d.%03d", true));
1147 : 67 : fmt_log.append(" UTC");
1148 : : } else {
1149 : 17 : fmt_log.append(timestamp.get());
1150 : : }
1151 : :
1152 : 84 : fmt_log.append(" @");
1153 : 84 : fmt_log.append(tid_str);
1154 : 84 : fmt_log.append(" - ");
1155 : 84 : fmt_log.append(get_log_level_str(level).data());
1156 : 84 : fmt_log.append(" - ");
1157 : :
1158 : 84 : fmt_log.append(log);
1159 : :
1160 : 84 : write_to_console_line(level, fmt_log);
1161 : 84 : } else {
1162 : 656 : write_to_console_line(level, log);
1163 : : }
1164 : : }
1165 : :
1166 : 740 : void Logger::write_to_console_line(Level level, std::string_view log) noexcept {
1167 [ + + + - ]: 740 : static auto& global_instance = LoggerGlobal::get();
1168 : 740 : const bool console_in_order = global_instance.console_in_order.load(std::memory_order_acquire);
1169 : :
1170 [ + + + + : 740 : switch (level) {
+ + - - ]
1171 : 2 : case kTrace:
1172 : 2 : print_with_color("", log, "\n", stdout, console_in_order, false);
1173 : 2 : return;
1174 : 4 : case kDebug:
1175 : 4 : print_with_color("", log, "\n", stdout, console_in_order, true);
1176 : 4 : return;
1177 : 160 : case kInfo:
1178 : 160 : print_with_color("\033[32m", log, "\033[0m\n", stdout, console_in_order, true);
1179 : 160 : return;
1180 : 272 : case kWarn:
1181 : 272 : print_with_color("\033[33m", log, "\033[0m\n", stderr, console_in_order, true);
1182 : 272 : return;
1183 : 168 : case kError:
1184 : 168 : print_with_color("\033[31m", log, "\033[0m\n", stderr, console_in_order, true);
1185 : 168 : return;
1186 : 134 : case kFatal:
1187 : 134 : print_with_color("\033[41;37;1m", log, "\033[0m\n", stderr, console_in_order, true);
1188 : 134 : return;
1189 : : // LCOV_EXCL_START GCOVR_EXCL_START
1190 : : case kOff:
1191 : : return;
1192 : : default:
1193 : : return;
1194 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1195 : : }
1196 : : }
1197 : :
1198 : 4917 : void Logger::write_to_file(Level level, std::string_view log) noexcept {
1199 [ + + + - ]: 4917 : static auto& global_instance = LoggerGlobal::get();
1200 : :
1201 [ + + ]: 4917 : if VUNLIKELY (global_instance.has_file_callback.load(std::memory_order_acquire)) {
1202 : 6 : std::shared_lock callback_lock(global_instance.callback_mtx);
1203 : :
1204 [ + - ]: 6 : if (global_instance.file_callback) {
1205 : 6 : auto& is_logging = is_logging_on_current_thread();
1206 : 6 : auto& is_in_handler = is_in_handler_on_current_thread();
1207 : 6 : const bool was_logging = is_logging;
1208 : 6 : const bool was_in_handler = is_in_handler;
1209 : :
1210 : 6 : is_logging = true;
1211 : 6 : is_in_handler = true;
1212 : :
1213 : : try {
1214 [ + - ]: 6 : global_instance.file_callback(level, log);
1215 [ - - ]: 0 : } catch (const std::exception& error) {
1216 : 0 : std::cerr << "VLink file logger handler failed: " << error.what() << std::endl;
1217 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1218 : : std::cerr << "VLink file logger handler failed" << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1219 : 0 : }
1220 : :
1221 : 6 : is_in_handler = was_in_handler;
1222 : 6 : is_logging = was_logging;
1223 : :
1224 : 6 : callback_lock.unlock();
1225 : 6 : install_pending_handlers();
1226 : :
1227 : 6 : return;
1228 : : }
1229 [ - + ]: 6 : }
1230 : :
1231 [ + + ]: 4911 : if VUNLIKELY (!impl_->is_enable_file_channel.load(std::memory_order_acquire)) {
1232 : 1 : return;
1233 : : }
1234 : :
1235 [ - + ]: 4910 : if VUNLIKELY (impl_->interface) {
1236 : 0 : auto& is_plugin_logging = is_plugin_logging_on_current_thread();
1237 : :
1238 [ # # ]: 0 : if VUNLIKELY (is_plugin_logging) {
1239 : 0 : return;
1240 : : }
1241 : :
1242 : 0 : is_plugin_logging = true;
1243 : 0 : impl_->interface->log(level, log);
1244 : 0 : is_plugin_logging = false;
1245 : :
1246 : 0 : return;
1247 : : }
1248 : :
1249 [ + + ]: 4910 : if VUNLIKELY (impl_->disk_emergency.load(std::memory_order_acquire)) {
1250 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1251 : : }
1252 : :
1253 : : #if defined(VLINK_ENABLE_LOG_BACKEND)
1254 [ + - - + : 4908 : if VUNLIKELY (!impl_->backend || !impl_->backend->log(level, log)) {
- + ]
1255 [ # # # # : 0 : if (impl_->backend && impl_->backend->has_error()) {
# # ]
1256 : 0 : impl_->disk_emergency.store(true, std::memory_order_release);
1257 : : }
1258 : : }
1259 : :
1260 : : #elif defined(__ANDROID__)
1261 : :
1262 : : int android_level = ANDROID_LOG_DEBUG;
1263 : : switch (level) {
1264 : : case kTrace:
1265 : : android_level = ANDROID_LOG_VERBOSE;
1266 : : break;
1267 : : case kDebug:
1268 : : android_level = ANDROID_LOG_DEBUG;
1269 : : break;
1270 : : case kInfo:
1271 : : android_level = ANDROID_LOG_INFO;
1272 : : break;
1273 : : case kWarn:
1274 : : android_level = ANDROID_LOG_WARN;
1275 : : break;
1276 : : case kError:
1277 : : android_level = ANDROID_LOG_ERROR;
1278 : : break;
1279 : : case kFatal:
1280 : : android_level = ANDROID_LOG_FATAL;
1281 : : break;
1282 : : case kOff:
1283 : : return;
1284 : : default:
1285 : : return;
1286 : : }
1287 : :
1288 : : __android_log_write(android_level, global_instance.app_name.c_str(), log.data());
1289 : :
1290 : : #elif defined(__QNX__)
1291 : : int platform_level = SLOG2_DEBUG1;
1292 : : switch (level) {
1293 : : case kTrace:
1294 : : platform_level = SLOG2_DEBUG2;
1295 : : break;
1296 : : case kDebug:
1297 : : platform_level = SLOG2_DEBUG1;
1298 : : break;
1299 : : case kInfo:
1300 : : platform_level = SLOG2_INFO;
1301 : : break;
1302 : : case kWarn:
1303 : : platform_level = SLOG2_WARNING;
1304 : : break;
1305 : : case kError:
1306 : : platform_level = SLOG2_ERROR;
1307 : : break;
1308 : : case kFatal:
1309 : : platform_level = SLOG2_CRITICAL;
1310 : : break;
1311 : : case kOff:
1312 : : return;
1313 : : default:
1314 : : return;
1315 : : }
1316 : :
1317 : : slog2c(impl_->slog2_buffer, ::gettid(), platform_level, log.data());
1318 : :
1319 : : #elif defined(__linux__)
1320 : :
1321 : : if (impl_->kmsg_dev.is_open()) {
1322 : : std::lock_guard lock(impl_->file_mtx);
1323 : :
1324 : : switch (level) {
1325 : : case kTrace:
1326 : : impl_->kmsg_dev << "<7>";
1327 : : break;
1328 : : case kDebug:
1329 : : impl_->kmsg_dev << "<6>";
1330 : : break;
1331 : : case kInfo:
1332 : : impl_->kmsg_dev << "<5>";
1333 : : break;
1334 : : case kWarn:
1335 : : impl_->kmsg_dev << "<4>";
1336 : : break;
1337 : : case kError:
1338 : : impl_->kmsg_dev << "<3>";
1339 : : break;
1340 : : case kFatal:
1341 : : impl_->kmsg_dev << "<2>";
1342 : : break;
1343 : : case kOff:
1344 : : return;
1345 : : default:
1346 : : return;
1347 : : }
1348 : :
1349 : : impl_->kmsg_dev << log << std::endl;
1350 : : }
1351 : :
1352 : : #endif
1353 : : }
1354 : :
1355 : : } // namespace vlink
|