Log.h#

Fully qualified name: carb/logging/Log.h

File members: carb/logging/Log.h

// SPDX-FileCopyrightText: Copyright (c) 2018-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: LicenseRef-NvidiaProprietary
//
// NVIDIA CORPORATION, its affiliates and licensors retain all intellectual
// property and proprietary rights in and to this material, related
// documentation and any modifications thereto. Any use, reproduction,
// disclosure or distribution of this material and related documentation
// without an express license agreement from NVIDIA CORPORATION or
// its affiliates is strictly prohibited.

#pragma once

#include "../Framework.h"
#include "ILogging.h"

#include "../../omni/log/ILog.h"
#include "../cpp/Optional.h"
#include "../cpp/ZStringView.h"
#include "../cpp/Chrono.h"

#include <cctype>
#include <cstdint>
#include <cstdio>
#include <atomic>

// example-begin Log levels
namespace carb
{
namespace logging
{

const int32_t kLevelVerbose = -2;
const int32_t kLevelInfo = -1;
const int32_t kLevelWarn = 0;
const int32_t kLevelError = 1;
const int32_t kLevelFatal = 2;

} // namespace logging
} // namespace carb
// example-end

CARB_WEAKLINK int32_t g_carbLogLevel = carb::logging::kLevelWarn;
CARB_WEAKLINK carb::logging::LogFn g_carbLogFn CARB_PRINTF_FUNCTION(6, 7);
CARB_WEAKLINK carb::logging::ILogging* g_carbLogging;

#if CARB_COMPILER_GNUC || defined(DOXYGEN_BUILD)
#    define CARB_FAKE_PRINTF(fmt, ...)                                                                                 \
        do                                                                                                             \
        {                                                                                                              \
        } while (0)
