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
52 changes: 51 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ option(LOGIT_WITH_GZIP "Enable gzip via zlib" OFF)
option(LOGIT_WITH_ZSTD "Enable zstd" OFF)
option(LOGIT_WITH_FMT "Enable fmt support" OFF)
option(LOGIT_WITH_OTLP "Enable OTLP/HTTP log export via optional kurlyk dependency" OFF)
option(LOGIT_WITH_PROMETHEUS "Enable Prometheus text payload support" OFF)
option(LOGIT_WITH_PROMETHEUS_SERVER "Enable Prometheus HTTP server backend" OFF)
option(LOGIT_USE_SUBMODULES "Allow bundled optional dependency fallback" OFF)
option(LOGIT_WITH_SYSLOG "Enable POSIX syslog backend" ON)
option(LOGIT_WITH_WIN_EVENT_LOG "Enable Windows Event Log backend" ON)
Expand All @@ -21,7 +23,7 @@ option(LOGIT_USE_MPSC_RING "Enable lock-free TaskExecutor queue" ON)
option(LOGIT_ENABLE_DROP_OLDEST_SLOWPATH "Enable TaskExecutor DropOldest slow-path" ON)

if(NOT DEFINED CMAKE_CXX_STANDARD)
if(LOGIT_WITH_OTLP)
if(LOGIT_WITH_OTLP OR LOGIT_WITH_PROMETHEUS_SERVER)
set(CMAKE_CXX_STANDARD 17)
else()
set(CMAKE_CXX_STANDARD 11)
Expand Down Expand Up @@ -130,6 +132,54 @@ if(LOGIT_WITH_OTLP)
target_link_libraries(log-it-cpp INTERFACE kurlyk)
endif()

# ---------- Prometheus ----------
if(LOGIT_WITH_PROMETHEUS)
if(EMSCRIPTEN)
message(FATAL_ERROR "LOGIT_WITH_PROMETHEUS is not supported for Emscripten.")
endif()
target_compile_definitions(log-it-cpp INTERFACE LOGIT_WITH_PROMETHEUS=1)
endif()

if(LOGIT_WITH_PROMETHEUS_SERVER)
if(EMSCRIPTEN)
message(FATAL_ERROR "LOGIT_WITH_PROMETHEUS_SERVER is not supported for Emscripten.")
endif()

target_compile_definitions(log-it-cpp INTERFACE ASIO_STANDALONE)

# Prefer standalone external/Simple-Web-Server
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/Simple-Web-Server/server_http.hpp")
target_include_directories(log-it-cpp INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external/Simple-Web-Server>
)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/Simple-Web-Server/../asio/include/asio.hpp")
target_include_directories(log-it-cpp INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external/asio/include>
)
endif()
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/external/Simple-Web-Server/server_http.hpp")
target_include_directories(log-it-cpp INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/external/Simple-Web-Server>
)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/external/asio/include/asio.hpp")
target_include_directories(log-it-cpp INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/external/kurlyk/external/asio/include>
)
endif()
else()
message(FATAL_ERROR "Simple-Web-Server not found. Add it as external/Simple-Web-Server or enable/provide kurlyk submodule.")
endif()

if(NOT LOGIT_WITH_PROMETHEUS)
set(LOGIT_WITH_PROMETHEUS ON)
target_compile_definitions(log-it-cpp INTERFACE LOGIT_WITH_PROMETHEUS=1)
endif()
target_compile_definitions(log-it-cpp INTERFACE LOGIT_WITH_PROMETHEUS_SERVER=1)
if(WIN32)
target_link_libraries(log-it-cpp INTERFACE ws2_32 wsock32)
endif()
endif()

# ---------- GZIP (zlib) ----------
if(LOGIT_WITH_GZIP)
if(NOT TARGET ZLIB::ZLIB)
Expand Down
116 changes: 116 additions & 0 deletions docs/PrometheusLogger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Prometheus Logger

## Overview

LogIt++ provides two Prometheus backends for exposing internal log metrics in the
[Prometheus text exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/):

- **PrometheusPayloadLogger** -- callback-based; delivers the serialized payload to a
user-provided function. Useful when you have your own HTTP server or want to push to
a Prometheus Pushgateway.

- **PrometheusHttpServerLogger** -- embedded HTTP server; serves `/metrics` on a
configurable port using Simple-Web-Server. Ideal for simple services without a
separate metrics endpoint.

## Built-in Metrics

| Metric | Type | Description |
|--------|------|-------------|
| `logit_log_records_total` | counter | Total log records processed |
| `logit_dropped_logs_total` | counter | Dropped log records |
| `logit_failed_exports_total` | counter | Failed export/callback attempts |
| `logit_last_log_timestamp_ms` | gauge | Timestamp of last log (ms) |
| `logit_time_since_last_log_ms` | gauge | Time since last log (ms) |
| `logit_build_info` | gauge | Build info (value=1, labels: version, compiler) |

The `metric_prefix` config option (default: `logit_`) is applied to all metric names.

## CMake Options

```cmake
option(LOGIT_WITH_PROMETHEUS "Enable Prometheus text payload support" OFF)
option(LOGIT_WITH_PROMETHEUS_SERVER "Enable Prometheus HTTP server backend" OFF)
```

`LOGIT_WITH_PROMETHEUS_SERVER` implies `LOGIT_WITH_PROMETHEUS` and requires C++17
(Simple-Web-Server dependency).

