Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

OTLP

The logs-otlp feature adds a sink that sends entries as OpenTelemetry log records to the Telemetry API. The same GcpOtlpExporterBuilder that exports the spans builds the logger provider, so one set of credentials and one endpoint serve both signals.

    let otlp = GcpOtlpExporterBuilder::new(google_project_id.clone()).with_resource(
        opentelemetry_sdk::Resource::builder()
            .with_service_name("logging-otlp-example")
            .build(),
    );
    let tracer_provider = otlp.create_provider().await?;
    let tracer = otlp.install(&tracer_provider);
    let logger_provider = otlp.create_logger_provider().await?;

    let log_layer = GcpCloudLoggingLayerBuilder::new(google_project_id)
        .with_otel_logger_provider(
            &logger_provider,
            LogId::new("gcloud-observability-example")?,
        )
        .build()?;

    // The OpenTelemetry layer goes before the logging layer: the logging layer reads the span
    // context the OpenTelemetry layer attaches, and sees only what is registered ahead of it.
    let subscriber = Registry::default()
        .with(EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")))
        .with(tracing_opentelemetry::layer().with_tracer(tracer))
        .with(log_layer);
    tracing::subscriber::set_global_default(subscriber)?;

with_otel_logger_provider takes the provider and the LogId of the log the entries are written to, which becomes the gcp.log.name attribute. Its build() is synchronous. The caller owns the provider and flushes it by shutting it down.

Shutting down

shutdown, force_flush and dropping the provider block the calling thread. The batch processor exports on a thread of its own, but the connection is driven by the Tokio runtime, which needs a thread free to do it. On a current-thread runtime they wait out their timeout and the queued records are not exported. Call them on a multi-threaded runtime, or from a thread outside the runtime.

The processor reports a failed export as an ERROR tracing event with the opentelemetry_sdk target. That target is on the list of transport crates the network sinks skip, so a failing export does not log itself into another export.

max_export_timeout bounds how long an export may take, see OTLP exporter.

Mapping

The Telemetry API maps an OTLP log record onto a LogEntry, see OTLP log record to LogEntry mapping. The sink follows it:

EventOTLP log recordLogEntry
level, or a severity fieldseverity_number and severity_textseverity
message and fieldsbody, a key-value listjsonPayload
enclosing spantrace ID, span ID and sampled flagtrace, spanId, traceSampled
http_request.*gcp.http_request; latency as secondshttpRequest
operation.*gcp.operationoperation
source locationcode.file.path and code.line.numbersourceLocation
insert_idgcp.insert_idinsertId
labels.*one attribute per labellabels
LogIdgcp.log.namelogName

The mapping page names the line attribute code.function.number, a typo: Google reads code.line.number, and that is what the sink sets.

Attributes that map to no typed field become labels. The resource of the provider carries gcp.project_id, and service.name when set. See Comparison with the OpenTelemetry tracing bridge for entries side by side.

Entries sent over OTLP store trace as the bare trace ID rather than projects/PROJECT_ID/traces/TRACE_ID, so filter them with trace:"TRACE_ID", which matches both forms.

Full example available here.