tracing-better-stack
A tracing-subscriber
layer for sending logs to Better Stack (formerly Logtail) via their HTTP API.
Features
- 🚀 Asynchronous & Non-blocking: Logs are sent in background without blocking your application
- 📦 Automatic Batching: Efficiently batches logs to reduce HTTP overhead
- 🔄 Retry Logic: Automatic retry with exponential backoff for failed requests
- 🎯 Structured Logging: Full support for structured fields and span context
- 🔒 Feature Flags: Opt-in to only the serialization format you need
- 🛡️ Production Ready: Comprehensive error handling and graceful degradation
- 🗜️ MessagePack Support: Choose between JSON and MessagePack serialization formats
- ⚡ Lazy Initialization: Handles tokio runtime initialization gracefully
Installation
Add this to your Cargo.toml
:
[]
= { = "1", = ["rt", "macros"] }
= "0.1"
= "0.3"
# Default: uses MessagePack for better performance
= "0.1"
# Or explicitly choose MessagePack:
= { = "0.1", = ["message_pack"] }
# Or use JSON format:
= { = "0.1", = false, = ["json"] }
Serialization Formats
Better Stack supports both JSON and MessagePack formats for log ingestion. This crate provides both options via mutually exclusive feature flags:
- MessagePack (default): More compact and efficient binary format
- JSON: Human-readable text format, useful for debugging
Using MessagePack (Default)
# This is the default, no special configuration needed
= "0.1"
Using JSON
= { = "0.1", = false, = ["json"] }
Both formats send the same structured data to Better Stack, so you can switch between them without changing your logging code.
Quick Start
use ;
use ;
use *;
async
Configuration
The layer can be configured using the builder pattern:
use Duration;
use ;
// Both ingesting host and source token are required
// Better Stack provides unique hosts for each source
let layer = new;
Advanced Usage
With Console Output
Combine with other layers for both console and Better Stack logging:
use *;
use ;
registry
.with
.with
.init;
With Environment Filter
Control log levels using environment variables:
use ;
use ;
registry
.with
.with
.init;
Structured Logging
Take advantage of tracing's structured logging capabilities:
use ;
// Log with structured fields
info!;
// Use spans for context
let span = span!;
let _enter = span.enter;
info!;
// All logs within this span will include the span's fields
How It Works
- Event Collection: The layer captures tracing events and converts them to internal
LogEvent
structures - Batching: Events are collected into batches (up to 100 events or 5 seconds)
- Serialization: Batches are serialized to either MessagePack (default) or JSON format
- Async Sending: Batches are sent asynchronously via HTTPS to Better Stack
- Retry Logic: Failed requests are retried with exponential backoff
- Graceful Degradation: If Better Stack is unreachable, logs are dropped without affecting your application
Log Format
Logs are sent to Better Stack with the following structure:
This structure is preserved in both JSON and MessagePack formats.
Performance Considerations
- MessagePack vs JSON: MessagePack is ~30-50% more compact than JSON, reducing network overhead
- Zero-cost when unreachable: If Better Stack is down, the layer fails open with minimal overhead
- Efficient batching: Reduces network calls by batching multiple events
- Non-blocking: All network I/O happens in background tasks
- Lazy initialization: Tokio runtime is only accessed when needed
Need synchronous flushing?
Add a delay before program exit to ensure final logs are sent:
// At the end of main()
sleep.await;
Examples
Check out the examples directory for more usage patterns:
- basic.rs - Simple usage with environment variables
- with_filters.rs - Using with EnvFilter
- custom_config.rs - Advanced configuration
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.