> ## 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.

# Logs Bridge API

> Core types and traits for bridging logging libraries to OpenTelemetry

## Overview

The OpenTelemetry Logs Bridge API provides the foundation for connecting existing logging libraries to OpenTelemetry. This API is **not intended for direct use by application developers**—instead, it's designed for logging library authors to build log appenders.

<Warning>
  Application developers should use logging libraries like `log` or `tracing` along with the provided appenders, not the Bridge API directly.
</Warning>

## Core Traits

The Bridge API defines three main traits in the `opentelemetry::logs` module:

### Logger

The `Logger` trait provides methods to create and emit log records:

```rust theme={null}
pub trait Logger {
    type LogRecord: LogRecord;

    /// Creates a new log record builder
    fn create_log_record(&self) -> Self::LogRecord;

    /// Emits a log record
    fn emit(&self, record: Self::LogRecord);

    /// Checks if a log event is enabled
    fn event_enabled(&self, level: Severity, target: &str, name: Option<&str>) -> bool;
}
```

**Key methods:**

* `create_log_record()` - Creates a new log record that can be populated with data
* `emit()` - Sends the log record to the logging pipeline
* `event_enabled()` - Allows early filtering to skip expensive logging operations

<Note>
  When `emit()` is called within an active trace context, the logger automatically attaches the current trace ID, span ID, and trace flags to the log record.
</Note>

### LoggerProvider

The `LoggerProvider` trait creates `Logger` instances:

```rust theme={null}
pub trait LoggerProvider {
    type Logger: Logger;

    /// Returns a logger with the given instrumentation scope
    fn logger_with_scope(&self, scope: InstrumentationScope) -> Self::Logger;

    /// Returns a logger with the given name
    fn logger(&self, name: impl Into<Cow<'static, str>>) -> Self::Logger {
        let scope = InstrumentationScope::builder(name).build();
        self.logger_with_scope(scope)
    }
}
```

**Usage example:**

```rust theme={null}
use opentelemetry::logs::LoggerProvider;
use opentelemetry::InstrumentationScope;
use opentelemetry_sdk::logs::SdkLoggerProvider;

let provider = SdkLoggerProvider::builder().build();

// Simple logger for applications
let logger = provider.logger("my-app");

// Logger with full instrumentation scope for libraries
let scope = InstrumentationScope::builder(env!("CARGO_PKG_NAME"))
    .with_version(env!("CARGO_PKG_VERSION"))
    .with_schema_url("https://opentelemetry.io/schemas/1.0.0")
    .build();
let logger = provider.logger_with_scope(scope);
```

### LogRecord

The `LogRecord` trait provides methods to populate log record fields:

```rust theme={null}
pub trait LogRecord {
    fn set_event_name(&mut self, name: &'static str);
    fn set_target<T: Into<Cow<'static, str>>>(&mut self, target: T);
    fn set_timestamp(&mut self, timestamp: SystemTime);
    fn set_observed_timestamp(&mut self, timestamp: SystemTime);
    fn set_severity_text(&mut self, text: &'static str);
    fn set_severity_number(&mut self, number: Severity);
    fn set_body(&mut self, body: AnyValue);
    fn add_attribute<K, V>(&mut self, key: K, value: V)
    where
        K: Into<Key>,
        V: Into<AnyValue>;
    fn add_attributes<I, K, V>(&mut self, attributes: I)
    where
        I: IntoIterator<Item = (K, V)>,
        K: Into<Key>,
        V: Into<AnyValue>;
    fn set_trace_context(
        &mut self,
        trace_id: TraceId,
        span_id: SpanId,
        trace_flags: Option<TraceFlags>,
    );
}
```

## Core Types

### Severity

The `Severity` enum defines 24 log severity levels:

```rust theme={null}
pub enum Severity {
    Trace = 1,   Trace2 = 2,  Trace3 = 3,  Trace4 = 4,
    Debug = 5,   Debug2 = 6,  Debug3 = 7,  Debug4 = 8,
    Info = 9,    Info2 = 10,  Info3 = 11,  Info4 = 12,
    Warn = 13,   Warn2 = 14,  Warn3 = 15,  Warn4 = 16,
    Error = 17,  Error2 = 18, Error3 = 19, Error4 = 20,
    Fatal = 21,  Fatal2 = 22, Fatal3 = 23, Fatal4 = 24,
}
```

Most appenders map standard log levels to the base severity:

```rust theme={null}
const fn severity_of_level(level: Level) -> Severity {
    match level {
        Level::Error => Severity::Error,  // 17
        Level::Warn => Severity::Warn,    // 13
        Level::Info => Severity::Info,    // 9
        Level::Debug => Severity::Debug,  // 5
        Level::Trace => Severity::Trace,  // 1
    }
}
```

Get the severity name:

```rust theme={null}
let severity = Severity::Error;
assert_eq!(severity.name(), "ERROR");
```

### AnyValue

The `AnyValue` enum represents values that can be stored in log attributes or body:

```rust theme={null}
#[non_exhaustive]
pub enum AnyValue {
    Int(i64),
    Double(f64),
    String(StringValue),
    Boolean(bool),
    Bytes(Box<Vec<u8>>),
    ListAny(Box<Vec<AnyValue>>),
    Map(Box<HashMap<Key, AnyValue>>),
}
```

**Automatic conversions:**