## Usage: PrometheusPayloadLogger

```cpp
#include <logit.hpp>

logit::PrometheusPayloadLogger::Config config;
config.format.metric_prefix = "myapp_";
config.emit_on_wait = true;
config.on_payload = [](std::string payload) {
// Send to your HTTP endpoint or Pushgateway
};

LOGIT_ADD_LOGGER(
logit::PrometheusPayloadLogger,
(config),
logit::SimpleLogFormatter,
("%v")
);

LOGIT_INFO("Application started");
LOGIT_WAIT(); // triggers on_payload with current metrics
```

## Usage: PrometheusHttpServerLogger

```cpp
#include <logit.hpp>

logit::PrometheusHttpServerLogger::Config config;
config.port = 9090;
config.path = "/metrics";

LOGIT_ADD_LOGGER(
logit::PrometheusHttpServerLogger,
(config),
logit::SimpleLogFormatter,
("%v")
);

LOGIT_INFO("Server started");
// Scrape http://localhost:9090/metrics
```

## Custom Metrics

Use the `on_collect` callback to add application-specific metrics on each scrape:

```cpp
config.on_collect = [](std::vector<logit::PrometheusMetricFamily>& families) {
logit::PrometheusMetricFamily mf;
mf.name = "myapp_queue_size";
mf.help = "Current queue depth";
mf.type = logit::PrometheusMetricType::Gauge;
logit::PrometheusSample s;
s.name = "myapp_queue_size";
s.value = get_queue_depth();
mf.samples.push_back(s);
families.push_back(mf);
};
```

## Prometheus Scrape Config

```yaml
scrape_configs:
- job_name: 'logit-app'
scrape_interval: 15s
static_configs:
- targets: ['localhost:9090']
metrics_path: /metrics
```

## Limitations

- Text exposition format only (no protobuf, no OpenMetrics `# EOF`).
- No histograms or summaries -- use `on_collect` for custom metric types.
- No TLS or authentication on the HTTP server.
- No metric renaming conflicts resolution -- user must ensure unique names.
35 changes: 35 additions & 0 deletions examples/example_logit_prometheus_payload.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <logit.hpp>

int main() {
#ifndef LOGIT_WITH_PROMETHEUS
LOGIT_ADD_CONSOLE_DEFAULT();
LOGIT_WARN("Prometheus payload example requires LOGIT_WITH_PROMETHEUS=ON");
LOGIT_WAIT();
return 0;
#else
logit::PrometheusPayloadLogger::Config config;
config.format.metric_prefix = "myapp_";
config.format.include_build_info = true;
config.emit_on_wait = true;
config.on_payload = [](std::string payload) {
// In a real application, send payload to your Prometheus push gateway
// or expose it via your own HTTP endpoint.
(void)payload;
};

LOGIT_ADD_LOGGER(
logit::PrometheusPayloadLogger,
(config),
logit::SimpleLogFormatter,
("%v")
);

LOGIT_INFO("Prometheus payload logger started");
LOGIT_WARN("Example warning message");
LOGIT_ERROR("Example error message");

LOGIT_WAIT();
LOGIT_SHUTDOWN();
return 0;
#endif
}
47 changes: 47 additions & 0 deletions examples/example_logit_prometheus_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include <logit.hpp>

int main() {
#ifndef LOGIT_WITH_PROMETHEUS_SERVER
LOGIT_ADD_CONSOLE_DEFAULT();
LOGIT_WARN("Prometheus server example requires LOGIT_WITH_PROMETHEUS_SERVER=ON");
LOGIT_WAIT();
return 0;
#else
logit::PrometheusHttpServerLogger::Config config;
config.port = 9090;
config.path = "/metrics";
config.format.metric_prefix = "myapp_";
config.format.include_build_info = true;

// Optional: add custom metrics on each scrape
config.on_collect = [](std::vector<logit::PrometheusMetricFamily>& families) {
logit::PrometheusMetricFamily mf;
mf.name = "myapp_uptime_seconds";
mf.help = "Application uptime in seconds";
mf.type = logit::PrometheusMetricType::Gauge;
logit::PrometheusSample s;
s.name = "myapp_uptime_seconds";
s.value = 42.0;
mf.samples.push_back(s);
families.push_back(mf);
};

LOGIT_ADD_LOGGER(
logit::PrometheusHttpServerLogger,
(config),
logit::SimpleLogFormatter,
("%v")
);

LOGIT_INFO("Prometheus HTTP server started on port 9090");
LOGIT_WARN("Scrape metrics at http://localhost:9090/metrics");

for (int i = 0; i < 5; ++i) {
LOGIT_INFO("Logging iteration %d", (i + 1));
std::this_thread::sleep_for(std::chrono::seconds(1));
}

LOGIT_SHUTDOWN();
return 0;
#endif
}
7 changes: 7 additions & 0 deletions include/logit_cpp/logit/loggers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,11 @@
#include "loggers/OtlpPayloadLogger.hpp"
#endif

#ifdef LOGIT_WITH_PROMETHEUS
#include "loggers/PrometheusPayloadLogger.hpp"
#endif
#ifdef LOGIT_WITH_PROMETHEUS_SERVER
#include "loggers/PrometheusHttpServerLogger.hpp"
#endif

#endif // _LOGIT_LOGGERS_HPP_INCLUDED
Loading
Loading