| // Copyright 2020 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 "sdk/lib/syslog/cpp/logging_backend_fuchsia_globals.h" |
| |
| #include "sdk/lib/syslog/structured_backend/cpp/log_connection.h" |
| #include "sdk/lib/syslog/structured_backend/cpp/logger.h" |
| #include "sdk/lib/syslog/structured_backend/cpp/raw_log_settings.h" |
| #include "sdk/lib/syslog/structured_backend/fuchsia_syslog.h" |
| |
| #ifndef _ALL_SOURCE |
| #define _ALL_SOURCE // To get MTX_INIT |
| #endif |
| |
| #include <lib/zx/result.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| #include <zircon/process.h> |
| #include <zircon/syscalls.h> |
| |
| #include <condition_variable> |
| #include <mutex> |
| |
| namespace fuchsia_logging::internal { |
| |
| class LogState; |
| |
| namespace { |
| |
| LogState* state = nullptr; |
| std::mutex state_lock; |
| // This thread's koid. |
| // Initialized on first use. |
| thread_local zx_koid_t tls_thread_koid{ZX_KOID_INVALID}; |
| |
| zx_koid_t GetKoid(zx_handle_t handle) { |
| zx_info_handle_basic_t info; |
| zx_status_t status = |
| zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr); |
| return status == ZX_OK ? info.koid : ZX_KOID_INVALID; |
| } |
| |
| } // namespace |
| |
| #if FUCHSIA_API_LEVEL_AT_LEAST(NEXT) |
| |
| // This is internal::Logger which is distinct from the public one. This is currently only used for |
| // the global logger. |
| class Logger { |
| public: |
| static void InitGlobalInstance(const RawLogSettings& settings) { |
| bool called = false; |
| std::call_once(once_, [&called, settings]() { |
| called = true; |
| if (auto logger = fuchsia_logging::Logger::Create(settings, &min_severity_); logger.is_ok()) { |
| global_instance_ = new Logger(*std::move(logger)); |
| } else { |
| global_instance_ = new Logger; |
| } |
| }); |
| if (!called) { |
| auto logger = fuchsia_logging::Logger::Create(settings, &min_severity_); |
| std::scoped_lock lock(global_instance_->mutex_); |
| if (logger.is_ok()) { |
| global_instance_->logger_ = *std::move(logger); |
| } else { |
| global_instance_->logger_ = {}; |
| } |
| } |
| } |
| |
| static Logger& GlobalInstance(RawLogSettings (*get_default_settings)()) { |
| std::call_once(once_, [get_default_settings] { |
| if (auto logger = fuchsia_logging::Logger::Create(get_default_settings(), &min_severity_); |
| logger.is_ok()) { |
| global_instance_ = new Logger(*std::move(logger)); |
| } else { |
| global_instance_ = new Logger; |
| } |
| }); |
| return *global_instance_; |
| } |
| |
| zx::result<> FlushSpan(cpp20::span<const uint8_t> span) const { return logger().FlushSpan(span); } |
| |
| template <typename T> |
| void ForEachTag(T callback) const { |
| logger().ForEachTag(callback); |
| } |
| |
| static FuchsiaLogSeverity min_severity() { return min_severity_.load(std::memory_order_relaxed); } |
| |
| private: |
| Logger() = default; |
| explicit Logger(fuchsia_logging::Logger logger) : logger_(std::move(logger)) {} |
| |
| fuchsia_logging::Logger logger() const { |
| std::scoped_lock lock(mutex_); |
| return logger_; |
| } |
| |
| static std::once_flag once_; |
| static Logger* global_instance_; |
| static std::atomic<FuchsiaLogSeverity> min_severity_; |
| |
| mutable std::mutex mutex_; |
| fuchsia_logging::Logger logger_ __TA_GUARDED(mutex_); |
| }; |
| |
| std::once_flag Logger::once_; |
| Logger* Logger::global_instance_; |
| std::atomic<FuchsiaLogSeverity> Logger::min_severity_; |
| |
| #endif |
| |
| extern "C" { |
| |
| __EXPORT |
| zx_koid_t FuchsiaLogGetCurrentThreadKoid() { |
| if (unlikely(tls_thread_koid == ZX_KOID_INVALID)) { |
| tls_thread_koid = GetKoid(zx_thread_self()); |
| } |
| ZX_DEBUG_ASSERT(tls_thread_koid != ZX_KOID_INVALID); |
| return tls_thread_koid; |
| } |
| |
| #if FUCHSIA_API_LEVEL_LESS_THAN(NEXT) |
| // FuchsiaLogSetStateLocked, FuchsiaLogAcquireState, FuchsiaLogReleaseState and |
| // FuchsiaLogGetStateLocked can can be removed once the API level mentioned above and all preceding |
| // levels, have been retired. |
| #endif |
| |
| __EXPORT |
| void FuchsiaLogSetStateLocked(LogState* new_state) { state = new_state; } |
| |
| __EXPORT void FuchsiaLogAcquireState() __TA_NO_THREAD_SAFETY_ANALYSIS { state_lock.lock(); } |
| |
| __EXPORT void FuchsiaLogReleaseState() __TA_NO_THREAD_SAFETY_ANALYSIS { state_lock.unlock(); } |
| |
| __EXPORT |
| LogState* FuchsiaLogGetStateLocked() { return state; } |
| |
| #if FUCHSIA_API_LEVEL_AT_LEAST(NEXT) |
| |
| __EXPORT |
| void FuchsiaLogInitGlobalLogger(const RawLogSettings* settings) { |
| Logger::InitGlobalInstance(*settings); |
| } |
| |
| __EXPORT uint8_t FuchsiaLogGetGlobalMinSeverity() { return Logger::min_severity(); } |
| |
| __EXPORT |
| Logger* FuchsiaLogGetGlobalLogger(RawLogSettings (*get_default_settings)()) { |
| return &Logger::GlobalInstance(get_default_settings); |
| } |
| |
| __EXPORT |
| zx_status_t FuchsiaLogWrite(const Logger* logger, const void* buffer, size_t len) { |
| return logger->FlushSpan(cpp20::span(static_cast<const uint8_t*>(buffer), len)).status_value(); |
| } |
| |
| __EXPORT |
| void FuchsiaLogForEachTag(const Logger* logger, void* context, |
| void (*callback)(void* context, const char* tag)) { |
| logger->ForEachTag( |
| [callback, context](const std::string& tag) { callback(context, tag.c_str()); }); |
| } |
| |
| #endif // FUCHSIA_API_LEVEL_AT_LEAST(NEXT) |
| |
| } // extern "C" |
| |
| } // namespace fuchsia_logging::internal |