> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/open-telemetry/opentelemetry-rust/llms.txt
> Use this file to discover all available pages before exploring further.

# log Appender

> Bridge logs from the log crate to OpenTelemetry

## Overview

The `opentelemetry-appender-log` crate provides a bridge between the [`log`](https://docs.rs/log) crate and OpenTelemetry. It implements the `log::Log` trait to capture logs emitted through the `log` crate's macros (`error!`, `warn!`, `info!`, `debug!`, `trace!`) and forwards them to OpenTelemetry exporters.

## Installation

Add the following to your `Cargo.toml`:

```toml theme={null}
[dependencies]
log = "0.4"
opentelemetry = { version = "0.27", features = ["logs"] }
opentelemetry-appender-log = "0.27"
opentelemetry-sdk = { version = "0.27", features = ["logs"] }
opentelemetry-stdout = { version = "0.27", features = ["logs"] }
```

## Quick Start

<Steps>
  <Step title="Create a LoggerProvider">
    Set up the OpenTelemetry logger provider with an exporter:

    ```rust theme={null}
    use opentelemetry_sdk::logs::SdkLoggerProvider;
    use opentelemetry_stdout::LogExporter;

    let exporter = LogExporter::default();
    let provider = SdkLoggerProvider::builder()
        .with_simple_exporter(exporter)
        .build();
    ```
  </Step>

  <Step title="Install the log appender">
    Create and register the OpenTelemetry log bridge:

    ```rust theme={null}
    use opentelemetry_appender_log::OpenTelemetryLogBridge;
    use log::Level;

    let appender = OpenTelemetryLogBridge::new(&provider);
    log::set_boxed_logger(Box::new(appender)).unwrap();
    log::set_max_level(Level::Info.to_level_filter());
    ```
  </Step>

  <Step title="Emit logs">
    Use standard `log` macros:

    ```rust theme={null}
    use log::{error, warn, info};

    error!("Application error occurred");
    warn!("Warning message");
    info!("Application started successfully");
    ```
  </Step>

  <Step title="Shutdown">
    Flush remaining logs before exit:

    ```rust theme={null}
    provider.shutdown().unwrap();
    ```
  </Step>
</Steps>

## Complete Example

```rust theme={null}
use log::{error, info, warn, Level};
use opentelemetry_appender_log::OpenTelemetryLogBridge;
use opentelemetry_sdk::{logs::SdkLoggerProvider, Resource};
use opentelemetry_stdout::LogExporter;

#[tokio::main]
async fn main() {
    // Create an exporter that writes to stdout
    let exporter = LogExporter::default();

    // Create a LoggerProvider with resource attributes
    let provider = SdkLoggerProvider::builder()
        .with_resource(
            Resource::builder()
                .with_service_name("my-service")
                .build(),
        )
        .with_simple_exporter(exporter)
        .build();

    // Set up the log appender
    let appender = OpenTelemetryLogBridge::new(&provider);
    log::set_boxed_logger(Box::new(appender)).unwrap();
    log::set_max_level(Level::Info.to_level_filter());

    // Emit logs with structured key-values
    let fruit = "apple";
    let price = 2.99;

    error!(fruit, price; "hello from {fruit}. My price is {price}");
    warn!("Warning message");
    info!("Application started");

    // Shutdown to ensure all logs are flushed
    provider.shutdown().unwrap();
}
```

## Field Mapping

The appender maps `log::Record` fields to OpenTelemetry `LogRecord` fields:

### Basic Fields

| log Field      | OpenTelemetry Field | Notes                                   |
| -------------- | ------------------- | --------------------------------------- |
| `args()`       | `body`              | The formatted log message               |
| `level()`      | `severity_number`   | Mapped using severity table below       |
| `level()`      | `severity_text`     | String representation ("ERROR", "INFO") |
| `target()`     | `target`            | Module/component identifier             |
| `key_values()` | `attributes`        | Structured key-value pairs              |

### Severity Mapping

| log::Level | Severity Text | Severity Number |
| ---------- | ------------- | --------------- |
| `Error`    | ERROR         | 17              |
| `Warn`     | WARN          | 13              |
| `Info`     | INFO          | 9               |
| `Debug`    | DEBUG         | 5               |
| `Trace`    | TRACE         | 1               |

### Metadata Attributes (Experimental)

With the `experimental_metadata_attributes` feature, source code metadata is captured:

```toml theme={null}
[dependencies]
opentelemetry-appender-log = { version = "0.27", features = ["experimental_metadata_attributes"] }
```

This adds attributes:

| log Field       | Attribute Key    | Example Value    |
| --------------- | ---------------- | ---------------- |
| `file()`        | `code.filepath`  | `src/main.rs`    |
| `line()`        | `code.lineno`    | `42`             |
| `module_path()` | `code.namespace` | `my_app::module` |

## Key-Value Attributes

The `log` crate supports structured logging with key-value pairs:

```rust theme={null}
use log::info;

info!(
    user_id = 12345,
    action = "login",
    duration_ms = 150;
    "User logged in successfully"
);
```

These are converted to OpenTelemetry attributes based on their type:

### Type Mapping

| Rust Type        | AnyValue Type     | Notes                                         |
| ---------------- | ----------------- | --------------------------------------------- |
| `i8`-`i64`       | `Int`             |                                               |
| `u8`-`u64`       | `Int`             | Converted to `i64` if possible, else `String` |
| `i128`, `u128`   | `Int` or `String` | Uses `Int` if fits in `i64`, else stringified |
| `f32`, `f64`     | `Double`          |                                               |
| `bool`           | `Boolean`         |                                               |
| `&str`, `String` | `String`          |                                               |
| Other types      | `String`          | Formatted using `Debug`                       |

### With Serde Support

Enable the `with-serde` feature for complex types:

```toml theme={null}
[dependencies]
opentelemetry-appender-log = { version = "0.27", features = ["with-serde"] }
```

With this feature enabled:

\| Type              | Result        | Notes                              |
\|-------------------|---------------|------------------------------------||
\| Sequences         | `ListAny`     | Vectors, arrays, etc.              |
\| Maps              | `Map`         | HashMaps, BTreeMaps, etc.          |
\| Structs           | `Map`         | Serialized as key-value maps       |
\| Enums             | Various       | Depends on variant type            |
\| Bytes             | `Bytes`       | Raw byte arrays                    |
\| `Option::None`    | —             | Discarded                          |
\| `Option::Some(T)` | `T`           | Uses inner value                   |
\| `()`              | —             | Discarded                          |

**Example:**

```rust theme={null}
use log::info;
use serde::Serialize;

#[derive(Serialize)]
struct UserInfo {
    id: u64,
    name: String,
    roles: Vec<String>,
}

let user = UserInfo {
    id: 12345,
    name: "alice".to_string(),
    roles: vec!["admin".to_string(), "user".to_string()],
};

info!(user = log::kv::Value::from_serde(&user); "User loaded");
```

Without `with-serde`, complex types are formatted using `Debug`:

```rust theme={null}
// Without with-serde feature
info!(data = vec![1, 2, 3]; "Processing data");
// Attribute value: "[1, 2, 3]" (string)
```

## Usage with Batch Processor

For production use, configure a batch processor:

```rust theme={null}
use opentelemetry_sdk::logs::BatchLogProcessor;
use std::time::Duration;

let exporter = opentelemetry_stdout::LogExporter::default();

let processor = BatchLogProcessor::builder(exporter)
    .with_batch_config(
        opentelemetry_sdk::logs::BatchConfigBuilder::default()
            .with_max_queue_size(2048)
            .with_max_export_batch_size(512)
            .with_scheduled_delay(Duration::from_secs(5))
            .build(),
    )
    .build();

let provider = SdkLoggerProvider::builder()
    .with_log_processor(processor)
    .build();
```

## Integration with Traces

When logs are emitted within an active OpenTelemetry span context, the trace context is automatically attached:

```rust theme={null}
use opentelemetry::trace::{Tracer, TracerProvider};
use opentelemetry_sdk::trace::SdkTracerProvider;
use log::error;

let tracer_provider = SdkTracerProvider::builder().build();
let tracer = tracer_provider.tracer("my-app");

tracer.in_span("process-request", |_cx| {
    // This log will have trace_id and span_id from the active span
    error!("Request processing failed");
});
```

The emitted log will include:

* `trace_id`: The ID of the distributed trace
* `span_id`: The ID of the current span
* `trace_flags`: Sampling flags

## Performance Considerations

### Filtering

Set appropriate log levels to avoid overhead:

```rust theme={null}
// Only process Info and above
log::set_max_level(log::Level::Info.to_level_filter());
```

### event\_enabled

The appender implements `enabled()` to check if a log should be processed:

```rust theme={null}
// This check happens before expensive formatting
if log::log_enabled!(log::Level::Debug) {
    let expensive_data = compute_expensive_data();
    log::debug!(data = expensive_data; "Debug information");
}
```

### Batching

Use `BatchLogProcessor` in production to amortize export costs:

* Reduces network overhead
* Improves throughput
* Adds minimal latency (configurable)

See [Log Processors](/logs/log-processors) for configuration details.

## Comparison with tracing Appender

| Feature                | log Appender        | tracing Appender     |
| ---------------------- | ------------------- | -------------------- |
| **Target Crate**       | `log`               | `tracing`            |
| **Structured Logging** | Key-values          | Fields + spans       |
| **Span Context**       | Manual              | Automatic            |
| **Async-aware**        | No                  | Yes                  |
| **Filtering**          | Level-based         | Target + level-based |
| **Event Names**        | Not supported       | Supported            |
| **Best For**           | Simple applications | Async applications   |

Choose the `log` appender if:

* You have existing code using the `log` crate
* You need simple, synchronous logging
* You don't need advanced filtering or span correlation

Choose the [`tracing` appender](/logs/appender-tracing) if:

* You're building async applications
* You want hierarchical span context
* You need advanced filtering capabilities
* You want automatic trace correlation

## Feature Flags

| Feature                            | Description                                   |
| ---------------------------------- | --------------------------------------------- |
| `with-serde`                       | Support complex types via serde serialization |
| `experimental_metadata_attributes` | Capture source code location as attributes    |

## Troubleshooting

### Logs not appearing

1. **Check log level**: Ensure `set_max_level()` is set appropriately
   ```rust theme={null}
   log::set_max_level(log::LevelFilter::Debug);
   ```

2. **Flush on shutdown**: Always call `provider.shutdown()`
   ```rust theme={null}
   provider.shutdown().unwrap();
   ```

3. **Check processor configuration**: Verify the exporter is configured correctly

### Complex types not serialized

Enable the `with-serde` feature:

```toml theme={null}
opentelemetry-appender-log = { version = "0.27", features = ["with-serde"] }
```

### Trace context not attached

Ensure logs are emitted within an active span context:

```rust theme={null}
tracer.in_span("my-operation", |_cx| {
    log::info!("This log will have trace context");
});
```

## See Also

<CardGroup cols={2}>
  <Card title="tracing Appender" icon="timeline" href="/logs/appender-tracing">
    Alternative appender for the `tracing` crate
  </Card>

  <Card title="Log Processors" icon="gears" href="/logs/log-processors">
    Configure batch and simple processors
  </Card>

  <Card title="Bridge API" icon="bridge" href="/logs/bridge-api">
    Understanding the underlying API
  </Card>

  <Card title="Overview" icon="book" href="/logs/overview">
    Return to logs overview
  </Card>
</CardGroup>
