This demo shows one C++ process loading a Rust shared library and sending OTLP
logs into ljd over both gRPC and HTTP, exercising the per-connection, reuse,
batch, and async send paths.
The path is:
C++ appliance -> liblogjet.so -> OTLP/gRPC or OTLP/HTTP -> ljd -> .logjet file -> ljx view
From the project root:
cargo build -p ljd -p ljx -p liblogjetYou also need g++ available locally because the demo compiles the C++
example on demand.
From this directory:
./run-demo.sh # 25 records per phase (default)
./run-demo.sh 100 # custom record countThe script:
- builds the example C++ logger
- starts two file-backed
ljdinstances: OTLP/gRPC on127.0.0.1:4317and OTLP/HTTP on127.0.0.1:4318 - loads
liblogjet.sothroughdlopen - runs the C++ logger once per transport, each exercising four send paths:
- per-connection (
lj_logger_log) - reuse (
lj_logger_log_reuse) - batch (
lj_logger_log_batch, many records in one request) - async (
lj_logger_log_asyncwithlj_logger_set_backpressure+lj_logger_flush, then prints the async counters)
- per-connection (
- opens
ljx viewon./logs/cpp-demo.logjet(gRPC), then on./logs/cpp-demo-http.logjet(HTTP)
- the demo runs both OTLP/gRPC and OTLP/HTTP; the C++ source picks the constructor (
lj_logger_new_grpc/lj_logger_new_http) from its 4th argument - reuse/batch/async work over both transports (HTTP uses a keep-alive connection pool)
- the FFI API is intentionally small: endpoint, service name, timestamp, severity, message body, and string attributes
- those key/value pairs become OTLP
LogRecord.attributes, which is the standard OTel metadata field for log records - if the appliance already has JSON metadata, the better long-term shape is to flatten that JSON into separate attributes where possible; a raw JSON blob can still be sent as one string attribute when needed
- the demo uses a local symlink named
liblogjet.sothat points at Cargo's built shared object