Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ try {
> 23:59:59.128 | An example runtime error
```

- **Macro-Based Logging**:
- **Macro-Based Logging**:

Easily log variables and messages using macros. Simply choose the appropriate macro and pass variables or arguments to it.

Expand All @@ -51,6 +51,25 @@ auto now = std::chrono::system_clock::now();
LOGIT_PRINT_INFO("TimePoint example: ", now);
```

- **Log Filters and Throttling**:

Reduce noise from repetitive messages with macros like `LOGIT_WARN_ONCE`, `LOGIT_INFO_EVERY_N`, and `LOGIT_ERROR_THROTTLE`.

```cpp
for (int i = 0; i < 1000; ++i) {
LOGIT_INFO_EVERY_N(100, "heartbeat");
}
```

- **Tagged Logging**:

Attach simple key-value attributes for easier filtering in log aggregators.

```cpp
LOGIT_INFO_TAG(({{"order_id", 123}, {"side", "BUY"}}), "sent order");
// Output: [info] sent order order_id=123 side=BUY
```

- **Rotating File Logs**:

Automatic file rotation based on size with optional asynchronous compression using gzip or zstd.
Expand Down
57 changes: 57 additions & 0 deletions include/logit_cpp/logit/LogMacros.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#define LOGIT_LEVEL_WARN 3
#define LOGIT_LEVEL_ERROR 4
#define LOGIT_LEVEL_FATAL 5
#define LOGIT_CONCAT_IMPL(x, y) x##y
#define LOGIT_CONCAT(x, y) LOGIT_CONCAT_IMPL(x, y)

#ifdef _LOGIT_ENUMS_HPP_INCLUDED
static_assert(LOGIT_LEVEL_TRACE == static_cast<int>(logit::LogLevel::LOG_LVL_TRACE));
Expand Down Expand Up @@ -615,6 +617,61 @@ static_assert(LOGIT_LEVEL_FATAL == static_cast<int>(logit::LogLevel::LOG_LVL_FAT
#define LOGIT_PRINTF_FATAL_IF(condition, fmt, ...) do { } while (0)
#endif

#define LOGIT_ONCE(level, ...) \
do { \
static bool LOGIT_CONCAT(_logit_once_, __LINE__) = false; \
if (!LOGIT_CONCAT(_logit_once_, __LINE__)) { \
LOGIT_CONCAT(_logit_once_, __LINE__) = true; \
LOGIT_LOG_AND_RETURN(level, {}, #__VA_ARGS__, __VA_ARGS__); \
} \
} while (0)

#define LOGIT_EVERY_N(level, n, ...) \
do { \
static unsigned LOGIT_CONCAT(_logit_count_, __LINE__) = 0; \
if (++LOGIT_CONCAT(_logit_count_, __LINE__) % (n) == 0) { \
LOGIT_LOG_AND_RETURN(level, {}, #__VA_ARGS__, __VA_ARGS__); \
} \
} while (0)

#define LOGIT_THROTTLE(level, period_ms, ...) \
do { \
static int64_t LOGIT_CONCAT(_logit_last_, __LINE__) = 0; \
int64_t _logit_now = LOGIT_CURRENT_TIMESTAMP_MS(); \
if (_logit_now - LOGIT_CONCAT(_logit_last_, __LINE__) >= (period_ms)) { \
LOGIT_CONCAT(_logit_last_, __LINE__) = _logit_now; \
LOGIT_LOG_AND_RETURN(level, {}, #__VA_ARGS__, __VA_ARGS__); \
} \
} while (0)

#define LOGIT_TRACE_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_TRACE, __VA_ARGS__)
#define LOGIT_DEBUG_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_DEBUG, __VA_ARGS__)
#define LOGIT_INFO_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_INFO, __VA_ARGS__)
#define LOGIT_WARN_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_WARN, __VA_ARGS__)
#define LOGIT_ERROR_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_ERROR, __VA_ARGS__)
#define LOGIT_FATAL_ONCE(...) LOGIT_ONCE(logit::LogLevel::LOG_LVL_FATAL, __VA_ARGS__)

#define LOGIT_TRACE_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_TRACE, n, __VA_ARGS__)
#define LOGIT_DEBUG_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_DEBUG, n, __VA_ARGS__)
#define LOGIT_INFO_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_INFO, n, __VA_ARGS__)
#define LOGIT_WARN_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_WARN, n, __VA_ARGS__)
#define LOGIT_ERROR_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_ERROR, n, __VA_ARGS__)
#define LOGIT_FATAL_EVERY_N(n, ...) LOGIT_EVERY_N(logit::LogLevel::LOG_LVL_FATAL, n, __VA_ARGS__)

#define LOGIT_TRACE_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_TRACE, p, __VA_ARGS__)
#define LOGIT_DEBUG_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_DEBUG, p, __VA_ARGS__)
#define LOGIT_INFO_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_INFO, p, __VA_ARGS__)
#define LOGIT_WARN_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_WARN, p, __VA_ARGS__)
#define LOGIT_ERROR_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_ERROR, p, __VA_ARGS__)
#define LOGIT_FATAL_THROTTLE(p, ...) LOGIT_THROTTLE(logit::LogLevel::LOG_LVL_FATAL, p, __VA_ARGS__)

#define LOGIT_TRACE_TAG(tags, msg) LOGIT_PRINT_TRACE((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))
#define LOGIT_DEBUG_TAG(tags, msg) LOGIT_PRINT_DEBUG((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))
#define LOGIT_INFO_TAG(tags, msg) LOGIT_PRINT_INFO((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))
#define LOGIT_WARN_TAG(tags, msg) LOGIT_PRINT_WARN((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))
#define LOGIT_ERROR_TAG(tags, msg) LOGIT_PRINT_ERROR((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))
#define LOGIT_FATAL_TAG(tags, msg) LOGIT_PRINT_FATAL((std::string(msg) + logit::detail::format_tags(logit::detail::make_tags(tags))))

//------------------------------------------------------------------------------
// Shorter versions of the macros when LOGIT_SHORT_NAME is defined
#if defined(LOGIT_SHORT_NAME)
Expand Down
1 change: 1 addition & 0 deletions include/logit_cpp/logit/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@
#include "utils/encoding_utils.hpp"
#include "utils/path_utils.hpp"
#include "utils/LogRecord.hpp"
#include "utils/tag_utils.hpp"

#endif // _LOGIT_UTILS_HPP_INCLUDED
33 changes: 33 additions & 0 deletions include/logit_cpp/logit/utils/tag_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once
#ifndef _LOGIT_TAG_UTILS_HPP_INCLUDED
#define _LOGIT_TAG_UTILS_HPP_INCLUDED

#include <sstream>
#include <string>

namespace logit {
namespace detail {

/// \brief Formats tags as key=value pairs separated by spaces.
/// \tparam Tags Container of pairs convertible to output stream.
/// \param tags Collection of tag key-value pairs.
/// \return String in format " key=value ..." or empty string when no tags.
template <typename Tags>
inline std::string format_tags(const Tags& tags) {
std::ostringstream oss;
for (const auto& kv : tags) {
oss << ' ' << kv.first << '=' << kv.second;
}
return oss.str();
}

/// \brief Helper to allow passing initializer lists to macros.
template <typename K, typename V>
inline std::initializer_list<std::pair<K, V>> make_tags(std::initializer_list<std::pair<K, V>> tags) {
return tags;
}

} // namespace detail
} // namespace logit

#endif // _LOGIT_TAG_UTILS_HPP_INCLUDED
30 changes: 30 additions & 0 deletions tests/log_filters_tags_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#define LOGIT_COMPILED_LEVEL LOGIT_LEVEL_TRACE
#include <LogIt.hpp>
#include <thread>
#include <chrono>

// Basic smoke test for frequency control macros.

int main() {
int once_counter = 0;
for (int i = 0; i < 10; ++i) {
LOGIT_WARN_ONCE(once_counter++);
}
if (once_counter != 1) return 1;

int every_n_counter = 0;
for (int i = 0; i < 10; ++i) {
LOGIT_INFO_EVERY_N(3, every_n_counter++);
}
if (every_n_counter != 3) return 1;

int throttle_counter = 0;
auto throttle_call = [&]() { LOGIT_ERROR_THROTTLE(50, throttle_counter++); };
throttle_call();
throttle_call();
std::this_thread::sleep_for(std::chrono::milliseconds(60));
throttle_call();
if (throttle_counter != 2) return 1;

return 0;
}
Loading