blob: 2bb06d9d43f01efcc651980e9a35d976477803cf [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "log.h"
#include <lib/ddk/debug.h>
#include <stdarg.h>
#include <algorithm>
#include <string_view>
#include <vector>
#include "src/connectivity/bluetooth/lib/cpp-string/string_printf.h"
namespace bt {
namespace {
std::atomic_int g_printf_min_severity(-1);
struct LogScopeState {
std::vector<std::string> scopes;
std::vector<std::string> added_contexts;
};
thread_local LogScopeState g_log_scope_state;
fx_log_severity_t kDdkSeverities[kNumLogSeverities] = {
DDK_LOG_ERROR, DDK_LOG_WARNING, DDK_LOG_INFO, DDK_LOG_DEBUG, DDK_LOG_TRACE,
};
const char* const kLogSeverityNames[kNumLogSeverities] = {
"ERROR", "WARNING", "INFO", "DEBUG", "TRACE",
};
constexpr size_t LogSeverityToIndex(LogSeverity severity) {
return std::min(kNumLogSeverities - 1, static_cast<size_t>(severity));
}
inline fx_log_severity_t LogSeverityToDdkLog(LogSeverity severity) {
return kDdkSeverities[LogSeverityToIndex(severity)];
}
inline const char* LogSeverityToString(LogSeverity severity) {
return kLogSeverityNames[LogSeverityToIndex(severity)];
}
bool IsPrintfEnabled() { return g_printf_min_severity >= 0; }
} // namespace
bool IsLogLevelEnabled(LogSeverity severity) {
if (IsPrintfEnabled()) {
return static_cast<int>(severity) <= g_printf_min_severity;
}
return zxlog_level_enabled_etc(LogSeverityToDdkLog(severity));
}
std::string FormattedLogScopes() {
std::string formatted;
for (const auto& scope : g_log_scope_state.scopes) {
formatted += bt_lib_cpp_string::StringPrintf("[%s]", scope.c_str());
}
return formatted;
}
std::string FormattedLogContexts() {
if (g_log_scope_state.added_contexts.empty()) {
return "";
}
std::string formatted;
for (auto it = g_log_scope_state.added_contexts.begin();
it != g_log_scope_state.added_contexts.end(); it++) {
if (it == g_log_scope_state.added_contexts.end() - 1) {
formatted += *it;
break;
}
formatted += *it + ",";
}
return bt_lib_cpp_string::StringPrintf("{%s}", formatted.c_str());
}
void LogMessage(const char* file, int line, LogSeverity severity, const char* tag, const char* fmt,
...) {
if (!bt::IsLogLevelEnabled(severity)) {
return;
}
va_list args;
va_start(args, fmt);
if (IsPrintfEnabled()) {
std::string msg = bt_lib_cpp_string::StringPrintf(
"%s: [%s:%s:%d]%s%s %s\n", LogSeverityToString(severity), tag, bt::internal::BaseName(file),
line, FormattedLogContexts().c_str(), FormattedLogScopes().c_str(), fmt);
vprintf(msg.c_str(), args);
} else {
std::string msg = bt_lib_cpp_string::StringPrintf(
"[%s]%s%s %s", tag, FormattedLogContexts().c_str(), FormattedLogScopes().c_str(), fmt);
zxlogvf_etc(LogSeverityToDdkLog(severity), nullptr, file, line, msg.c_str(), args);
}
va_end(args);
}
void UsePrintf(LogSeverity min_severity) { g_printf_min_severity = static_cast<int>(min_severity); }
namespace internal {
LogScopeGuard::LogScopeGuard(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
std::string scope = bt_lib_cpp_string::StringVPrintf(fmt, args);
va_end(args);
g_log_scope_state.scopes.push_back(std::move(scope));
}
LogScopeGuard::~LogScopeGuard() { g_log_scope_state.scopes.pop_back(); }
LogContextGuard::LogContextGuard(LogContext context) {
empty_ = context.context.empty();
if (!empty_) {
g_log_scope_state.added_contexts.push_back(std::move(context.context));
}
}
LogContextGuard::~LogContextGuard() {
if (!empty_) {
g_log_scope_state.added_contexts.pop_back();
}
}
LogContext SaveLogContext() {
return LogContext{bt_lib_cpp_string::StringPrintf("%s%s", FormattedLogContexts().c_str(),
FormattedLogScopes().c_str())};
}
} // namespace internal
} // namespace bt