Alternatives

Use older alternatives to tracing

tracing is now the "go-to" crate for logging, but log, slog and log4rs are still in extensive use.

log

log log-crates.io log-github log-lib.rs cat-development-tools::debugging

log is an older and simpler crate, if your needs are simple and you are not using any async code.

slog

slog slog-crates.io slog-github slog-lib.rs cat-development-tools::debugging

Structured, extensible, composable logging. slog remains a stable, featureful and battle-tested library, used in many important projects.

log4rs

log4rs log4rs-crates.io log4rs-github log4rs-lib.rs

log4rs is a highly configurable multi-output logging implementation for the log facade.

use log::{error, info, warn};
use log4rs;

fn main() {
    log4rs::init_file("config/log4rs.yaml", Default::default()).unwrap();

    info!("booting up");

    // ...
}

env_logger

env_logger env_logger-crates.io env_logger-github env_logger-lib.rs cat-development-tools::debugging

A logging implementation for log which is configured via an environment variable. env_logger makes sense when used in executables (binary projects). Libraries should use the log crate instead.

use log::info;

fn main() {
    env_logger::init();

    info!("starting up");

    // ...
}

Other frameworks

OpenTelemetry

OpenTelemetry Rust documentation

OpenObserve

OpenObserve⮳ (written in Rust) is a petabyte-scale Elasticsearch/Splunk/Datadog alternative for logs, metrics, traces, RUM, error tracking, and session replay.


// // OpenObserve (O2 for short) is a cloud-native observability platform built
// // specifically for logs, metrics, traces, analytics, RUM (Real User
// Monitoring // - Performance, Errors, Session Replay) designed to work at
// petabyte scale. // OpenObserve serves as a seamless replacement for
// Elasticsearch for users who // ingest data using APIs and perform searches.
// OpenObserve comes with its own // user interface, eliminating the need for
// separate installation.

// // Install with:
// // (Docker) docker run -v $PWD/data:/data -e ZO_DATA_DIR="/data" -p 5080:5080
// \ //    -e ZO_ROOT_USER_EMAIL="root@example.com" -e
// // ZO_ROOT_USER_PASSWORD="Complexpass#123" \    public.ecr.aws/zinclabs/
// // openobserve:latest

// // Example from https://openobserve.ai/docs/ingestion/traces/rust/

// use std::io::Error;

// use opentelemetry::Key;
// use opentelemetry::KeyValue;
// use opentelemetry::global;
// use opentelemetry::trace::TracerProvider;
// use opentelemetry_otlp::WithExportConfig;
// use opentelemetry_sdk::Resource;
// use opentelemetry_sdk::metrics::Aggregation;
// use opentelemetry_sdk::metrics::instrument;
// use opentelemetry_sdk::metrics::MeterProviderBuilder;
// use opentelemetry_sdk::metrics::PeriodicReader;
// use opentelemetry_sdk::metrics::SdkMeterProvider;
// use opentelemetry_sdk::metrics::Stream;
// use opentelemetry_sdk::metrics::reader::DefaultAggregationSelector;
// use opentelemetry_sdk::metrics::reader::DefaultTemporalitySelector;
// use opentelemetry_sdk::runtime;
// use opentelemetry_sdk::trace::BatchConfig;
// use opentelemetry_sdk::trace::RandomIdGenerator;
// use opentelemetry_sdk::trace::Sampler;
// use opentelemetry_sdk::trace::Tracer;
// use opentelemetry_semantic_conventions::SCHEMA_URL;
// use opentelemetry_semantic_conventions::attribute::DEPLOYMENT_ENVIRONMENT_NAME;
// use opentelemetry_semantic_conventions::resource::SERVICE_NAME;
// use opentelemetry_semantic_conventions::resource::SERVICE_VERSION;
// use tonic::metadata::*;
// use tracing::Level;
// use tracing_opentelemetry::MetricsLayer;
// use tracing_opentelemetry::OpenTelemetryLayer;
// use tracing_subscriber::layer::SubscriberExt;
// use tracing_subscriber::util::SubscriberInitExt;

// // `opentelemetry_otlp` is the official crate to build an Exporter.
// // `tracing_opentelemetry` is required if you want to integrate OpenObserve
// with Tokio's tracing project.

// // Create a Resource that captures information about the entity for which
// // telemetry is recorded.
// fn resource() -> Resource {
//     Resource::from_schema_url(
//         [
//             KeyValue::new(SERVICE_NAME, env!("CARGO_PKG_NAME")),
//             KeyValue::new(SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
//             KeyValue::new(DEPLOYMENT_ENVIRONMENT_NAME, "develop"),
//         ],
//         SCHEMA_URL,
//     )
// }

// // Create the required Metadata headers for OpenObserve
// fn otl_metadata() -> Result<MetadataMap, Error> {
//     let mut map = MetadataMap::with_capacity(3);

