Log.h#
Fully qualified name: carb/logging/Log.h
File members: carb/logging/Log.h
// SPDX-FileCopyrightText: Copyright (c) 2018-2026 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 "LogChannel.h"
#include "../../omni/log/ILog.h"
#include "../cpp/Optional.h"
#include "../cpp/ZStringView.h"
#include "../cpp/StringView.h"
#include "../cpp/UnboundedString.h"
#include "../extras/StringUtils.h"
#include "../cpp/Chrono.h"
#include "../detail/PushBadMacros.h"
#include <algorithm>
#include <atomic>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <utility>
#include "../detail/PopBadMacros.h"
#ifndef CARB_LOG_REQUIRE_CHANNEL
# define CARB_LOG_REQUIRE_CHANNEL 0
#endif
// example-begin Log levels
namespace carb::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 carb::logging
// 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;
CARB_WEAKLINK carb::logging::detail::DefaultLogChannelData g_defaultLogChannel;
#if CARB_COMPILER_GNUC || defined(DOXYGEN_BUILD)
# define CARB_CHECK_FORMAT(fmtOrChannel, ...) \
do \
{ \
if (false) \
::carb::logging::detail::checkFormat(fmtOrChannel, ##__VA_ARGS__); \
} while (0)
#else
// Windows cannot effectively evaluate this
# define CARB_CHECK_FORMAT(fmtOrChannel, ...) static_cast<void>(0)
#endif
#if CARB_LOG_REQUIRE_CHANNEL || defined(DOXYGEN_BUILD)
# define CARB_LOG_CHECK_CHANNEL(fmtOrChannel) \
static_assert(std::is_convertible_v<decltype(fmtOrChannel), const ::carb::logging::detail::LogChannelData&>, \
"Specifying a channel is required due to CARB_LOG_REQUIRE_CHANNEL=1")
#else
# define CARB_LOG_CHECK_CHANNEL(...) static_cast<void>(0)
#endif
#define CARB_LOG(level, fmtOrChannel, ...) \
do \
{ \
int32_t lvl = (level); \
const auto& foc = (fmtOrChannel); \
CARB_CHECK_FORMAT(foc, ##__VA_ARGS__); \
CARB_LOG_CHECK_CHANNEL(foc); \
if (::carb::logging::detail::checkLevel(lvl, foc)) \
::carb::logging::detail::log(__FILE__, __func__, __LINE__, lvl, foc, ##__VA_ARGS__); \
} while (0)
#define CARB_LOG_VERBOSE(fmtOrChannel, ...) CARB_LOG(carb::logging::kLevelVerbose, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_INFO(fmtOrChannel, ...) CARB_LOG(carb::logging::kLevelInfo, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_WARN(fmtOrChannel, ...) CARB_LOG(carb::logging::kLevelWarn, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ERROR(fmtOrChannel, ...) CARB_LOG(carb::logging::kLevelError, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_FATAL(fmtOrChannel, ...) CARB_LOG(carb::logging::kLevelFatal, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ONCE(level, fmtOrChannel, ...) \
do \
{ \
int32_t lvl = (level); \
const auto& foc = (fmtOrChannel); \
CARB_CHECK_FORMAT(foc, ##__VA_ARGS__); \
CARB_LOG_CHECK_CHANNEL(foc); \
if (::carb::logging::detail::checkLevel(lvl, foc)) \
{ \
[[maybe_unused]] static bool CARB_JOIN(logged_, __LINE__) = \
(::carb::logging::detail::log(__FILE__, __func__, __LINE__, lvl, foc, ##__VA_ARGS__), true); \
} \
} while (0)
#define CARB_LOG_VERBOSE_ONCE(fmtOrChannel, ...) \
CARB_LOG_ONCE(carb::logging::kLevelVerbose, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_INFO_ONCE(fmtOrChannel, ...) CARB_LOG_ONCE(carb::logging::kLevelInfo, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_WARN_ONCE(fmtOrChannel, ...) CARB_LOG_ONCE(carb::logging::kLevelWarn, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ERROR_ONCE(fmtOrChannel, ...) CARB_LOG_ONCE(carb::logging::kLevelError, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_FATAL_ONCE(fmtOrChannel, ...) CARB_LOG_ONCE(carb::logging::kLevelFatal, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_DECLARE_GATE(gateName) static std::atomic_bool CARB_JOIN(gate_, gateName) = false
#define CARB_LOG_ONCE_RESETTABLE(level, gateName, fmtOrChannel, ...) \
do \
{ \
int32_t lvl = (level); \
const auto& foc = (fmtOrChannel); \
CARB_CHECK_FORMAT(foc, ##__VA_ARGS__); \
CARB_LOG_CHECK_CHANNEL(foc); \
if (::carb::logging::detail::checkLevel(lvl, foc) && !CARB_JOIN(gate_, gateName) && \
!CARB_JOIN(gate_, gateName).exchange(true, std::memory_order_relaxed)) \
{ \
::carb::logging::detail::log(__FILE__, __func__, __LINE__, lvl, foc, ##__VA_ARGS__); \
} \
} while (0)
#define CARB_LOG_RESET_GATE(gateName) CARB_JOIN(gate_, gateName).store(false)
#define CARB_LOG_VERBOSE_ONCE_RESETTABLE(gateName, fmtOrChannel, ...) \
CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelVerbose, gateName, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_INFO_ONCE_RESETTABLE(gateName, fmtOrChannel, ...) \
CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelInfo, gateName, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_WARN_ONCE_RESETTABLE(gateName, fmtOrChannel, ...) \
CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelWarn, gateName, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ERROR_ONCE_RESETTABLE(gateName, fmtOrChannel, ...) \
CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelError, gateName, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_FATAL_ONCE_RESETTABLE(gateName, fmtOrChannel, ...) \
CARB_LOG_ONCE_RESETTABLE(carb::logging::kLevelFatal, gateName, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_EVERY_N_MS(level, intervalMs, fmtOrChannel, ...) \
do \
{ \
int32_t lvl = (level); \
const auto& foc = (fmtOrChannel); \
CARB_CHECK_FORMAT(foc, ##__VA_ARGS__); \
CARB_LOG_CHECK_CHANNEL(foc); \
static std::atomic<::carb::logging::detail::Clock::time_point> CARB_JOIN(lastLogTime_, __LINE__){ {} }; \
if (::carb::logging::detail::checkLevel(lvl, foc) && \
::carb::logging::detail::updateInterval(CARB_JOIN(lastLogTime_, __LINE__), (intervalMs))) \
{ \
::carb::logging::detail::log(__FILE__, __func__, __LINE__, lvl, foc, ##__VA_ARGS__); \
} \
} while (0)
#define CARB_LOG_VERBOSE_EVERY_N_MS(intervalMs, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_MS(carb::logging::kLevelVerbose, intervalMs, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_INFO_EVERY_N_MS(intervalMs, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_MS(carb::logging::kLevelInfo, intervalMs, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_WARN_EVERY_N_MS(intervalMs, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_MS(carb::logging::kLevelWarn, intervalMs, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ERROR_EVERY_N_MS(intervalMs, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_MS(carb::logging::kLevelError, intervalMs, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_FATAL_EVERY_N_MS(intervalMs, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_MS(carb::logging::kLevelFatal, intervalMs, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_EVERY_N_CALLS(level, callCount, fmtOrChannel, ...) \
do \
{ \
int32_t lvl = (level); \
const auto& foc = (fmtOrChannel); \
CARB_CHECK_FORMAT(foc, ##__VA_ARGS__); \
CARB_LOG_CHECK_CHANNEL(foc); \
static std::atomic_uint32_t CARB_JOIN(callCounter_, __LINE__){ 0 }; \
const uint32_t count = (callCount); \
if (::carb::logging::detail::checkLevel(lvl, foc) && \
(count == 0 || ::carb::logging::detail::updateCounter(CARB_JOIN(callCounter_, __LINE__), count))) \
{ \
::carb::logging::detail::log(__FILE__, __func__, __LINE__, lvl, foc, ##__VA_ARGS__); \
} \
} while (0)
#define CARB_LOG_VERBOSE_EVERY_N_CALLS(callCount, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelVerbose, callCount, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_INFO_EVERY_N_CALLS(callCount, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelInfo, callCount, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_WARN_EVERY_N_CALLS(callCount, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelWarn, callCount, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_ERROR_EVERY_N_CALLS(callCount, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelError, callCount, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_FATAL_EVERY_N_CALLS(callCount, fmtOrChannel, ...) \
CARB_LOG_EVERY_N_CALLS(carb::logging::kLevelFatal, callCount, fmtOrChannel, ##__VA_ARGS__)
#define CARB_LOG_GLOBALS()
namespace carb::logging
{
namespace detail
{
inline bool checkLevel(int32_t level, const char*)
{
return g_carbLogFn != nullptr && g_carbLogLevel <= level;
}
inline bool checkLevel(int32_t level, const DefaultLogChannelData&)
{
return g_carbLogFn != nullptr && g_carbLogLevel <= level;
}
inline bool checkLevel(int32_t level, const LogChannelData& channel)
{
return g_carbLogFn != nullptr && channel.level <= level;
}
void checkFormat(const char* fmt, ...) CARB_PRINTF_FUNCTION(1, 2);
void checkFormat(const LogChannelData&, const char*, ...) CARB_PRINTF_FUNCTION(2, 3);
template <typename... Args>
void log(const char* file,
const char* func,
int line,
int32_t level,
const LogChannelData& channel,
const char* fmt,
Args... args)
{
LogFn fn = g_carbLogFn; // use a temporary to negate the format checking on g_carbLogFn
fn(channel.name, level, file, func, line, fmt, args...);
}
template <typename... Args>
void log(const char* file, const char* func, int line, int32_t level, const DefaultLogChannelData&, const char* fmt, Args... args)
{
LogFn fn = g_carbLogFn; // use a temporary to negate the format checking on g_carbLogFn
fn(g_carbClientName.c_str(), level, file, func, line, fmt, args...);
}
template <typename... Args>
void log(const char* file, const char* func, int line, int32_t level, const char* fmt, Args... args)
{
LogFn fn = g_carbLogFn; // use a temporary to negate the format checking on g_carbLogFn
fn(g_carbClientName.c_str(), level, file, func, line, fmt, args...);
}
using Clock = ::std::chrono::steady_clock;
inline bool updateInterval(std::atomic<Clock::time_point>& tracker, std::chrono::milliseconds::rep intervalMs) noexcept
{
using namespace std::chrono;
const milliseconds interval(intervalMs);
auto now = Clock::now();
auto val = tracker.load(std::memory_order_acquire);
auto diff = (now - val);
while (diff < milliseconds(0) || diff > interval)
{
if (tracker.compare_exchange_weak(val, now, std::memory_order_release, std::memory_order_relaxed))
return true;
// val reloaded by failed cmpxchg
diff = (now - val);
}
return false;
}
inline bool updateCounter(std::atomic_uint32_t& counter, uint32_t callCount) noexcept
{
return (counter.fetch_add(1, std::memory_order_relaxed) % callCount) == 0;
}
class LogChannelRegistrar
{
public:
LogChannelRegistrar(LogChannelData* data) noexcept
{
data->next = std::exchange(logChannelHead(), data);
if (logChannelsRegistered())
{
#if CARB_VERSION_ATLEAST(carb_logging_ILogging, 1, 6)
if (g_carbLogging)
g_carbLogging->addChannel(data->name, &data->level, data->description);
#else
if (auto log = omniGetLogWithoutAcquire())
log->addChannel(data->name, cpp::bit_cast<omni::log::Level*>(&data->level), data->description);
#endif
}
}
};
} // namespace detail
inline ILogging* getLogging()
{
return g_carbLogging;
}
inline void registerLoggingForClient() noexcept
{
g_carbLogging = getFramework()->tryAcquireInterface<ILogging>();
if (g_carbLogging)
{
g_carbLogging->registerSource(g_carbClientName.c_str(), [](int32_t logLevel) { g_carbLogLevel = logLevel; });
g_carbLogFn = g_carbLogging->log;
#if CARB_VERSION_ATLEAST(carb_logging_ILogging, 1, 6)
logging::detail::logChannelsRegistered() = true;
for (auto channel = logging::detail::logChannelHead(); channel; channel = channel->next)
g_carbLogging->addChannel(channel->name, &channel->level, channel->description);
#endif
}
#if !CARB_VERSION_ATLEAST(carb_logging_ILogging, 1, 6)
omni::log::addModulesChannels();
#endif
}
inline void deregisterLoggingForClient() noexcept
{
#if !CARB_VERSION_ATLEAST(carb_logging_ILogging, 1, 6)
omni::log::removeModulesChannels();
#endif
if (g_carbLogging)
{
g_carbLogFn = nullptr;
if (getFramework() && getFramework()->verifyInterface<ILogging>(g_carbLogging))
{
g_carbLogging->unregisterSource(g_carbClientName.c_str());
#if CARB_VERSION_ATLEAST(carb_logging_ILogging, 1, 6)
logging::detail::logChannelsRegistered() = false;
for (auto channel = logging::detail::logChannelHead(); channel; channel = channel->next)
g_carbLogging->removeChannel(channel->name, &channel->level);
#endif
}
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 = std::size(kStringToLevelMappings);
inline int32_t stringToLevel(carb::cpp::string_view levelString)
{
const int32_t kFallbackLevel = kLevelFatal;
if (levelString.empty())
return kFallbackLevel;
// Find longest common prefix with any mapping
int32_t bestLevel = kLevelFatal;
size_t bestLen = 0;
for (const auto& mapping : kStringToLevelMappings)
{
size_t commonLen = 0;
size_t maxLen = carb_min(levelString.size(), mapping.name.size());
while (commonLen < maxLen)
{
auto c1 = std::tolower(static_cast<unsigned char>(levelString[commonLen]));
auto c2 = std::tolower(static_cast<unsigned char>(mapping.name[commonLen]));
if (c1 != c2)
break;
++commonLen;
}
if (commonLen > bestLen)
{
bestLen = commonLen;
bestLevel = mapping.level;
}
if (commonLen == levelString.size() && commonLen == mapping.name.size())
{
return mapping.level;
}
}
if (bestLen > 0)
return bestLevel;
// Ideally, this should never happen if level string is valid.
CARB_LOG_ERROR(g_defaultLogChannel, "Unknown log level string: %.*s", static_cast<int>(levelString.size()),
levelString.data());
return kFallbackLevel;
}
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 {};
}
CARB_DEPRECATED("Use stringToLevel(carb::cpp::string_view) instead")
inline int32_t stringToLevel(carb::cpp::unbounded_string levelString)
{
return stringToLevel(carb::cpp::string_view(carb::cpp::unsafe_length, levelString.data()));
}
} // namespace carb::logging