#else
#    define CARB_FAKE_PRINTF(fmt, ...)                                                                                 \
        do                                                                                                             \
        {                                                                                                              \
            if (false)                                                                                                 \
                ::printf(fmt, ##__VA_ARGS__);                                                                          \
        } while (0)
#endif

#define CARB_LOG(level, fmt, ...)                                                                                      \
    do                                                                                                                 \
    {                                                                                                                  \
        auto lvl = (level);                                                                                            \
        if (g_carbLogFn && g_carbLogLevel <= lvl)                                                                      \
        {                                                                                                              \
            CARB_FAKE_PRINTF(fmt, ##__VA_ARGS__);                                                                      \
            g_carbLogFn(g_carbClientName, lvl, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__);                      \
        }                                                                                                              \
    } while (0)

#define CARB_LOG_VERBOSE(fmt, ...) CARB_LOG(carb::logging::kLevelVerbose, fmt, ##__VA_ARGS__)
#define CARB_LOG_INFO(fmt, ...) CARB_LOG(carb::logging::kLevelInfo, fmt, ##__VA_ARGS__)
#define CARB_LOG_WARN(fmt, ...) CARB_LOG(carb::logging::kLevelWarn, fmt, ##__VA_ARGS__)
#define CARB_LOG_ERROR(fmt, ...) CARB_LOG(carb::logging::kLevelError, fmt, ##__VA_ARGS__)
#define CARB_LOG_FATAL(fmt, ...) CARB_LOG(carb::logging::kLevelFatal, fmt, ##__VA_ARGS__)

#define CARB_LOG_ONCE(level, fmt, ...)                                                                                   \
    do                                                                                                                   \
    {                                                                                                                    \
        [[maybe_unused]] static bool CARB_JOIN(logged_, __LINE__) =                                                      \
            (g_carbLogFn && g_carbLogLevel <= (level)) ?                                                                 \
                (false ?                                                                                                 \
                     (::printf(fmt, ##__VA_ARGS__), true) :                                                              \
                     (g_carbLogFn(g_carbClientName, (level), __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__), true)) : \
                false;                                                                                                   \
    } while (0)

#define CARB_LOG_VERBOSE_ONCE(fmt, ...) CARB_LOG_ONCE(carb::logging::kLevelVerbose, fmt, ##__VA_ARGS__)
#define CARB_LOG_INFO_ONCE(fmt, ...) CARB_LOG_ONCE(carb::logging::kLevelInfo, fmt, ##__VA_ARGS__)
#define CARB_LOG_WARN_ONCE(fmt, ...) CARB_LOG_ONCE(carb::logging::kLevelWarn, fmt, ##__VA_ARGS__)
#define CARB_LOG_ERROR_ONCE(fmt, ...) CARB_LOG_ONCE(carb::logging::kLevelError, fmt, ##__VA_ARGS__)
#define CARB_LOG_FATAL_ONCE(fmt, ...) CARB_LOG_ONCE(carb::logging::kLevelFatal, fmt, ##__VA_ARGS__)

#define CARB_LOG_DECLARE_GATE(gateName) static std::atomic_bool CARB_JOIN(gate_, gateName) = false

#define CARB_LOG_ONCE_RESETTABLE(level, gateName, fmt, ...)                                                            \
    do                                                                                                                 \
    {                                                                                                                  \
        if (g_carbLogFn && g_carbLogLevel <= (level) && !CARB_JOIN(gate_, gateName))                                   \
        {                                                                                                              \
            CARB_FAKE_PRINTF(fmt, ##__VA_ARGS__);                                                                      \
            g_carbLogFn(g_carbClientName, (level), __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__);                  \
            CARB_JOIN(gate_, gateName) = true;                                                                         \
        }                                                                                                              \
    } while (0)

#define CARB_LOG_RESET_GATE(gateName)                                                                                  \
    do                                                                                                                 \
    {                                                                                                                  \
        CARB_JOIN(gate_, gateName) = false;                                                                            \
    } while (0)

#define CARB_LOG_VERBOSE_ONCE_RESETTABLE(gateName, fmt, ...)                                                           \
    CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelVerbose, gateName, fmt, ##__VA_ARGS__)
#define CARB_LOG_INFO_ONCE_RESETTABLE(gateName, fmt, ...)                                                              \
    CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelInfo, gateName, fmt, ##__VA_ARGS__)
#define CARB_LOG_WARN_ONCE_RESETTABLE(gateName, fmt, ...)                                                              \
    CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelWarn, gateName, fmt, ##__VA_ARGS__)
#define CARB_LOG_ERROR_ONCE_RESETTABLE(gateName, fmt, ...)                                                             \
    CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelError, gateName, fmt, ##__VA_ARGS__)
#define CARB_LOG_FATAL_ONCE_RESETTABLE(gateName, fmt, ...)                                                             \
    CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelFatal, gateName, fmt, ##__VA_ARGS__)

#define CARB_LOG_EVERY_N_MS(level, intervalMs, fmt, ...)                                                                \
    do                                                                                                                  \
    {                                                                                                                   \
        [[maybe_unused]] static std::atomic<carb::chrono::high_resolution_clock::time_point> CARB_JOIN(                 \
            lastLogTime_, __LINE__) =                                                                                   \
            carb::chrono::high_resolution_clock::now() - std::chrono::milliseconds(intervalMs + 1);                     \
        if (g_carbLogFn && g_carbLogLevel <= (level))                                                                   \
        {                                                                                                               \
            auto now_ = carb::chrono::high_resolution_clock::now();                                                     \
            auto elapsed_ =                                                                                             \
                std::chrono::duration_cast<std::chrono::milliseconds>(now_ - CARB_JOIN(lastLogTime_, __LINE__).load()); \
            if (elapsed_.count() >= (intervalMs))                                                                       \
            {                                                                                                           \
                CARB_FAKE_PRINTF(fmt, ##__VA_ARGS__);                                                                   \
                g_carbLogFn(g_carbClientName, (level), __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__);               \
                CARB_JOIN(lastLogTime_, __LINE__) = now_;                                                               \
            }                                                                                                           \
        }                                                                                                               \
    } while (0)

#define CARB_LOG_VERBOSE_EVERY_N_MS(intervalMs, fmt, ...)                                                              \
    CARB_LOG_EVERY_N_MS(carb::logging::kLevelVerbose, intervalMs, fmt, ##__VA_ARGS__)
#define CARB_LOG_INFO_EVERY_N_MS(intervalMs, fmt, ...)                                                                 \
    CARB_LOG_EVERY_N_MS(carb::logging::kLevelInfo, intervalMs, fmt, ##__VA_ARGS__)
#define CARB_LOG_WARN_EVERY_N_MS(intervalMs, fmt, ...)                                                                 \
    CARB_LOG_EVERY_N_MS(carb::logging::kLevelWarn, intervalMs, fmt, ##__VA_ARGS__)
#define CARB_LOG_ERROR_EVERY_N_MS(intervalMs, fmt, ...)                                                                \
    CARB_LOG_EVERY_N_MS(carb::logging::kLevelError, intervalMs, fmt, ##__VA_ARGS__)
#define CARB_LOG_FATAL_EVERY_N_MS(intervalMs, fmt, ...)                                                                \
    CARB_LOG_EVERY_N_MS(carb::logging::kLevelFatal, intervalMs, fmt, ##__VA_ARGS__)

#define CARB_LOG_EVERY_N_CALLS(level, callCount, fmt, ...)                                                             \
    do                                                                                                                 \
    {                                                                                                                  \
        [[maybe_unused]] static std::atomic_uint32_t CARB_JOIN(callCounter_, __LINE__) = 0;                            \
        if (g_carbLogFn && g_carbLogLevel <= (level))                                                                  \
        {                                                                                                              \
            auto count_ = ++CARB_JOIN(callCounter_, __LINE__);                                                         \
            if (count_ >= (callCount))                                                                                 \
            {                                                                                                          \
                CARB_JOIN(callCounter_, __LINE__) = 0;                                                                 \
            }                                                                                                          \
            if (count_ == 1)                                                                                           \
            {                                                                                                          \
                CARB_FAKE_PRINTF(fmt, ##__VA_ARGS__);                                                                  \
                g_carbLogFn(g_carbClientName, (level), __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__);              \
            }                                                                                                          \
        }                                                                                                              \
    } while (0)

#define CARB_LOG_VERBOSE_EVERY_N_CALLS(callCount, fmt, ...)                                                            \
    CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelVerbose, callCount, fmt, ##__VA_ARGS__)
#define CARB_LOG_INFO_EVERY_N_CALLS(callCount, fmt, ...)                                                               \
    CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelInfo, callCount, fmt, ##__VA_ARGS__)
#define CARB_LOG_WARN_EVERY_N_CALLS(callCount, fmt, ...)                                                               \
    CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelWarn, callCount, fmt, ##__VA_ARGS__)
#define CARB_LOG_ERROR_EVERY_N_CALLS(callCount, fmt, ...)                                                              \
    CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelError, callCount, fmt, ##__VA_ARGS__)
#define CARB_LOG_FATAL_EVERY_N_CALLS(callCount, fmt, ...)                                                              \
    CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelFatal, callCount, fmt, ##__VA_ARGS__)

#define CARB_LOG_GLOBALS()

namespace carb
{
namespace logging
{

inline ILogging* getLogging()
{
    return g_carbLogging;
}

inline void registerLoggingForClient() noexcept
{
    g_carbLogging = getFramework()->tryAcquireInterface<ILogging>();
    if (g_carbLogging)
    {
        g_carbLogging->registerSource(g_carbClientName, [](int32_t logLevel) { g_carbLogLevel = logLevel; });
        g_carbLogFn = g_carbLogging->log;
    }

    omni::log::addModulesChannels();
}

inline void deregisterLoggingForClient() noexcept
{
    omni::log::removeModulesChannels();

    if (g_carbLogging)
    {
        g_carbLogFn = nullptr;
        if (getFramework() && getFramework()->verifyInterface<ILogging>(g_carbLogging))
        {
            g_carbLogging->unregisterSource(g_carbClientName);
        }
        g_carbLogging = nullptr;
    }
}

struct StringToLogLevelMapping
{
    const cpp::zstring_view name;
    int32_t level;
};

const StringToLogLevelMapping kStringToLevelMappings[] = { { "verbose", kLevelVerbose },
                                                           { "info", kLevelInfo },
                                                           { "warning", kLevelWarn },
                                                           { "error", kLevelError },
                                                           { "fatal", kLevelFatal } };

const size_t kStringToLevelMappingsCount = CARB_COUNTOF(kStringToLevelMappings);

inline carb::cpp::zstring_view levelToString(int32_t level)
{
    // clamp the given level to the expected range.
    level = CARB_CLAMP(level, kLevelVerbose, kLevelFatal);

    for (size_t i = 0; i < CARB_COUNTOF(kStringToLevelMappings); i++)
    {
        if (level == kStringToLevelMappings[i].level)
            return kStringToLevelMappings[i].name;
    }

    // should never get here since we clamp the requested level above.
    CARB_ASSERT(false);
    return "";
}

inline int32_t stringToLevel(const char* levelString)
{
    const int32_t kFallbackLevel = kLevelFatal;
    if (!levelString)
        return kFallbackLevel;

    // Since our log level identifiers start with different characters, we're allowed to just compare the first one.
    // However, whether the need arises, it is worth making a score-based system, where full match will win over
    // partial match, if there are similarly starting log levels.
    int lcLevelChar = tolower((unsigned char)levelString[0]);
    for (size_t lm = 0; lm < kStringToLevelMappingsCount; ++lm)
    {
        int lcMappingChar = tolower((unsigned char)kStringToLevelMappings[lm].name[0]);
        if (lcLevelChar == lcMappingChar)
            return kStringToLevelMappings[lm].level;
    }

    // Ideally, this should never happen if level string is valid.
    CARB_ASSERT(false);
    CARB_LOG_ERROR("Unknown log level string: %s", levelString);
    return kFallbackLevel;
}

inline int32_t stringToLevel(carb::cpp::optional<std::string> levelString)
{
    return stringToLevel(levelString ? levelString->c_str() : nullptr);
}

} // namespace logging
} // namespace carb