```rust theme={null}
use opentelemetry::logs::AnyValue;

// Integers (i8-i64, u8-u32)
let value: AnyValue = 42i32.into();
assert!(matches!(value, AnyValue::Int(42)));

// Floating point
let value: AnyValue = 3.14f64.into();
assert!(matches!(value, AnyValue::Double(_)));

// Strings
let value: AnyValue = "hello".into();
assert!(matches!(value, AnyValue::String(_)));

// Booleans
let value: AnyValue = true.into();
assert!(matches!(value, AnyValue::Boolean(true)));

// Bytes
let value: AnyValue = AnyValue::from(&b"data"[..]);
assert!(matches!(value, AnyValue::Bytes(_)));
```

**Collections:**

```rust theme={null}
use std::collections::HashMap;
use opentelemetry::{logs::AnyValue, Key};

// Lists from iterators
let list: AnyValue = vec![1, 2, 3]
    .into_iter()
    .collect();
assert!(matches!(list, AnyValue::ListAny(_)));

// Maps from iterators
let mut map = HashMap::new();
map.insert("key", "value");
let map_value: AnyValue = map.into_iter().collect();
assert!(matches!(map_value, AnyValue::Map(_)));
```

<Note>
  **Performance consideration:** The `log` and `tracing` crates only support basic types (i64, f64, strings, bool). Complex types like `ListAny` and `Map` are available for custom appenders but involve heap allocations.
</Note>

## SDK Implementation

The `opentelemetry-sdk` crate provides the concrete implementation:

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

let provider = SdkLoggerProvider::builder()
    .with_simple_exporter(exporter)
    .build();

let logger = provider.logger("my-logger");
```

### SdkLogRecord

The SDK's `SdkLogRecord` implements the `LogRecord` trait and stores:

* Timestamps (event time and observed time)
* Severity (number and text)
* Body (the log message)
* Attributes (structured key-value data)
* Trace context (trace ID, span ID, flags)
* Target (module/component identifier)
* Event name (optional event identifier)

## Building a Log Appender

Here's how to build a custom log appender using the Bridge API:

<Steps>
  <Step title="Get a Logger">
    Obtain a logger from the provider:

    ```rust theme={null}
    let logger = provider.logger("");
    ```

    Note: Log appenders typically use an empty scope name. See the [semantic conventions issue](https://github.com/open-telemetry/semantic-conventions/issues/1550).
  </Step>

  <Step title="Create a LogRecord">
    When a log event occurs:

    ```rust theme={null}
    let mut log_record = logger.create_log_record();
    ```
  </Step>

  <Step title="Populate the LogRecord">
    Map fields from the logging library to the log record:

    ```rust theme={null}
    log_record.set_severity_number(severity);
    log_record.set_severity_text("ERROR");
    log_record.set_body(message.into());
    log_record.set_target(target);
    log_record.add_attribute("key", "value");
    ```
  </Step>

  <Step title="Emit the LogRecord">
    Send the log record to the pipeline:

    ```rust theme={null}
    logger.emit(log_record);
    ```
  </Step>
</Steps>

### Example: Simple Appender

```rust theme={null}
use opentelemetry::logs::{AnyValue, Logger, LoggerProvider, Severity};

struct SimpleAppender<L> {
    logger: L,
}

impl<P, L> SimpleAppender<L>
where
    P: LoggerProvider<Logger = L>,
    L: Logger,
{
    fn new(provider: &P) -> Self {
        Self {
            logger: provider.logger(""),
        }
    }

    fn log(&self, level: Severity, message: &str) {
        // Check if the event is enabled before doing expensive work
        if !self.logger.event_enabled(level, "my-app", None) {
            return;
        }

        let mut record = self.logger.create_log_record();
        record.set_severity_number(level);
        record.set_severity_text(level.name());
        record.set_body(AnyValue::from(message.to_string()));
        record.set_target("my-app");

        self.logger.emit(record);
    }
}
```

## Performance Optimization

### event\_enabled Check

Always check `event_enabled()` before expensive operations:

```rust theme={null}
if !logger.event_enabled(severity, target, Some(name)) {
    return; // Skip expensive serialization
}

// Only do expensive work if the event is enabled
let mut record = logger.create_log_record();
// ... populate record ...
logger.emit(record);
```

This allows processors to filter logs early, avoiding unnecessary work for logs that would be dropped anyway.

### Target Field

The `target` field serves a special purpose:

```rust theme={null}
record.set_target("my_module::component");
```

* Used for filtering and routing logs
* Exporters may use this as the instrumentation scope name
* Both `log` and `tracing` appenders default to the module path

## Noop Implementation

For testing or when logging is disabled, use the noop provider:

```rust theme={null}
use opentelemetry::logs::NoopLoggerProvider;

let provider = NoopLoggerProvider::new();
let logger = provider.logger("my-app");

// All operations are no-ops
let mut record = logger.create_log_record();
logger.emit(record); // Does nothing
```

## See Also

<CardGroup cols={2}>
  <Card title="log Appender" icon="file-code" href="/logs/appender-log">
    Implementation using the `log` crate
  </Card>

  <Card title="tracing Appender" icon="timeline" href="/logs/appender-tracing">
    Implementation using the `tracing` crate
  </Card>

  <Card title="Log Processors" icon="gears" href="/logs/log-processors">
    Processing and exporting log records
  </Card>

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