betterstack-tracing
A tracing layer for sending logs to Betterstack.
This crate provides a Rust implementation inspired by the slog-betterstack Go library, adapted for Rust's tracing ecosystem.
Features
- Non-blocking async log sending - Logs are sent in the background without blocking your application
- Automatic batching - Configurable size and time-based batching for efficient log delivery
- Gzip compression - Automatically compresses batches for reduced bandwidth usage
- Size limit validation - Enforces Betterstack API limits (1 MiB per log, 10 MiB per batch)
- Span context tracking - Automatically includes parent span information for distributed tracing
- Configurable error handling - Optional error callbacks for monitoring send failures
- Connection pooling - Reuses HTTP connections for better performance
- Type-safe configuration - Builder pattern with compile-time validation
Installation
Add this to your Cargo.toml:
[]
= "0.1"
= "0.1"
= "0.3"
= { = "1", = ["full"] }
Quick Start
use BetterstackLayer;
use *;
async
Configuration
Builder Options
use Duration;
let config = builder
// Optional: Custom endpoint (default: https://in.logs.betterstack.com/)
.endpoint
// Optional: HTTP request timeout (default: 10s)
.timeout
// Optional: Batch size (default: 10 logs)
.batch_size
// Optional: Batch delay (default: 2s)
.batch_delay
// Optional: Channel capacity (default: 1000)
.channel_capacity
// Optional: Include span context (default: true)
.include_span_context
// Optional: Custom logger name (default: "tracing-betterstack")
.logger_name
// Optional: Custom logger version (default: crate version)
.logger_version
// Optional: Error callback
.on_error
.build
.expect;
let layer = new;
Batching Strategy
Logs are automatically batched and sent when either:
- The batch reaches
batch_sizelogs, OR batch_delaytime has elapsed since the last send
This provides a good balance between latency and throughput.
Span Context
When include_span_context is enabled (default), the layer automatically captures the current span hierarchy and includes it in the log payload:
let span = info_span!;
let _enter = span.enter;
info!;
// This log will include the "http_request" span context
Examples
Basic Usage
use BetterstackLayer;
use *;
async
With Console Output
Combine with the fmt layer to log to both console and Betterstack:
use BetterstackLayer;
use *;
async
With Spans
use BetterstackLayer;
use *;
async
See the examples/ directory for more complete examples.
Payload Format
Logs are sent to Betterstack in the following JSON format:
All custom fields and span information are included at the root level of the JSON object, not nested under an "extra" key, in compliance with the Betterstack logs API.
Performance
- Non-blocking: Log calls return immediately, sending happens in background
- Batched: Reduces HTTP overhead by sending multiple logs per request
- Backpressure: Bounded channel prevents memory growth under load
- Connection pooling: Reuses HTTP connections for better performance
If the log channel is full, new logs are dropped to prevent blocking your application.
Error Handling
By default, send errors are silently ignored. You can provide an error callback:
let config = builder
.on_error
.build
.expect;
Size Limits
The crate automatically enforces Betterstack API size limits:
- Individual logs: Maximum 1 MiB per log record
- Logs exceeding this limit are dropped with a warning
- Logs exceeding 100 KiB generate a debug message
- Batches: Maximum 10 MiB uncompressed per batch
- Batches are automatically compressed with gzip (typically 3-4x compression ratio)
- Oversized batches result in an error
These limits help prevent rejected requests and ensure reliable log delivery.
Graceful Shutdown
The layer automatically flushes pending logs when dropped, with a 5-second timeout. For explicit control:
layer.flush.await;
License
Licensed under the MIT license.
Acknowledgments
- Inspired by slog-betterstack by @samber
- Built for the tracing ecosystem