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 <shared_mutex>
37 : : #include <string>
38 : : #include <utility>
39 : : #include <vector>
40 : :
41 : : #include "./base/cached_timestamp.h"
42 : : #include "./base/logger_plugin_interface.h"
43 : : #include "./base/utils.h"
44 : : #include "./vlink/version.h"
45 : :
46 : : #if defined(VLINK_ENABLE_LOG_SPD)
47 : :
48 : : #define SPDLOG_LEVEL_NAMES \
49 : : { \
50 : : spdlog::string_view_t("TRACE", 5), spdlog::string_view_t("DEBUG", 5), spdlog::string_view_t("INFO ", 5), \
51 : : spdlog::string_view_t("WARN ", 5), spdlog::string_view_t("ERROR", 5), spdlog::string_view_t("FATAL", 5), \
52 : : spdlog::string_view_t("EMPTY", 5), \
53 : : }
54 : :
55 : : #define SPDLOG_SHORT_LEVEL_NAMES {"T", "D", "I", "W", "E", "F", " "}
56 : :
57 : : #include <spdlog/async.h>
58 : : #include <spdlog/sinks/rotating_file_sink.h>
59 : : #include <spdlog/sinks/stdout_color_sinks.h>
60 : : #include <spdlog/spdlog.h>
61 : :
62 : : #include "./private/spdlog_time_rolling_file_sink.h"
63 : : #elif defined(VLINK_ENABLE_LOG_QUI)
64 : : #include <quill/Backend.h>
65 : : #include <quill/Frontend.h>
66 : : #include <quill/LogMacros.h>
67 : : #include <quill/Logger.h>
68 : : #include <quill/sinks/ConsoleSink.h>
69 : : #include <quill/sinks/RotatingFileSink.h>
70 : : #elif defined(VLINK_ENABLE_LOG_DLT)
71 : : #include <dlt/dlt.h>
72 : : DLT_DECLARE_CONTEXT(dlt_global_ctx_);
73 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__ANDROID__)
74 : : #include <android/log.h>
75 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__QNX__)
76 : : #include <process.h>
77 : : #include <sys/slog2.h>
78 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__linux__)
79 : : #include <linux/kernel.h>
80 : : #define VLINK_KMSG_DEV_PATH "/dev/kmsg"
81 : : #endif
82 : :
83 : : namespace vlink {
84 : :
85 : : [[maybe_unused]] static constexpr size_t kDefaultWriteDepth = 1024L * 8;
86 : : [[maybe_unused]] static constexpr size_t kDefaultLogMaxSize = 1024L * 1024L * 10U;
87 : : [[maybe_unused]] static constexpr size_t kDefaultLogMaxCount = 10U;
88 : : [[maybe_unused]] static constexpr int kDefaultLogFlushDelay = 500;
89 : :
90 : 98 : [[maybe_unused]] static std::string get_current_date(bool use_utc = false) {
91 : 98 : auto now = std::chrono::system_clock::now();
92 : 98 : std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
93 : 98 : std::tm now_tm{};
94 : :
95 : : #if defined(_WIN32)
96 : :
97 : : if (use_utc) {
98 : : gmtime_s(&now_tm, &now_time_t);
99 : : } else {
100 : : localtime_s(&now_tm, &now_time_t);
101 : : }
102 : : #else
103 : :
104 [ + + ]: 98 : if (use_utc) {
105 : 1 : gmtime_r(&now_time_t, &now_tm);
106 : : } else {
107 : 97 : localtime_r(&now_time_t, &now_tm);
108 : : }
109 : : #endif
110 : :
111 : : char buffer[32];
112 : 98 : std::strftime(buffer, sizeof(buffer), "%Y-%m-%d", &now_tm);
113 [ + - ]: 98 : return std::string(buffer);
114 : : }
115 : :
116 : 29 : [[maybe_unused]] static std::string_view get_current_time(bool use_utc = false) {
117 [ + + ]: 29 : thread_local CachedTimestamp cache;
118 : 29 : return cache.get("%02d-%02d %02d:%02d:%02d.%03d", use_utc);
119 : : }
120 : :
121 : 29 : [[maybe_unused]] static constexpr std::string_view get_log_level_str(Logger::Level level) {
122 [ + + + + : 29 : switch (level) {
+ + - - ]
123 : 2 : case Logger::kTrace:
124 : 2 : return "TRACE";
125 : 6 : case Logger::kDebug:
126 : 6 : return "DEBUG";
127 : 6 : case Logger::kInfo:
128 : 6 : return "INFO ";
129 : 6 : case Logger::kWarn:
130 : 6 : return "WARN ";
131 : 6 : case Logger::kError:
132 : 6 : return "ERROR";
133 : 3 : case Logger::kFatal:
134 : 3 : return "FATAL";
135 : : // LCOV_EXCL_START GCOVR_EXCL_START
136 : : case Logger::kOff:
137 : : return "EMPTY";
138 : : default:
139 : : return "EMPTY";
140 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
141 : : }
142 : : }
143 : :
144 : 324 : [[maybe_unused]] static int get_log_level(const std::string& key) {
145 [ + - ]: 648 : std::string str_value = Utils::get_env(key);
146 : :
147 [ + + ]: 324 : if (str_value.empty()) {
148 : 296 : return -1;
149 : : }
150 : :
151 [ + + + - : 28 : if (str_value == "Trace" || str_value == "TRACE" || str_value == "trace") {
- + + + ]
152 : 4 : return Logger::kTrace;
153 [ + - + + : 24 : } else if (str_value == "Debug" || str_value == "DEBUG" || str_value == "debug") {
- + + + ]
154 : 1 : return Logger::kDebug;
155 [ + + + - : 23 : } else if (str_value == "Info" || str_value == "INFO" || str_value == "info") {
+ + + + ]
156 : 4 : return Logger::kInfo;
157 [ + + + - : 19 : } else if (str_value == "Warn" || str_value == "WARN" || str_value == "warn") {
- + + + ]
158 : 1 : return Logger::kWarn;
159 [ + - + + : 18 : } else if (str_value == "Error" || str_value == "ERROR" || str_value == "error") {
- + + + ]
160 : 1 : return Logger::kError;
161 [ + - + - : 17 : } else if (str_value == "Fatal" || str_value == "FATAL" || str_value == "fatal") {
+ + + + ]
162 : 1 : return Logger::kFatal;
163 [ + + + - : 16 : } else if (str_value == "Off" || str_value == "OFF" || str_value == "off") {
- + + + ]
164 : 13 : return Logger::kOff;
165 : : }
166 : :
167 : 3 : int value = -1;
168 : :
169 [ + - ]: 3 : auto [p, error] = std::from_chars(str_value.data(), str_value.data() + str_value.size(), value);
170 : :
171 [ + + ]: 3 : if VUNLIKELY (error != std::errc()) {
172 : 1 : return -1;
173 : : }
174 : :
175 [ + - + + : 2 : if VUNLIKELY (value < 0 || value > Logger::kOff) {
+ + ]
176 : 1 : return Logger::kOff;
177 : : }
178 : :
179 : 1 : return value;
180 : 324 : }
181 : :
182 : 29 : [[maybe_unused]] static std::string_view get_thread_id_str() {
183 : : thread_local char buffer[32];
184 : : thread_local bool initialized = false;
185 : :
186 [ + + ]: 29 : if VUNLIKELY (!initialized) {
187 [ + - ]: 2 : auto [p, ec] = std::to_chars(buffer, buffer + sizeof(buffer) - 1, Utils::get_native_thread_id());
188 : :
189 [ + - ]: 2 : if (ec == std::errc()) {
190 : 2 : *p = '\0';
191 : : } else {
192 : : std::snprintf( // LCOV_EXCL_LINE GCOVR_EXCL_LINE
193 : : buffer, sizeof(buffer), "%llu",
194 : 0 : static_cast<unsigned long long>(Utils::get_native_thread_id())); // NOLINT(runtime/int, google-runtime-int)
195 : : // // LCOV_EXCL_LINE GCOVR_EXCL_LINE
196 : : }
197 : :
198 : 2 : initialized = true;
199 : : }
200 : :
201 : 29 : return buffer;
202 : : }
203 : :
204 : 623 : [[maybe_unused]] static std::mutex& get_print_mtx() {
205 : : static std::mutex print_mtx;
206 : 623 : return print_mtx;
207 : : }
208 : :
209 : : template <uint8_t PrefixSizeT, uint8_t SuffixSizeT>
210 : 623 : [[maybe_unused]] static void print_with_color(const char (&prefix)[PrefixSizeT], std::string_view log,
211 : : const char (&suffix)[SuffixSizeT], FILE* file, bool in_order,
212 : : bool force_flush) {
213 : 623 : std::unique_lock lock(get_print_mtx(), std::defer_lock);
214 : :
215 [ + + ]: 623 : if (in_order) {
216 [ + - ]: 617 : lock.lock();
217 : : }
218 : :
219 : : if constexpr (PrefixSizeT > 1) {
220 [ + - ]: 613 : std::fwrite(prefix, sizeof(char), PrefixSizeT - 1, file);
221 : : }
222 : :
223 [ + - ]: 623 : if VLIKELY (!log.empty()) {
224 [ + - ]: 623 : std::fwrite(log.data(), sizeof(char), log.size(), file);
225 : : }
226 : :
227 : : if constexpr (SuffixSizeT > 1) {
228 [ + - ]: 623 : std::fwrite(suffix, sizeof(char), SuffixSizeT - 1, file);
229 : : }
230 : :
231 [ + + + + ]: 623 : if (in_order && force_flush) {
232 [ + - ]: 615 : std::fflush(file);
233 : : }
234 : 623 : }
235 : :
236 : : // LoggerGlobal
237 : : struct LoggerGlobal final { // NOLINT(clang-analyzer-optin.performance.Padding)
238 : : std::atomic_bool is_busy{false};
239 : :
240 : : std::string app_name;
241 : : std::string log_path;
242 : : std::string version_log;
243 : : std::atomic<int> console_level{Logger::kDebug};
244 : : std::atomic<int> file_level{Logger::kDebug};
245 : : std::atomic_bool console_in_order{true};
246 : : std::atomic_bool console_level_by_user{false};
247 : : std::atomic_bool file_level_by_user{false};
248 : : std::atomic_bool console_format_enable{false};
249 : : std::atomic_bool utc_enable{false};
250 : : // Protected by callback_mtx (shared on read/invoke, exclusive on register).
251 : : Logger::Callback console_callback;
252 : : Logger::Callback file_callback;
253 : : mutable std::shared_mutex callback_mtx;
254 : : std::atomic<std::ios_base::fmtflags> stream_flags{std::ios_base::dec | std::ios_base::skipws};
255 : : std::atomic<int> stream_precision{6};
256 : : std::atomic<int> stream_width{0};
257 : :
258 : 575 : static LoggerGlobal& get() {
259 [ + + + - ]: 575 : static LoggerGlobal instance;
260 : 575 : return instance;
261 : : }
262 : :
263 : : private:
264 : 112 : LoggerGlobal() = default;
265 : : };
266 : :
267 : : // Logger::Impl
268 : : struct Logger::Impl final { // NOLINT(clang-analyzer-optin.performance.Padding)
269 : : std::atomic_bool disk_emergency{false};
270 : : std::atomic_bool is_enable_backtrace{false};
271 : :
272 : : bool is_enable_file_channel{false};
273 : : Plugin plugin;
274 : : std::shared_ptr<LoggerPluginInterface> interface;
275 : :
276 : : #if defined(VLINK_ENABLE_LOG_SPD)
277 : : std::shared_ptr<spdlog::logger> spd;
278 : : std::shared_ptr<spdlog::sinks::sink> spd_console_sink;
279 : : std::shared_ptr<spdlog::sinks::sink> spd_file_sink;
280 : : std::shared_ptr<spdlog::details::thread_pool> spd_thread_pool;
281 : : #elif defined(VLINK_ENABLE_LOG_QUI)
282 : : quill::Logger* quill_log{nullptr};
283 : : std::shared_ptr<quill::StreamSink> quill_console_sink;
284 : : std::shared_ptr<quill::StreamSink> quill_file_sink;
285 : : quill::MacroMetadata quill_metadata_trace{
286 : : "", "", "{}", nullptr, quill::LogLevel::TraceL1, quill::MacroMetadata::Event::Log};
287 : : quill::MacroMetadata quill_metadata_debug{
288 : : "", "", "{}", nullptr, quill::LogLevel::Debug, quill::MacroMetadata::Event::Log};
289 : : quill::MacroMetadata quill_metadata_info{
290 : : "", "", "{}", nullptr, quill::LogLevel::Info, quill::MacroMetadata::Event::Log};
291 : : quill::MacroMetadata quill_metadata_warn{
292 : : "", "", "{}", nullptr, quill::LogLevel::Warning, quill::MacroMetadata::Event::Log};
293 : : quill::MacroMetadata quill_metadata_error{
294 : : "", "", "{}", nullptr, quill::LogLevel::Error, quill::MacroMetadata::Event::Log};
295 : : quill::MacroMetadata quill_metadata_fatal{
296 : : "", "", "{}", nullptr, quill::LogLevel::Critical, quill::MacroMetadata::Event::Log};
297 : : quill::MacroMetadata quill_metadata_backtrace{
298 : : "", "", "{}", nullptr, quill::LogLevel::Backtrace, quill::MacroMetadata::Event::Log};
299 : : #elif defined(VLINK_ENABLE_LOG_DLT)
300 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__ANDROID__)
301 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__QNX__)
302 : : slog2_buffer_t slog2_buffer{nullptr};
303 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__linux__)
304 : : std::ofstream kmsg_dev;
305 : : std::mutex file_mtx;
306 : : #endif
307 : : };
308 : :
309 : : // Logger
310 : 45 : void Logger::init(const std::string& app_name, const std::string& log_path) noexcept {
311 [ + + + - ]: 45 : static auto& global_instance = LoggerGlobal::get();
312 : :
313 [ + - ]: 45 : if (!app_name.empty()) {
314 : 45 : global_instance.app_name = app_name;
315 [ + + ]: 45 : if (!log_path.empty()) {
316 : 2 : global_instance.log_path = log_path;
317 : : }
318 : : }
319 : :
320 : 45 : Logger::get();
321 : 45 : }
322 : :
323 : 2015 : Logger& Logger::get() noexcept {
324 [ + + + - ]: 2015 : static Logger instance;
325 : :
326 : 2015 : return instance;
327 : : }
328 : :
329 : 145 : void Logger::flush() noexcept {
330 [ + + + - ]: 145 : static Logger& instance = Logger::get();
331 : :
332 [ + + ]: 145 : if (!instance.impl_->is_enable_file_channel) {
333 : 1 : return;
334 : : }
335 : :
336 [ + + ]: 144 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
337 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
338 : : }
339 : :
340 : : try {
341 : : #if defined(VLINK_ENABLE_LOG_SPD)
342 [ + - ]: 143 : instance.impl_->spd->flush();
343 : : #elif defined(VLINK_ENABLE_LOG_QUI)
344 : : quill::Backend::notify();
345 : : instance.impl_->quill_file_sink->flush_sink();
346 : : #endif
347 : : // LCOV_EXCL_START GCOVR_EXCL_START
348 : : } catch (std::exception& e) {
349 : : instance.impl_->disk_emergency.store(true, std::memory_order_release);
350 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl;
351 : : }
352 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
353 : : }
354 : :
355 : 19 : void Logger::register_console_handler(Callback&& callback) noexcept {
356 : 19 : auto& global_instance = LoggerGlobal::get();
357 : :
358 : 19 : std::unique_lock lock(global_instance.callback_mtx);
359 : :
360 : 19 : global_instance.console_callback = std::move(callback);
361 : 19 : }
362 : :
363 : 2 : void Logger::register_file_handler(Callback&& callback) noexcept {
364 : 2 : auto& global_instance = LoggerGlobal::get();
365 : :
366 : 2 : std::unique_lock lock(global_instance.callback_mtx);
367 : :
368 : 2 : global_instance.file_callback = std::move(callback);
369 : 2 : }
370 : :
371 : 36 : void Logger::set_console_level(Level level) noexcept {
372 : 36 : LoggerGlobal::get().console_level.store(level, std::memory_order_release);
373 : 36 : LoggerGlobal::get().console_level_by_user.store(true, std::memory_order_release);
374 : 36 : }
375 : :
376 : 13 : void Logger::set_file_level(Level level) noexcept {
377 : 13 : LoggerGlobal::get().file_level.store(level, std::memory_order_release);
378 : 13 : LoggerGlobal::get().file_level_by_user.store(true, std::memory_order_release);
379 : 13 : }
380 : :
381 : 4 : void Logger::set_console_fmt_enable(bool enable) noexcept {
382 : 4 : LoggerGlobal::get().console_format_enable.store(enable, std::memory_order_release);
383 : 4 : }
384 : :
385 : 15 : Logger::Level Logger::get_console_level() noexcept {
386 : 30 : return static_cast<Logger::Level>(LoggerGlobal::get().console_level.load(std::memory_order_acquire));
387 : : }
388 : :
389 : 6 : Logger::Level Logger::get_file_level() noexcept {
390 : 12 : return static_cast<Logger::Level>(LoggerGlobal::get().file_level.load(std::memory_order_acquire));
391 : : }
392 : :
393 : 3 : bool Logger::get_console_fmt_enable() noexcept {
394 : 3 : return LoggerGlobal::get().console_format_enable.load(std::memory_order_acquire);
395 : : }
396 : :
397 : 2 : void Logger::set_stream_flag(std::ios_base::fmtflags flags) noexcept {
398 : 2 : LoggerGlobal::get().stream_flags.store(flags, std::memory_order_release);
399 : 2 : }
400 : :
401 : 2 : void Logger::set_stream_precision(int precision) noexcept {
402 : 2 : LoggerGlobal::get().stream_precision.store(precision, std::memory_order_release);
403 : 2 : }
404 : :
405 : 2 : void Logger::set_stream_width(int width) noexcept {
406 : 2 : LoggerGlobal::get().stream_width.store(width, std::memory_order_release);
407 : 2 : }
408 : :
409 : 2 : std::ios_base::fmtflags Logger::get_stream_flag() noexcept {
410 : 2 : return LoggerGlobal::get().stream_flags.load(std::memory_order_acquire);
411 : : }
412 : :
413 : 2 : int Logger::get_stream_precision() noexcept {
414 : 4 : return LoggerGlobal::get().stream_precision.load(std::memory_order_acquire);
415 : : }
416 : :
417 : 4 : int Logger::get_stream_width() noexcept { return LoggerGlobal::get().stream_width.load(std::memory_order_acquire); }
418 : :
419 : 3 : void Logger::enable_backtrace(size_t size) noexcept {
420 [ + + + - ]: 3 : static Logger& instance = Logger::get();
421 : :
422 [ - + ]: 3 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
423 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
424 : : }
425 : :
426 [ - + ]: 3 : if VUNLIKELY (!instance.impl_->is_enable_file_channel) {
427 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
428 : : }
429 : :
430 [ - + ]: 3 : if VUNLIKELY (instance.impl_->interface) {
431 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
432 : : }
433 : :
434 : : #if defined(VLINK_ENABLE_LOG_SPD) || defined(VLINK_ENABLE_LOG_QUI)
435 : 3 : bool expected = false;
436 : :
437 [ - + ]: 3 : if (!instance.impl_->is_enable_backtrace.compare_exchange_strong(
438 : : expected, true, std::memory_order_acq_rel, // LCOV_EXCL_LINE GCOVR_EXCL_LINE
439 : : std::memory_order_relaxed)) {
440 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
441 : : }
442 : :
443 : : #if defined(VLINK_ENABLE_LOG_SPD)
444 : 3 : instance.impl_->spd_console_sink->set_level(spdlog::level::trace);
445 : 3 : instance.impl_->spd->set_level(spdlog::level::warn);
446 : 3 : instance.impl_->spd->enable_backtrace(size);
447 : : #elif defined(VLINK_ENABLE_LOG_QUI)
448 : : instance.impl_->quill_log->init_backtrace(size, quill::LogLevel::TraceL3);
449 : : #endif
450 : : #else
451 : : (void)size;
452 : : #endif
453 : : }
454 : :
455 : 3 : void Logger::disable_backtrace() noexcept {
456 [ + + + - ]: 3 : static Logger& instance = Logger::get();
457 : :
458 [ - + ]: 3 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
459 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
460 : : }
461 : :
462 : 3 : bool expected = true;
463 : :
464 [ - + ]: 3 : if (!instance.impl_->is_enable_backtrace.compare_exchange_strong(
465 : : expected, false, std::memory_order_acq_rel, // LCOV_EXCL_LINE GCOVR_EXCL_LINE
466 : : std::memory_order_relaxed)) {
467 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
468 : : }
469 : :
470 [ - + ]: 3 : if VUNLIKELY (!instance.impl_->is_enable_file_channel) {
471 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
472 : : }
473 : :
474 [ - + ]: 3 : if VUNLIKELY (instance.impl_->interface) {
475 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
476 : : }
477 : :
478 : : #if defined(VLINK_ENABLE_LOG_SPD)
479 : 3 : instance.impl_->spd->disable_backtrace();
480 : 3 : instance.impl_->spd_console_sink->set_level(spdlog::level::off);
481 : 3 : instance.impl_->spd->set_level(spdlog::level::trace);
482 : : #elif defined(VLINK_ENABLE_LOG_QUI)
483 : : instance.impl_->quill_log->init_backtrace(0, quill::LogLevel::None);
484 : : #else
485 : : (void)instance;
486 : : #endif
487 : : }
488 : :
489 : 2 : void Logger::dump_backtrace() noexcept {
490 [ + - + - ]: 2 : static Logger& instance = Logger::get();
491 : :
492 [ - + ]: 2 : if VUNLIKELY (instance.impl_->disk_emergency.load(std::memory_order_acquire)) {
493 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
494 : : }
495 : :
496 [ - + ]: 2 : if (!instance.impl_->is_enable_backtrace.load(std::memory_order_acquire)) {
497 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
498 : : }
499 : :
500 [ - + ]: 2 : if VUNLIKELY (!instance.impl_->is_enable_file_channel) {
501 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
502 : : }
503 : :
504 [ - + ]: 2 : if VUNLIKELY (instance.impl_->interface) {
505 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
506 : : }
507 : :
508 : : #if defined(VLINK_ENABLE_LOG_SPD)
509 : 2 : instance.impl_->spd->dump_backtrace();
510 : 2 : instance.impl_->spd_console_sink->flush();
511 : 2 : std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait for dump to console
512 : : #elif defined(VLINK_ENABLE_LOG_QUI)
513 : : instance.impl_->quill_log->flush_backtrace();
514 : : #else
515 : : (void)instance;
516 : : #endif
517 : : }
518 : :
519 : 1 : bool Logger::is_busy() noexcept { return LoggerGlobal::get().is_busy.load(std::memory_order_acquire); }
520 : :
521 : 46503 : bool Logger::is_writable(Level level) noexcept {
522 [ + + + - ]: 46503 : static auto& global_instance = LoggerGlobal::get();
523 : :
524 [ + + ]: 93165 : return level >= global_instance.console_level.load(std::memory_order_acquire) ||
525 [ + + ]: 46821 : level >= global_instance.file_level.load(std::memory_order_acquire);
526 : : }
527 : :
528 : 45834 : bool Logger::try_acquire_periodic_log(Level level, int64_t interval_ms,
529 : : std::atomic<uint64_t>& last_log_time_ns) noexcept {
530 [ + + ]: 45834 : if VUNLIKELY (level >= kFatal) {
531 : 1 : return false;
532 : : }
533 : :
534 [ + + ]: 45833 : if (!is_writable(level)) {
535 : 1 : return false;
536 : : }
537 : :
538 [ + + ]: 45832 : if VUNLIKELY (interval_ms <= 0) {
539 : 3 : return true;
540 : : }
541 : :
542 : 45829 : constexpr uint64_t kNanosecondsPerMillisecond = 1000U * 1000U;
543 : 45829 : const auto unsigned_interval_ms = static_cast<uint64_t>(interval_ms);
544 : 45829 : const auto interval_ns = unsigned_interval_ms > std::numeric_limits<uint64_t>::max() / kNanosecondsPerMillisecond
545 [ - + ]: 45829 : ? std::numeric_limits<uint64_t>::max()
546 : 45829 : : unsigned_interval_ms * kNanosecondsPerMillisecond;
547 [ + + + - ]: 45829 : static const auto kStartTime = std::chrono::steady_clock::now();
548 : : const auto now_ns =
549 : 45829 : static_cast<uint64_t>(
550 : 45829 : std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - kStartTime).count()) +
551 : 45829 : 1U;
552 : 91658 : auto last_ns = last_log_time_ns.load(std::memory_order_relaxed);
553 : :
554 : : for (;;) {
555 [ + + ]: 45829 : if VLIKELY (last_ns != 0U) {
556 [ + + ]: 45819 : if VUNLIKELY (last_ns >= now_ns) {
557 : 1 : return false;
558 : : }
559 : :
560 [ + + ]: 45818 : if VLIKELY (now_ns - last_ns < interval_ns) {
561 : 45817 : return false;
562 : : }
563 : : }
564 : :
565 [ + - ]: 22 : if VLIKELY (last_log_time_ns.compare_exchange_weak(last_ns, now_ns, std::memory_order_relaxed,
566 : : std::memory_order_relaxed)) {
567 : 11 : return true;
568 : : }
569 : : }
570 : : }
571 : :
572 : 108 : Logger::Logger() noexcept : impl_(std::make_unique<Impl>()) {
573 [ + - + - ]: 108 : static auto& global_instance = LoggerGlobal::get();
574 : :
575 : 108 : global_instance.is_busy.store(true, std::memory_order_release);
576 : :
577 : 108 : int common_level = get_log_level("VLINK_LOG_LEVEL");
578 : :
579 [ + - ]: 108 : if (!global_instance.console_level_by_user.load(std::memory_order_acquire)) {
580 : 108 : int console_level = get_log_level("VLINK_LOG_CONSOLE_LEVEL");
581 : :
582 [ + + ]: 108 : if (console_level >= 0) {
583 : 13 : global_instance.console_level.store(console_level, std::memory_order_release);
584 [ + + ]: 95 : } else if (common_level >= 0) {
585 : 1 : global_instance.console_level.store(common_level, std::memory_order_release);
586 : : }
587 : : }
588 : :
589 [ + - ]: 108 : if (!global_instance.file_level_by_user.load(std::memory_order_acquire)) {
590 : 108 : int file_level = get_log_level("VLINK_LOG_FILE_LEVEL");
591 : :
592 [ + + ]: 108 : if (file_level >= 0) {
593 : 12 : global_instance.file_level.store(file_level, std::memory_order_release);
594 [ + + ]: 96 : } else if (common_level >= 0) {
595 : 2 : global_instance.file_level.store(common_level, std::memory_order_release);
596 : : }
597 : : }
598 : :
599 [ + + + + ]: 221 : if (global_instance.console_level.load(std::memory_order_acquire) < kOff ||
600 [ + + ]: 10 : global_instance.file_level.load(std::memory_order_acquire) < kOff) {
601 : 210 : std::string enable_console_unorder = Utils::get_env("VLINK_LOG_CONSOLE_UNORDER");
602 : 105 : global_instance.console_in_order.store(enable_console_unorder != "1", std::memory_order_release);
603 : :
604 : 210 : std::string enable_utc_str = Utils::get_env("VLINK_LOG_ENABLE_UTC");
605 : 105 : global_instance.utc_enable.store(enable_utc_str == "1", std::memory_order_release);
606 : :
607 [ + + ]: 105 : if (global_instance.app_name.empty()) {
608 : 103 : global_instance.app_name = Utils::get_app_name();
609 : :
610 [ - + ]: 103 : if VUNLIKELY (global_instance.app_name.empty()) {
611 : : global_instance.app_name = "unknown"; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
612 : : std::cerr << "Can not get app name for logger!" << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
613 : : }
614 : : }
615 : :
616 : 210 : std::string console_format = Utils::get_env("VLINK_LOG_CONSOLE_FMT");
617 : 105 : global_instance.console_format_enable.store(console_format == "1", std::memory_order_release);
618 : 105 : }
619 : :
620 [ + + ]: 216 : if (global_instance.file_level.load(std::memory_order_acquire) < kOff) {
621 : 98 : global_instance.version_log.reserve(128);
622 : :
623 : 98 : global_instance.version_log.append("***** ");
624 : 98 : global_instance.version_log.append("[PNAME: ");
625 : 98 : global_instance.version_log.append(global_instance.app_name);
626 : 98 : global_instance.version_log.append("] ");
627 : :
628 : 98 : global_instance.version_log.append("[PID: ");
629 : 98 : global_instance.version_log.append(Utils::get_pid_str());
630 : 98 : global_instance.version_log.append("] ");
631 : :
632 [ + + ]: 98 : if (global_instance.utc_enable.load(std::memory_order_acquire)) {
633 : 1 : global_instance.version_log.append("[DATE (UTC): ");
634 : 1 : global_instance.version_log.append(get_current_date(true));
635 : 1 : global_instance.version_log.append("] ");
636 : : } else {
637 : 97 : global_instance.version_log.append("[DATE: ");
638 : 97 : global_instance.version_log.append(get_current_date(false));
639 : 97 : global_instance.version_log.append("] ");
640 : : }
641 : :
642 : 98 : global_instance.version_log.append("[VERSION: ");
643 : 98 : global_instance.version_log.append(VLINK_VERSION);
644 : 98 : global_instance.version_log.append("] ");
645 : 98 : global_instance.version_log.append("*****");
646 : :
647 : 98 : impl_->is_enable_file_channel = true;
648 : :
649 : 196 : std::string plugin_name = Utils::get_env("VLINK_LOG_PLUGIN");
650 : :
651 [ + + ]: 98 : if (!plugin_name.empty()) {
652 : : // Use plugin
653 : 1 : impl_->plugin.set_log_level(kOff); // Must set!
654 : :
655 : 1 : impl_->interface = impl_->plugin.load<LoggerPluginInterface>(plugin_name, 1, 0);
656 : :
657 [ - + ]: 1 : if (impl_->interface) {
658 : : // LCOV_EXCL_START GCOVR_EXCL_START
659 : : bool plugin_inited = false;
660 : :
661 : : try {
662 : : plugin_inited = impl_->interface->init(global_instance.app_name);
663 : : } catch (const std::exception& e) {
664 : : std::cerr << "Failed to init plugin for env 'VLINK_LOG_PLUGIN', libname: " << plugin_name << ": " << e.what()
665 : : << std::endl;
666 : : } catch (...) {
667 : : std::cerr << "Failed to init plugin for env 'VLINK_LOG_PLUGIN', libname: " << plugin_name
668 : : << ": non-std exception" << std::endl;
669 : : }
670 : :
671 : : if (plugin_inited) {
672 : : std::cout << "Successfully loaded plugin for env 'VLINK_LOG_PLUGIN', libname: " << plugin_name << std::endl;
673 : :
674 : : write_to_file(kInfo, global_instance.version_log);
675 : : } else {
676 : : impl_->interface.reset();
677 : : impl_->plugin.clear();
678 : : impl_->is_enable_file_channel = false;
679 : : std::cerr << "Failed to load plugin for env 'VLINK_LOG_PLUGIN', libname: " << plugin_name << std::endl;
680 : : }
681 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
682 : : } else {
683 : 1 : impl_->is_enable_file_channel = false;
684 : 1 : std::cerr << "Failed to load plugin for env 'VLINK_LOG_PLUGIN', libname: " << plugin_name << std::endl;
685 : : }
686 : :
687 : 1 : global_instance.is_busy.store(false, std::memory_order_release);
688 : :
689 : 1 : return;
690 : : }
691 : :
692 [ + + ]: 97 : if (global_instance.log_path.empty()) {
693 : 192 : std::string log_dir = Utils::get_env("VLINK_LOG_DIR");
694 : :
695 [ + + ]: 96 : if (log_dir.empty()) {
696 : 94 : log_dir = Utils::get_tmp_dir() + "/" + "vlink-log";
697 [ + + ]: 2 : } else if (log_dir.back() == '/') {
698 : 1 : log_dir.pop_back();
699 : : }
700 : :
701 : : try {
702 [ + - + - : 96 : if (!std::filesystem::exists(log_dir)) {
+ + ]
703 [ + - + - ]: 1 : std::filesystem::create_directories(log_dir);
704 : : }
705 : : } catch (std::exception&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
706 : : log_dir = Utils::get_tmp_dir(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
707 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
708 : :
709 : 96 : global_instance.log_path = log_dir + "/" + global_instance.app_name;
710 : 96 : }
711 : :
712 : 97 : size_t log_max_size = kDefaultLogMaxSize;
713 : 97 : size_t log_max_count = kDefaultLogMaxCount;
714 : 97 : int log_flush_delay_ms = kDefaultLogFlushDelay;
715 : :
716 : : {
717 : 194 : std::string log_max_size_str = Utils::get_env("VLINK_LOG_MAX_SIZE");
718 : 194 : std::string log_max_count_str = Utils::get_env("VLINK_LOG_MAX_COUNT");
719 : 194 : std::string log_flush_delay_str = Utils::get_env("VLINK_LOG_FLUSH_DELAY");
720 : :
721 [ + + ]: 97 : if (!log_max_size_str.empty()) {
722 : 2 : std::from_chars(log_max_size_str.data(), log_max_size_str.data() + log_max_size_str.size(), log_max_size);
723 : : }
724 : :
725 [ + + ]: 97 : if (!log_max_count_str.empty()) {
726 : 2 : std::from_chars(log_max_count_str.data(), log_max_count_str.data() + log_max_count_str.size(), log_max_count);
727 : : }
728 : :
729 [ + + ]: 97 : if (!log_flush_delay_str.empty()) {
730 : 2 : std::from_chars(log_flush_delay_str.data(), log_flush_delay_str.data() + log_flush_delay_str.size(),
731 : : log_flush_delay_ms);
732 : : }
733 : 97 : }
734 : :
735 : : #if defined(VLINK_ENABLE_LOG_SPD)
736 : 194 : std::string log_strategy = Utils::get_env("VLINK_LOG_STORE_STRATEGY");
737 : 194 : std::string log_append = Utils::get_env("VLINK_LOG_OPEN_APPEND");
738 : 194 : std::string log_block = Utils::get_env("VLINK_LOG_BLOCK_SYNC");
739 : 194 : std::string log_depth = Utils::get_env("VLINK_LOG_WRITE_DEPTH");
740 : :
741 : 97 : bool use_log_strategy = (log_strategy == "1");
742 : 97 : bool use_log_append = (log_append == "1");
743 : 97 : bool use_log_block = (log_block == "1");
744 : 97 : int log_write_depth = kDefaultWriteDepth;
745 : :
746 [ + + ]: 97 : if (!log_depth.empty()) {
747 : 2 : std::from_chars(log_depth.data(), log_depth.data() + log_depth.size(), log_write_depth);
748 : : }
749 : :
750 : 97 : impl_->spd_thread_pool = std::make_shared<spdlog::details::thread_pool>(log_write_depth, 1);
751 : :
752 : 97 : impl_->spd_console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
753 : 97 : impl_->spd_console_sink->set_level(spdlog::level::off);
754 : :
755 [ + + ]: 97 : if (use_log_strategy) {
756 : 4 : std::string log_path = global_instance.log_path + "/" + global_instance.app_name + ".log";
757 : :
758 : : try {
759 : 2 : impl_->spd_file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_path, log_max_size,
760 [ + + ]: 4 : log_max_count, !use_log_append);
761 : : } catch (std::exception& e) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
762 : : impl_->disk_emergency.store(true, std::memory_order_release); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
763 : :
764 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
765 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
766 : :
767 : 2 : } else {
768 : 95 : spdlog_custom_sink::TimeZone timezone = global_instance.utc_enable.load(std::memory_order_acquire)
769 [ + + ]: 95 : ? spdlog_custom_sink::TimeZone::kTimezoneUtc
770 : 95 : : spdlog_custom_sink::TimeZone::kTimezoneLocal;
771 : :
772 : : try {
773 : 190 : impl_->spd_file_sink = std::make_shared<spdlog_custom_sink::TimeRollingFile_mt>(
774 [ + - ]: 285 : global_instance.log_path, log_max_size, log_max_count, timezone, !use_log_append);
775 : : } catch (std::exception& e) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
776 : : impl_->disk_emergency.store(true, std::memory_order_release); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
777 : :
778 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
779 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
780 : : }
781 : :
782 : 97 : std::vector<spdlog::sink_ptr> spd_sinks;
783 : 97 : spd_sinks.emplace_back(impl_->spd_console_sink);
784 : :
785 [ + + ]: 97 : if (impl_->spd_file_sink) {
786 : 96 : spd_sinks.emplace_back(impl_->spd_file_sink);
787 : : }
788 : :
789 : 194 : impl_->spd = std::make_shared<spdlog::async_logger>(
790 : 97 : global_instance.app_name, spd_sinks.begin(), spd_sinks.end(), impl_->spd_thread_pool,
791 [ + + ]: 194 : use_log_block ? spdlog::async_overflow_policy::block : spdlog::async_overflow_policy::overrun_oldest);
792 : :
793 : 97 : spdlog::register_logger(impl_->spd);
794 : :
795 : 97 : impl_->spd->set_error_handler([this](const std::string& msg) {
796 : : impl_->disk_emergency.store(true, std::memory_order_release); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
797 : : std::cerr << "VLink logger disk emergency: " << msg << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
798 : : }); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
799 : :
800 : 97 : spdlog::set_default_logger(impl_->spd);
801 : :
802 [ + + ]: 97 : if (global_instance.utc_enable.load(std::memory_order_acquire)) {
803 : 1 : impl_->spd->set_pattern("%m-%d %H:%M:%S.%e UTC @%t - %l - %v", spdlog::pattern_time_type::utc);
804 : : } else {
805 : 96 : impl_->spd->set_pattern("%m-%d %H:%M:%S.%e @%t - %l - %v", spdlog::pattern_time_type::local);
806 : : }
807 : :
808 : 97 : impl_->spd->set_level(spdlog::level::level_enum::trace);
809 : :
810 [ + + ]: 97 : if (log_flush_delay_ms > 0) {
811 : 96 : impl_->spd->flush_on(spdlog::level::level_enum::err);
812 : 96 : spdlog::flush_every(std::chrono::milliseconds(log_flush_delay_ms));
813 : :
814 : : } else {
815 : 1 : impl_->spd->flush_on(spdlog::level::level_enum::trace);
816 : : }
817 : :
818 : : #elif defined(VLINK_ENABLE_LOG_QUI)
819 : : std::string log_append = Utils::get_env("VLINK_LOG_OPEN_APPEND");
820 : : std::string log_depth = Utils::get_env("VLINK_LOG_WRITE_DEPTH");
821 : :
822 : : bool use_log_append = (log_append == "1");
823 : : int log_write_depth = kDefaultWriteDepth;
824 : :
825 : : if (!log_depth.empty()) {
826 : : std::from_chars(log_depth.data(), log_depth.data() + log_depth.size(), log_write_depth);
827 : : }
828 : :
829 : : std::string log_path = global_instance.log_path + "/" + global_instance.app_name + ".log";
830 : :
831 : : quill::BackendOptions backend_options;
832 : :
833 : : // backend_options.cpu_affinity=0;
834 : : backend_options.sleep_duration =
835 : : log_flush_delay_ms > 0 ? std::chrono::milliseconds(log_flush_delay_ms) : std::chrono::milliseconds(10);
836 : : backend_options.transit_event_buffer_initial_capacity = 1024U;
837 : : backend_options.transit_events_soft_limit = 8192U;
838 : : backend_options.wait_for_queues_to_empty_before_exit = true;
839 : : backend_options.log_level_descriptions = {"TRACE", "TRACE", "TRACE", "DEBUG", "INFO", "NOTICE",
840 : : "WARN", "ERROR", "FATAL", "BACKTRACE", "EMPTY"};
841 : : backend_options.log_level_short_codes = {"T", "T", "T", "D", "I", "N", "W", "E", "F", "BT", " "};
842 : :
843 : : backend_options.error_notifier = [this](std::string const& msg) {
844 : : impl_->disk_emergency.store(true, std::memory_order_release);
845 : :
846 : : std::cerr << "VLink logger disk emergency: " << msg << std::endl;
847 : : };
848 : :
849 : : quill::Backend::start(backend_options);
850 : :
851 : : quill::RotatingFileSinkConfig sink_config;
852 : :
853 : : sink_config.set_open_mode('a');
854 : : sink_config.set_write_buffer_size(log_write_depth);
855 : : sink_config.set_rotation_max_file_size(log_max_size);
856 : : sink_config.set_max_backup_files(log_max_count);
857 : : sink_config.set_overwrite_rolled_files(true);
858 : : sink_config.set_remove_old_files(true);
859 : : sink_config.set_fsync_enabled(true);
860 : : sink_config.set_minimum_fsync_interval(std::chrono::milliseconds(log_flush_delay_ms));
861 : : sink_config.set_rotation_on_creation(!use_log_append);
862 : : sink_config.set_filename_append_option(quill::FilenameAppendOption::None);
863 : : sink_config.set_rotation_naming_transport(quill::RotatingFileSinkConfig::RotationNamingScheme::Index);
864 : :
865 : : quill::PatternFormatterOptions format_options;
866 : :
867 : : if (global_instance.utc_enable.load(std::memory_order_acquire)) {
868 : : sink_config.set_timezone(quill::Timezone::GmtTime);
869 : : format_options = quill::PatternFormatterOptions("%(time) UTC @%(thread_id) - %(log_level:<5) - %(message)",
870 : : "%m-%d %H:%M:%S.%Qms", quill::Timezone::GmtTime);
871 : : } else {
872 : : sink_config.set_timezone(quill::Timezone::LocalTime);
873 : : format_options = quill::PatternFormatterOptions("%(time) @%(thread_id) - %(log_level:<5) - %(message)",
874 : : "%m-%d %H:%M:%S.%Qms", quill::Timezone::LocalTime);
875 : : }
876 : :
877 : : impl_->quill_console_sink = std::make_shared<quill::ConsoleSink>();
878 : : impl_->quill_console_sink->set_log_level_filter(quill::LogLevel::Backtrace);
879 : :
880 : : try {
881 : : impl_->quill_file_sink = std::make_shared<quill::RotatingFileSink>(log_path, sink_config);
882 : : } catch (const std::exception& e) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
883 : : impl_->disk_emergency.store(true, std::memory_order_release); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
884 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
885 : : }
886 : :
887 : : std::vector<std::shared_ptr<quill::Sink> > quill_sinks;
888 : :
889 : : if (impl_->quill_file_sink) {
890 : : quill_sinks.emplace_back(impl_->quill_file_sink);
891 : : }
892 : :
893 : : quill_sinks.emplace_back(impl_->quill_console_sink);
894 : :
895 : : impl_->quill_log = quill::Frontend::create_or_get_logger(global_instance.app_name, quill_sinks, format_options);
896 : : impl_->quill_log->set_log_level(quill::LogLevel::None);
897 : :
898 : : #elif defined(VLINK_ENABLE_LOG_DLT)
899 : : DLT_REGISTER_APP(global_instance.app_name.c_str(), "Application for Logging");
900 : : DLT_REGISTER_CONTEXT(dlt_global_ctx_, "Context", "Context for Logging");
901 : :
902 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__ANDROID__)
903 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__QNX__)
904 : : slog2_buffer_set_config_t buffer_cfg;
905 : :
906 : : buffer_cfg.num_buffers = 1;
907 : : buffer_cfg.buffer_set_name = "vlink-log";
908 : : buffer_cfg.verbosity_level = SLOG2_DEBUG2;
909 : : buffer_cfg.buffer_config[0].buffer_name = global_instance.app_name.c_str();
910 : : buffer_cfg.buffer_config[0].num_pages = 32;
911 : : buffer_cfg.max_retries = 3;
912 : :
913 : : if (slog2_register(&buffer_cfg, &impl_->slog2_buffer, 0) == 0) {
914 : : slog2_set_default_buffer(impl_->slog2_buffer);
915 : : }
916 : :
917 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__linux__)
918 : : impl_->kmsg_dev.open(VLINK_KMSG_DEV_PATH, std::ofstream::out | std::ofstream::app);
919 : :
920 : : std::error_code ec(errno, std::generic_category());
921 : :
922 : : if VUNLIKELY (!impl_->kmsg_dev.is_open()) {
923 : : std::cerr << "Failed to open " << VLINK_KMSG_DEV_PATH << ": " << ec.message() << std::endl;
924 : :
925 : : global_instance.is_busy.store(false, std::memory_order_release);
926 : :
927 : : return;
928 : : }
929 : :
930 : : #endif
931 : :
932 : 97 : write_to_file(kInfo, global_instance.version_log);
933 [ + + ]: 98 : }
934 : :
935 : 107 : global_instance.is_busy.store(false, std::memory_order_release);
936 : : }
937 : :
938 : 205 : Logger::~Logger() noexcept {
939 [ + + ]: 108 : if (!impl_->is_enable_file_channel) {
940 : 11 : return;
941 : : }
942 : :
943 [ - + ]: 97 : if (impl_->interface) {
944 : : impl_->interface.reset(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
945 : : impl_->plugin.clear(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
946 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
947 : : }
948 : :
949 : : #if defined(VLINK_ENABLE_LOG_SPD)
950 : : try {
951 [ + - ]: 97 : impl_->spd->flush();
952 : 97 : impl_->spd.reset();
953 [ + - ]: 97 : spdlog::shutdown();
954 : 97 : impl_->spd_console_sink.reset();
955 : 97 : impl_->spd_file_sink.reset();
956 : 97 : impl_->spd_thread_pool.reset();
957 : : } catch (std::exception&) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
958 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
959 : :
960 : : #elif defined(VLINK_ENABLE_LOG_QUI)
961 : : try {
962 : : quill::Backend::notify();
963 : :
964 : : if (impl_->quill_file_sink) {
965 : : impl_->quill_file_sink->flush_sink();
966 : : }
967 : :
968 : : quill::Backend::stop();
969 : : impl_->quill_console_sink.reset();
970 : : impl_->quill_file_sink.reset();
971 : : quill::Frontend::remove_logger(impl_->quill_log);
972 : : impl_->quill_log = nullptr;
973 : : } catch (std::exception&) {
974 : : }
975 : :
976 : : #elif defined(VLINK_ENABLE_LOG_DLT)
977 : : DLT_UNREGISTER_CONTEXT(dlt_global_ctx_);
978 : : DLT_UNREGISTER_APP();
979 : :
980 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__ANDROID__)
981 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__QNX__)
982 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__linux__)
983 : :
984 : : if (impl_->kmsg_dev.is_open()) {
985 : : impl_->kmsg_dev.close();
986 : : }
987 : :
988 : : #endif
989 [ + + ]: 108 : }
990 : :
991 : 185 : char* Logger::get_local_buffer() noexcept {
992 : : thread_local char buffer[kLocalBufferSize];
993 : :
994 : 185 : return buffer;
995 : : }
996 : :
997 : 646 : FastStream& Logger::get_local_stream() noexcept {
998 [ + + + - ]: 646 : static auto& global_instance = LoggerGlobal::get();
999 : :
1000 [ + + ]: 646 : thread_local FastStream stream;
1001 : :
1002 : 646 : stream.reset();
1003 : 646 : stream.flags(global_instance.stream_flags.load(std::memory_order_acquire));
1004 : 1292 : stream.precision(global_instance.stream_precision.load(std::memory_order_acquire));
1005 : 1292 : stream.width(global_instance.stream_width.load(std::memory_order_acquire));
1006 : :
1007 : 646 : return stream;
1008 : : }
1009 : :
1010 : 655 : void Logger::write_to_console(Level level, std::string_view log) noexcept {
1011 [ + + + - ]: 655 : static auto& global_instance = LoggerGlobal::get();
1012 : :
1013 [ + + ]: 1310 : if (level < global_instance.console_level.load(std::memory_order_acquire)) {
1014 : 8 : return;
1015 : : }
1016 : :
1017 : : {
1018 : 647 : std::shared_lock callback_lock(global_instance.callback_mtx);
1019 : :
1020 [ + + ]: 647 : if VUNLIKELY (global_instance.console_callback) {
1021 : 23 : global_instance.console_callback(level, log);
1022 : 23 : return;
1023 : : }
1024 [ + + ]: 647 : }
1025 : :
1026 [ + + ]: 624 : if VUNLIKELY (impl_->is_enable_backtrace.load(std::memory_order_acquire)) {
1027 : 1 : return;
1028 : : }
1029 : :
1030 : 623 : const bool console_in_order = global_instance.console_in_order.load(std::memory_order_acquire);
1031 : :
1032 [ + + ]: 623 : if (global_instance.console_format_enable.load(std::memory_order_acquire)) {
1033 [ + + ]: 29 : thread_local std::string fmt_log;
1034 : :
1035 : 29 : fmt_log.clear();
1036 : :
1037 : 29 : auto tid_str = get_thread_id_str();
1038 : :
1039 [ + + ]: 29 : if (global_instance.utc_enable.load(std::memory_order_acquire)) {
1040 : 6 : fmt_log.append(get_current_time(true));
1041 : 6 : fmt_log.append(" UTC");
1042 : : } else {
1043 : 23 : fmt_log.append(get_current_time(false));
1044 : : }
1045 : :
1046 : 29 : fmt_log.append(" @");
1047 : 29 : fmt_log.append(tid_str);
1048 : 29 : fmt_log.append(" - ");
1049 : 29 : fmt_log.append(get_log_level_str(level).data());
1050 : 29 : fmt_log.append(" - ");
1051 : :
1052 : 29 : fmt_log.append(log);
1053 : :
1054 [ + + + + : 29 : switch (level) {
+ + - - ]
1055 : 2 : case kTrace:
1056 : 2 : print_with_color("", fmt_log, "\n", stdout, console_in_order, false);
1057 : 2 : return;
1058 : 6 : case kDebug:
1059 : 6 : print_with_color("", fmt_log, "\n", stdout, console_in_order, true);
1060 : 6 : return;
1061 : 6 : case kInfo:
1062 : 6 : print_with_color("\033[32m", fmt_log, "\033[0m\n", stdout, console_in_order, true);
1063 : 6 : return;
1064 : 6 : case kWarn:
1065 : 6 : print_with_color("\033[33m", fmt_log, "\033[0m\n", stderr, console_in_order, true);
1066 : 6 : return;
1067 : 6 : case kError:
1068 : 6 : print_with_color("\033[31m", fmt_log, "\033[0m\n", stderr, console_in_order, true);
1069 : 6 : return;
1070 : 3 : case kFatal:
1071 : 3 : print_with_color("\033[41;37;1m", fmt_log, "\033[0m\n", stderr, console_in_order, true);
1072 : 3 : return;
1073 : : // LCOV_EXCL_START GCOVR_EXCL_START
1074 : : case kOff:
1075 : : return;
1076 : : default:
1077 : : return;
1078 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1079 : : }
1080 : : } else {
1081 [ + + + + : 594 : switch (level) {
+ + - - ]
1082 : 1 : case kTrace:
1083 : 1 : print_with_color("", log, "\n", stdout, console_in_order, false);
1084 : 1 : return;
1085 : 1 : case kDebug:
1086 : 1 : print_with_color("", log, "\n", stdout, console_in_order, true);
1087 : 1 : return;
1088 : 77 : case kInfo:
1089 : 77 : print_with_color("\033[32m", log, "\033[0m\n", stdout, console_in_order, true);
1090 : 77 : return;
1091 : 213 : case kWarn:
1092 : 213 : print_with_color("\033[33m", log, "\033[0m\n", stderr, console_in_order, true);
1093 : 213 : return;
1094 : 170 : case kError:
1095 : 170 : print_with_color("\033[31m", log, "\033[0m\n", stderr, console_in_order, true);
1096 : 170 : return;
1097 : 132 : case kFatal:
1098 : 132 : print_with_color("\033[41;37;1m", log, "\033[0m\n", stderr, console_in_order, true);
1099 : 132 : return;
1100 : : // LCOV_EXCL_START GCOVR_EXCL_START
1101 : : case kOff:
1102 : : return;
1103 : : default:
1104 : : return;
1105 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1106 : : }
1107 : : }
1108 : : }
1109 : :
1110 : 752 : void Logger::write_to_file(Level level, std::string_view log) noexcept {
1111 [ + + + - ]: 752 : static auto& global_instance = LoggerGlobal::get();
1112 : :
1113 [ + + ]: 1504 : if (level < global_instance.file_level.load(std::memory_order_acquire)) {
1114 : 51 : return;
1115 : : }
1116 : :
1117 [ + + ]: 701 : if VUNLIKELY (!impl_->is_enable_file_channel) {
1118 : 1 : return;
1119 : : }
1120 : :
1121 : : {
1122 : 700 : std::shared_lock callback_lock(global_instance.callback_mtx);
1123 : :
1124 [ + + ]: 700 : if VUNLIKELY (global_instance.file_callback) {
1125 : 1 : global_instance.file_callback(level, log);
1126 : 1 : return;
1127 : : }
1128 [ + + ]: 700 : }
1129 : :
1130 [ - + ]: 699 : if (impl_->interface) {
1131 : : impl_->interface->log(level, log); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1132 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1133 : : }
1134 : :
1135 [ + + ]: 699 : if VUNLIKELY (impl_->disk_emergency.load(std::memory_order_acquire)) {
1136 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1137 : : }
1138 : :
1139 : : #if defined(VLINK_ENABLE_LOG_SPD)
1140 : 697 : spdlog::level::level_enum spd_level = spdlog::level::debug;
1141 : :
1142 [ + + + + : 697 : switch (level) {
+ + - - ]
1143 : 2 : case kTrace:
1144 : 2 : spd_level = spdlog::level::trace;
1145 : 2 : break;
1146 : 2 : case kDebug:
1147 : 2 : spd_level = spdlog::level::debug;
1148 : 2 : break;
1149 : 175 : case kInfo:
1150 : 175 : spd_level = spdlog::level::info;
1151 : 175 : break;
1152 : 214 : case kWarn:
1153 : 214 : spd_level = spdlog::level::warn;
1154 : 214 : break;
1155 : 171 : case kError:
1156 : 171 : spd_level = spdlog::level::err;
1157 : 171 : break;
1158 : 133 : case kFatal:
1159 : 133 : spd_level = spdlog::level::critical;
1160 : 133 : break;
1161 : : // LCOV_EXCL_START GCOVR_EXCL_START
1162 : : case kOff:
1163 : : return;
1164 : : default:
1165 : : return;
1166 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1167 : : }
1168 : :
1169 : : try {
1170 [ + - ]: 697 : impl_->spd->log(spd_level, log);
1171 : : // LCOV_EXCL_START GCOVR_EXCL_START
1172 : : } catch (const std::exception& e) {
1173 : : impl_->disk_emergency.store(true, std::memory_order_release);
1174 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl;
1175 : : }
1176 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
1177 : :
1178 : : #elif defined(VLINK_ENABLE_LOG_QUI)
1179 : : try {
1180 : : if (impl_->is_enable_backtrace.load(std::memory_order_acquire)) {
1181 : : impl_->quill_log->log_statement<true>(&impl_->quill_metadata_backtrace, log);
1182 : :
1183 : : if (level < kWarn) {
1184 : : return;
1185 : : }
1186 : : }
1187 : :
1188 : : switch (level) {
1189 : : case kTrace:
1190 : : impl_->quill_log->log_statement<false>(&impl_->quill_metadata_trace, log);
1191 : : break;
1192 : : case kDebug:
1193 : : impl_->quill_log->log_statement<false>(&impl_->quill_metadata_debug, log);
1194 : : break;
1195 : : case kInfo:
1196 : : impl_->quill_log->log_statement<false>(&impl_->quill_metadata_info, log);
1197 : : break;
1198 : : case kWarn:
1199 : : impl_->quill_log->log_statement<false>(&impl_->quill_metadata_warn, log);
1200 : : break;
1201 : : case kError:
1202 : : impl_->quill_log->log_statement<true>(&impl_->quill_metadata_error, log);
1203 : : quill::Backend::notify();
1204 : : break;
1205 : : case kFatal:
1206 : : impl_->quill_log->log_statement<true>(&impl_->quill_metadata_fatal, log);
1207 : : quill::Backend::notify();
1208 : : break;
1209 : : case kOff:
1210 : : return;
1211 : : default:
1212 : : return;
1213 : : }
1214 : : } catch (const std::exception& e) {
1215 : : impl_->disk_emergency.store(true, std::memory_order_release);
1216 : : std::cerr << "VLink logger disk emergency: " << e.what() << std::endl;
1217 : : }
1218 : :
1219 : : #elif defined(VLINK_ENABLE_LOG_DLT)
1220 : :
1221 : : DltLogLevelType dlt_level = DLT_LOG_DEFAULT;
1222 : : switch (level) {
1223 : : case kTrace:
1224 : : dlt_level = DLT_LOG_VERBOSE;
1225 : : break;
1226 : : case kDebug:
1227 : : dlt_level = DLT_LOG_DEBUG;
1228 : : break;
1229 : : case kInfo:
1230 : : dlt_level = DLT_LOG_INFO;
1231 : : break;
1232 : : case kWarn:
1233 : : dlt_level = DLT_LOG_WARN;
1234 : : break;
1235 : : case kError:
1236 : : dlt_level = DLT_LOG_ERROR;
1237 : : break;
1238 : : case kFatal:
1239 : : dlt_level = DLT_LOG_FATAL;
1240 : : break;
1241 : : case kOff:
1242 : : return;
1243 : : default:
1244 : : return;
1245 : : }
1246 : :
1247 : : #ifdef DLT_SIZED_STRING
1248 : : DLT_LOG(dlt_global_ctx_, dlt_level, DLT_SIZED_STRING(log.data(), log.size()));
1249 : : #else
1250 : : DLT_LOG(dlt_global_ctx_, dlt_level, DLT_STRING(log.data()));
1251 : : #endif
1252 : :
1253 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__ANDROID__)
1254 : :
1255 : : int android_level = ANDROID_LOG_DEBUG;
1256 : : switch (level) {
1257 : : case kTrace:
1258 : : android_level = ANDROID_LOG_VERBOSE;
1259 : : break;
1260 : : case kDebug:
1261 : : android_level = ANDROID_LOG_DEBUG;
1262 : : break;
1263 : : case kInfo:
1264 : : android_level = ANDROID_LOG_INFO;
1265 : : break;
1266 : : case kWarn:
1267 : : android_level = ANDROID_LOG_WARN;
1268 : : break;
1269 : : case kError:
1270 : : android_level = ANDROID_LOG_ERROR;
1271 : : break;
1272 : : case kFatal:
1273 : : android_level = ANDROID_LOG_FATAL;
1274 : : break;
1275 : : case kOff:
1276 : : return;
1277 : : default:
1278 : : return;
1279 : : }
1280 : :
1281 : : __android_log_write(android_level, global_instance.app_name.c_str(), log.data());
1282 : :
1283 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__QNX__)
1284 : : int nat_level = SLOG2_DEBUG1;
1285 : : switch (level) {
1286 : : case kTrace:
1287 : : nat_level = SLOG2_DEBUG2;
1288 : : break;
1289 : : case kDebug:
1290 : : nat_level = SLOG2_DEBUG1;
1291 : : break;
1292 : : case kInfo:
1293 : : nat_level = SLOG2_INFO;
1294 : : break;
1295 : : case kWarn:
1296 : : nat_level = SLOG2_WARNING;
1297 : : break;
1298 : : case kError:
1299 : : nat_level = SLOG2_ERROR;
1300 : : break;
1301 : : case kFatal:
1302 : : nat_level = SLOG2_CRITICAL;
1303 : : break;
1304 : : case kOff:
1305 : : return;
1306 : : default:
1307 : : return;
1308 : : }
1309 : :
1310 : : slog2c(impl_->slog2_buffer, ::gettid(), nat_level, log.data());
1311 : :
1312 : : #elif defined(VLINK_ENABLE_LOG_NAT) && defined(__linux__)
1313 : :
1314 : : if (impl_->kmsg_dev.is_open()) {
1315 : : std::lock_guard lock(impl_->file_mtx);
1316 : :
1317 : : switch (level) {
1318 : : case kTrace:
1319 : : impl_->kmsg_dev << "<7>";
1320 : : break;
1321 : : case kDebug:
1322 : : impl_->kmsg_dev << "<6>";
1323 : : break;
1324 : : case kInfo:
1325 : : impl_->kmsg_dev << "<5>";
1326 : : break;
1327 : : case kWarn:
1328 : : impl_->kmsg_dev << "<4>";
1329 : : break;
1330 : : case kError:
1331 : : impl_->kmsg_dev << "<3>";
1332 : : break;
1333 : : case kFatal:
1334 : : impl_->kmsg_dev << "<2>";
1335 : : break;
1336 : : case kOff:
1337 : : return;
1338 : : default:
1339 : : return;
1340 : : }
1341 : :
1342 : : impl_->kmsg_dev << log << std::endl;
1343 : : }
1344 : :
1345 : : #endif
1346 : : }
1347 : :
1348 : : } // namespace vlink
|