//     map.insert(
//         "authorization",
//         format!("Basic cm9vdEBleGFtcGxlLmNvbTo3a2xCT052cHhTd09DZ09u") // This
// is picked from the Ingestion tab openobserve             .parse()
//             .unwrap(),
//     );
//     map.insert("organization", "default".parse().unwrap());
//     map.insert("stream-name", "default".parse().unwrap());
//     Ok(map)
// }

// // Construct MeterProvider for MetricsLayer
// fn init_meter_provider() -> SdkMeterProvider {
//     let exporter = opentelemetry_otlp::new_exporter()
//         .tonic()
//         .with_endpoint("http://localhost:5081/api/development")
//         .with_protocol(opentelemetry_otlp::Protocol::Grpc)
//         .with_metadata(otl_metadata().unwrap())
//         .build_metrics_exporter(
//             Box::new(DefaultAggregationSelector::new()),
//             Box::new(DefaultTemporalitySelector::new()),
//         )
//         .unwrap();

//     let reader = PeriodicReader::builder(exporter, runtime::Tokio)
//         .with_interval(std::time::Duration::from_secs(30))
//         .build();

//     // For debugging in development
//     let stdout_reader = PeriodicReader::builder(
//         opentelemetry_stdout::MetricsExporter::default(),
//         runtime::Tokio,
//     )
//     .build();

//     // Rename foo metrics to foo_named and drop key_2 attribute
//     let view_foo = |instrument: &Instrument| -> Option<Stream> {
//         if instrument.name == "foo" {
//             Some(
//                 Stream::new()
//                     .name("foo_named")
//                     .allowed_attribute_keys([Key::from("key_1")]),
//             )
//         } else {
//             None
//         }
//     };

//     // Set Custom histogram boundaries for baz metrics
//     let view_baz = |instrument: &Instrument| -> Option<Stream> {
//         if instrument.name == "baz" {
//             Some(Stream::new().name("baz").aggregation(
//                 Aggregation::ExplicitBucketHistogram {
//                     boundaries: vec![0.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0],
//                     record_min_max: true,
//                 },
//             ))
//         } else {
//             None
//         }
//     };

//     let meter_provider = MeterProviderBuilder::default()
//         .with_resource(resource())
//         .with_reader(reader)
//         .with_reader(stdout_reader)
//         .with_view(view_foo)
//         .with_view(view_baz)
//         .build();

//     global::set_meter_provider(meter_provider.clone());

//     meter_provider
// }

// // Construct Tracer for OpenTelemetryLayer
// fn init_tracer() -> Tracer {
//     let provider = opentelemetry_otlp::new_pipeline()
//         .tracing()
//         .with_trace_config(
//             //opentelemetry_sdk::trace::Config::default()
//             Builder::with_sampler(
//                 // Customize sampling strategy
//                 Sampler::ParentBased(Box::new(Sampler::TraceIdRatioBased(
//                     1.0,
//                 ))))
//                 // If export trace to AWS X-Ray, you can use XrayIdGenerator
//                 .with_id_generator(RandomIdGenerator::default())
//                 .with_resource(resource()),
//         )
//         .with_batch_config(BatchConfig::default())
//         .with_exporter(
//             opentelemetry_otlp::new_exporter()
//                 .tonic()
//                 .with_endpoint("http://localhost:5081/api/development")
//                 .with_metadata(otl_metadata().unwrap()),
//         )
//         .install_batch(runtime::Tokio)
//         .unwrap();

//     global::set_tracer_provider(provider.clone());
//     provider.tracer("tracing-otel-subscriber")
// }

// // Initialize tracing-subscriber and return OtelGuard for
// opentelemetry-related // termination processing
// fn init_tracing_subscriber() -> OtelGuard {
//     let meter_provider = init_meter_provider();
//     let tracer = init_tracer();

//     tracing_subscriber::registry()
//         .with(tracing_subscriber::filter::LevelFilter::from_level(
//             Level::INFO,
//         ))
//         .with(tracing_subscriber::fmt::layer())
//         .with(MetricsLayer::new(meter_provider.clone()))
//         .with(OpenTelemetryLayer::new(tracer))
//         .init();

//     OtelGuard { meter_provider }
// }

// struct OtelGuard {
//     meter_provider: SdkMeterProvider,
// }

// impl Drop for OtelGuard {
//     fn drop(&mut self) {
//         if let Err(err) = self.meter_provider.shutdown() {
//             eprintln!("{err:?}");
//         }
//         opentelemetry::global::shutdown_tracer_provider();
//     }
// }

// #[tokio::main]
// async fn main() {
//     let _guard = init_tracing_subscriber();

//     foo().await;
// }

// #[tracing::instrument]
// async fn foo() {
//     tracing::info!(
//         monotonic_counter.foo = 1_u64,
//         key_1 = "bar",
//         key_2 = 10,
//         "handle foo",
//     );

//     tracing::info!(histogram.baz = 10, "histogram example",);
// }