| /* |
| * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license |
| * that can be found in the LICENSE file. |
| */ |
| |
| #ifndef RUNTIME_LOGGING_H |
| #define RUNTIME_LOGGING_H |
| |
| #include <cstdarg> |
| #include <initializer_list> |
| #include <memory> |
| #include <string_view> |
| |
| #include "Clock.hpp" |
| #include "CompilerConstants.hpp" |
| #include "std_support/Span.hpp" |
| |
| namespace kotlin { |
| namespace logging { |
| |
| // Must match LoggingLevel in RuntimeLogging.kt |
| enum class Level : int32_t { |
| kNone = 0, |
| kError = 1, |
| kWarning = 2, |
| kInfo = 3, |
| kDebug = 4, |
| }; |
| |
| // Must match LoggingTag in RuntimeLogging.kt |
| enum class Tag : int32_t { |
| kLogging = 0, |
| kRT = 1, |
| kGC = 2, |
| kMM = 3, |
| kTLS = 4, |
| kPause = 5, |
| kAlloc = 6, |
| kBalancing = 7, |
| kBarriers = 8, |
| kGCMark = 9, |
| kGCScheduler = 10, |
| kMemoryDump = 11, |
| |
| kEnumSize = 12 |
| }; |
| |
| namespace internal { |
| |
| inline const char* name(Level level) { |
| switch (level) { |
| case Level::kNone: return "NONE"; |
| case Level::kError: return "ERROR"; |
| case Level::kWarning: return "WARNING"; |
| case Level::kInfo: return "INFO"; |
| case Level::kDebug: return "DEBUG"; |
| } |
| } |
| |
| inline const char* name(Tag tag) { |
| switch (tag) { |
| case Tag::kLogging: return "logging"; |
| case Tag::kRT: return "rt"; |
| case Tag::kGC: return "gc"; |
| case Tag::kMM: return "mm"; |
| case Tag::kTLS: return "tls"; |
| case Tag::kPause: return "pause"; |
| case Tag::kAlloc: return "alloc"; |
| case Tag::kBalancing: return "balancing"; |
| case Tag::kBarriers: return "barriers"; |
| case Tag::kGCMark: return "gcMark"; |
| case Tag::kGCScheduler: return "gcScheduler"; |
| case Tag::kMemoryDump: return "memoryDump"; |
| |
| case Tag::kEnumSize: break; |
| } |
| RuntimeFail("Unexpected logging tag %d", static_cast<int>(tag)); |
| } |
| |
| ALWAYS_INLINE inline Level maxLevel(Tag tag, const int32_t logLevels[]) { |
| return static_cast<logging::Level>(logLevels[static_cast<int>(tag)]); |
| } |
| |
| ALWAYS_INLINE inline bool enabled(logging::Level level, std_support::span<const logging::Tag> tags, const int32_t logLevels[]) { |
| for (auto tag: tags) { |
| if (level <= maxLevel(tag, logLevels)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| ALWAYS_INLINE inline bool enabled(logging::Level level, std::initializer_list<const logging::Tag> tags, ...) noexcept { |
| std_support::span<const logging::Tag> tagsSpan(std::data(tags), std::size(tags)); |
| return enabled(level, tagsSpan, compiler::runtimeLogs()); |
| } |
| |
| class Logger { |
| public: |
| virtual ~Logger() = default; |
| |
| virtual void Log(Level level, std_support::span<const Tag> tags, std::string_view message) const noexcept = 0; |
| }; |
| |
| std::unique_ptr<Logger> CreateStderrLogger() noexcept; |
| |
| std_support::span<char> FormatLogEntry( |
| std_support::span<char> buffer, |
| Level level, |
| std_support::span<const Tag> tags, |
| int threadId, |
| kotlin::nanoseconds timestamp, |
| const char* format, |
| std::va_list args) noexcept; |
| |
| void Log( |
| const Logger& logger, |
| Level level, |
| std_support::span<const Tag> tags, |
| int threadId, |
| kotlin::nanoseconds timestamp, |
| const char* format, |
| std::va_list args) noexcept; |
| |
| } // namespace internal |
| |
| void OnRuntimeInit() noexcept; |
| |
| __attribute__((format(printf, 3, 4))) |
| void Log(Level level, std::initializer_list<Tag> tags, const char* format, ...) noexcept; |
| void VLog(Level level, std::initializer_list<Tag> tags, const char* format, std::va_list args) noexcept; |
| |
| } // namespace logging |
| |
| // Well known tags. |
| // These are defined outside of logging namespace for simpler usage. |
| inline constexpr auto kTagGC = logging::Tag::kGC; |
| inline constexpr auto kTagMM = logging::Tag::kMM; |
| inline constexpr auto kTagTLS = logging::Tag::kTLS; |
| inline constexpr auto kTagBalancing = logging::Tag::kBalancing; |
| |
| } // namespace kotlin |
| |
| #endif // RUNTIME_LOGGING_H |
| |
| // Using macros to simplify forwarding of varargs without breaking __attribute__((format)) and to avoid |
| // evaluating args in `...` if logging is disabled. |
| |
| #define RuntimeLog(level, ...) \ |
| do { \ |
| if (::kotlin::logging::internal::enabled(level, ##__VA_ARGS__)) { \ |
| ::kotlin::logging::Log(level, ##__VA_ARGS__); \ |
| } \ |
| } while (false) |
| |
| #define RuntimeLogDebug(...) RuntimeLog(::kotlin::logging::Level::kDebug, ##__VA_ARGS__) |
| #define RuntimeLogInfo(...) RuntimeLog(::kotlin::logging::Level::kInfo, ##__VA_ARGS__) |
| #define RuntimeLogWarning(...) RuntimeLog(::kotlin::logging::Level::kWarning, ##__VA_ARGS__) |
| #define RuntimeLogError(...) RuntimeLog(::kotlin::logging::Level::kError, ##__VA_ARGS__) |