# Rust logs installation - Docs

1.  1

    ## Install OpenTelemetry packages

    Required

    Terminal

    PostHog AI

    ```bash
    cargo add opentelemetry opentelemetry_sdk opentelemetry-appender-tracing tracing
    cargo add tracing-subscriber --features env-filter
    cargo add opentelemetry-otlp --features reqwest-rustls
    ```

    > **Important:** `opentelemetry-otlp` enables no TLS backend by default, so `reqwest-rustls` is required to reach an `https` endpoint. Without it, every export fails immediately with `HTTP export failed: network error`.

    This guide targets `opentelemetry*` 0.32 and needs 0.28 or later. If your application uses the [`log`](https://docs.rs/log) crate rather than [`tracing`](https://docs.rs/tracing), swap `opentelemetry-appender-tracing` for `opentelemetry-appender-log`.

2.  2

    ## Get your project token

    Required

    You'll need your PostHog project token to authenticate log requests. This is the same key you use for capturing events and exceptions with the PostHog SDK.

    > **Important:** Use your **project token** which starts with `phc_`. Do **not** use a personal API key (which starts with `phx_`).

    You can find your project token in [Project Settings](https://app.posthog.com/settings).

3.  3

    ## Configure the SDK

    Required

    Set up the OpenTelemetry SDK to send logs to PostHog.

    Rust

    PostHog AI

    ```rust
    use std::collections::HashMap;
    use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge;
    use opentelemetry_otlp::{LogExporter, Protocol, WithExportConfig, WithHttpConfig};
    use opentelemetry_sdk::{logs::SdkLoggerProvider, Resource};
    use tracing_subscriber::{prelude::*, EnvFilter};
    fn init_logs() -> SdkLoggerProvider {
        // use the blocking client even in an async app, the batch processor exports from
        // its own thread and the async reqwest-client feature panics there
        let exporter = LogExporter::builder()
            .with_http()
            .with_endpoint("https://us.i.posthog.com/i/v1/logs")
            .with_protocol(Protocol::HttpBinary)
            .with_headers(HashMap::from([(
                "Authorization".to_string(),
                "Bearer <ph_project_token>".to_string(),
            )]))
            .build()
            .expect("failed to build the OTLP log exporter");
        // you could also set the service name outside your application, with OTEL_SERVICE_NAME
        let provider = SdkLoggerProvider::builder()
            .with_resource(Resource::builder().with_service_name("my-service").build())
            .with_batch_exporter(exporter)
            .build();
        // the transport crates log through tracing too, so their output would loop back
        let export_filter = EnvFilter::new("info")
            .add_directive("opentelemetry=off".parse().unwrap())
            .add_directive("hyper=off".parse().unwrap())
            .add_directive("h2=off".parse().unwrap())
            .add_directive("reqwest=off".parse().unwrap());
        tracing_subscriber::registry()
            .with(OpenTelemetryTracingBridge::new(&provider).with_filter(export_filter))
            // export failures are only reported here, so keep this layer
            .with(
                tracing_subscriber::fmt::layer().with_filter(
                    EnvFilter::new("info").add_directive("opentelemetry=debug".parse().unwrap()),
                ),
            )
            .init();
        provider
    }
    fn main() {
        // keep the provider alive for the lifetime of the process
        let provider = init_logs();
        tracing::info!("this is a log line");
        // flush on graceful exit, logs emitted after this are dropped. on tokio's
        // current-thread runtime call this from spawn_blocking, it can deadlock
        if let Err(err) = provider.shutdown() {
            eprintln!("failed to flush logs: {err}");
        }
    }
    ```

    Alternatively, you can pass the project token as a query parameter by modifying the URL path, though it will then appear in proxy and CDN access logs:

    Rust

    PostHog AI

    ```rust
    .with_endpoint("https://us.i.posthog.com/i/v1/logs?token=<ph_project_token>")
    ```

4.  4

    ## Use OpenTelemetry logging

    Required

    Now you can start logging with OpenTelemetry. The message becomes the log body, and structured fields become searchable attributes:

    Rust

    PostHog AI

    ```rust
    // debug! and trace! are excluded by the info filter above
    tracing::info!(user_id = "123", action = "login", "User action");
    tracing::warn!(endpoint = "/old-api", "Deprecated API used");
    tracing::error!(error = "Connection timeout", "Database connection failed");
    // these keys are matched exactly, snake_case versions won't link
    tracing::info!(posthogDistinctId = %user_id, sessionId = %session_id, "Checkout completed");
    ```

5.  5

    ## Test your setup

    Recommended

    Once everything is configured, test that logs are flowing into PostHog:

    1.  Send a test log from your application
    2.  Check the PostHog Logs interface for your log entries
    3.  Verify the logs appear in your project

    [View your logs in PostHog](https://app.posthog.com/logs)

7.  ## Next steps

    Checkpoint

    *What you can do with your logs*

    | Action | Description |
    | --- | --- |
    | [Why you need logs](/docs/logs/basics.md) | What logs show you that nothing else does |
    | [Search logs](/docs/logs/search.md) | Use the search interface to find specific log entries |
    | Filter by level | Filter by INFO, WARN, ERROR, etc. |
    | [Link session replay](/docs/logs/link-session-replay.md) | Connect logs to users and session replays by passing posthogDistinctId and sessionId |
    | [Link logs to a person](/docs/logs/link-person.md) | Surface every log emitted on behalf of a user on their PostHog person profile |
    | [Logging best practices](/docs/logs/best-practices.md) | Learn what to log, how to structure logs, and patterns that make logs useful in production |

    [Troubleshoot common issues](/docs/logs/troubleshooting.md)

### Community questions

Ask a question

### Was this page useful?

HelpfulCould be better