#tracing-subscriber #datadog #layer #tracing #open-telemetry


A crate providing a tracing-subscriber layer for formatting events so Datadog can parse them

5 stable releases

2.1.0 Mar 6, 2024
2.0.0 Mar 5, 2024
1.1.0 Sep 18, 2023
1.0.1 Jun 21, 2023

#215 in Debugging

Download history 234/week @ 2023-12-18 53/week @ 2024-01-01 83/week @ 2024-01-08 71/week @ 2024-01-15 86/week @ 2024-01-22 123/week @ 2024-01-29 77/week @ 2024-02-05 85/week @ 2024-02-12 159/week @ 2024-02-19 194/week @ 2024-02-26 362/week @ 2024-03-04 113/week @ 2024-03-11 56/week @ 2024-03-18 402/week @ 2024-03-25 272/week @ 2024-04-01

857 downloads per month


499 lines

Datadog Formatting Layer

A crate providing a tracing-subscriber layer for formatting events so Datadog can parse them.

Release Test License Crates.io


  • Provides a layer for tracing-subscriber
  • Generates parsable "logs" for datadog and prints them to stdout
  • Enables log correlation between spans and "logs" (see datadog docs)

Why not just tracing_subscriber::fmt().json() ?

The problem is, that datadog expects the "logs" to be in a specific (mostly undocumented) json format.

This crates tries to mimic this format.



use datadog_formatting_layer::DatadogFormattingLayer;
use tracing::info;
use tracing_subscriber::prelude::*;


info!(user = "Jack", "Hello World!");

Running this code will result in the following output on stdout:

  "timestamp": "2023-06-21T10:36:50.364874878+00:00",
  "level": "INFO",
  "message": "Hello World user=Jack",
  "target": "simple"

With Opentelemetry

use datadog_formatting_layer::DatadogFormattingLayer;
use opentelemetry::global;
use opentelemetry_datadog::ApiVersion;
use opentelemetry_sdk::{
    trace::{config, RandomIdGenerator, Sampler},
use tracing::{debug, error, info, instrument, warn};
use tracing_subscriber::{prelude::*, util::SubscriberInitExt};

// Just some otel boilerplate

let tracer = opentelemetry_datadog::new_pipeline()

// Use both the tracer and the formatting layer

// Here no span exists
info!(user = "Jack", "Hello World!");

// This will create a span and a trace id which is attached to the "logs"
#[instrument(fields(hello = "world"))]
fn some_test(value: &str) {
    // Here some span exists
    info!(ola = "salve", value, "Bla {value}");

When running this code with an datadog agent installed the logs will be sent to datadog and parsed there.

Otherwise the following output will be printed to stdout

{"timestamp":"2023-06-21T10:36:50.363224217+00:00","level":"INFO","message":"Hello World! user=Jack","target":"otel"}
{"timestamp":"2023-06-21T10:36:50.363384118+00:00","level":"INFO","message":"Bla fasel user=Jack ola=salve value=Fasel hello=world","target":"otel","dd.trace_id":0,"dd.span_id":10201226522570980512}

Supported Opentelemetry versions:

Opentelemetry DatadogFormattingLayer
0.22.* 2.1.*
0.20.* 2.0.*
0.20.* 1.1.*
0.19.* 1.0.*


~135K SLoC