Crate emit_otlp

Source
Expand description

Emit diagnostic events via the OpenTelemetry Protocol (OTLP).

This library provides Otlp, an emit::Emitter that sends export requests directly to some remote OTLP receiver. If you need to integrate emit with the OpenTelemetry SDK, see emit-opentelemetry.

§How it works

┌────────────────────────────────────────┐  ┌─────────────┐    ┌─────────────────────────────┐
│                caller                  │  │   channel   │    │     background worker       │
│                                        │  │             │    │                             │
│ emit::Event─┬─*─►is trace?──►Span──────┼──┼──►Trace─────┼─┐  │ ExportTraceServiceRequest   │
│             │                          │  │             │ │  │                             │
│             ├─*─►is metric?─►Metric────┼──┼──►Metrics───┼─┼──► ExportMetricsServiceRequest │
│             │                          │  │             │ │  │                             │
│             └─*─────────────►LogRecord─┼──┼──►Logs──────┼─┘  │ ExportLogsServiceRequest    │
└────────────────────────────────────────┘  └─────────────┘    └─────────────────────────────┘
 * Only if the logs/trace/metrics signal is configured

The emitter is based on an asynchronous, batching channel. A diagnostic event makes its way from emit::emit! through to the remote OTLP receiver in the following key steps:

  1. Determine what kind of signal the event belongs to:
    • If the event carries emit::Kind::Span, and the trace signal is configured, then treat it as a span.
    • If the event carries emit::Kind::Metric, and the metrics signal is configured, then treat it as a metric.
    • In any other case, if the logs signal is configured, then treat it as a log record.
  2. Serialize the event into the OTLP datastructure in the target format (JSON/protobuf).
  3. Put the serialized event into a channel. Each signal has its own internal queue in the channel.
  4. On a background worker, process the events in the channel by forming them up into OTLP export requests and sending them using the target protocol (HTTP/gRPC).

This library is based on hyper with tokio for HTTP, and rustls with ring for TLS. Some of these dependencies can be configured using Cargo features:

§Getting started

Add emit and emit_otlp to your Cargo.toml:

[dependencies.emit]
version = "1.7.0"

[dependencies.emit_otlp]
version = "1.7.0"

Initialize emit at the start of your main.rs using an OTLP emitter:

fn main() {
    let rt = emit::setup()
        .emit_to(emit_otlp::new()
            // Add required resource properties for OTLP
            .resource(emit::props! {
                #[emit::key("service.name")]
                service_name: emit::pkg!(),
            })
            // Configure endpoints for logs/traces/metrics using gRPC + protobuf
            .logs(emit_otlp::logs_grpc_proto("http://localhost:4319"))
            .traces(emit_otlp::traces_grpc_proto("http://localhost:4319"))
            .metrics(emit_otlp::metrics_grpc_proto("http://localhost:4319"))
            .spawn())
        .init();

    // Your app code goes here

    rt.blocking_flush(std::time::Duration::from_secs(30));
}

The new function returns an OtlpBuilder, which can be configured with endpoints for the desired signals through its OtlpBuilder::logs, OtlpBuilder::traces, and OtlpBuilder::metrics methods.

You don’t need to configure all signals, but you should at least configure OtlpBuilder::logs.

Once the builder is configured, call OtlpBuilder::spawn and pass the resulting Otlp to emit::Setup::emit_to.

§Where the background worker is spawned

The Otlp emitter doesn’t do any work directly. That’s all handled by a background worker created through OtlpBuilder::spawn. The worker will spawn on a background thread with a single-threaded tokio executor on it.

§Configuring for gRPC+protobuf

The logs_grpc_proto, traces_grpc_proto, and metrics_grpc_proto functions produce builders for gRPC+protobuf:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .logs(emit_otlp::logs_grpc_proto("http://localhost:4319"))
    .traces(emit_otlp::traces_grpc_proto("http://localhost:4319"))
    .metrics(emit_otlp::metrics_grpc_proto("http://localhost:4319"))
    .spawn()

gRPC is based on HTTP and internally uses well-known URI paths to route RPC requests. These paths are appended automatically to the endpoint, so you don’t need to specify them during configuration.

§Configuring for HTTP+JSON

The logs_http_json, traces_http_json, and metrics_http_json functions produce builders for HTTP+JSON:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .logs(emit_otlp::logs_http_json("http://localhost:4318/v1/logs"))
    .traces(emit_otlp::traces_http_json("http://localhost:4318/v1/traces"))
    .metrics(emit_otlp::metrics_http_json("http://localhost:4318/v1/metrics"))
    .spawn()

§Configuring for HTTP+protobuf

The logs_http_proto, traces_http_proto, and metrics_http_proto functions produce builders for HTTP+protobuf:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .logs(emit_otlp::logs_http_proto("http://localhost:4318/v1/logs"))
    .traces(emit_otlp::traces_http_proto("http://localhost:4318/v1/traces"))
    .metrics(emit_otlp::metrics_http_proto("http://localhost:4318/v1/metrics"))
    .spawn()

§Configuring TLS

If the tls Cargo feature is enabled, and the scheme of your endpoint uses the https:// scheme then it will use TLS from rustls and rustls-native-certs.

You can specify the tls-native Cargo feature to use native-tls instead of rustls.

§Configuring compression

If the gzip Cargo feature is enabled then gzip compression will be applied automatically to all export requests.

You can disable any compression through an OtlpTransportBuilder:

emit_otlp::new()
   .logs(emit_otlp::logs_proto(emit_otlp::http("http://localhost:4318/v1/logs")
      .allow_compression(false))
   )

§Customizing HTTP headers

You can specify custom headers to be used for HTTP or gRPC requests through an OtlpTransportBuilder:

emit_otlp::new()
   .logs(emit_otlp::logs_proto(emit_otlp::http("http://localhost:4318/v1/logs")
      .headers([
         ("X-ApiKey", "abcd"),
      ]))
   )

§Configuring a resource

The OtlpBuilder::resource method configures the OTLP resource to send with each export request. Some OTLP receivers accept data without a resource but the OpenTelemetry specification itself mandates it.

At a minimum, you should add the service.name property:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })

You should also consider setting other well-known resource properties:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
        #[emit::key("telemetry.sdk.language")]
        language: emit_otlp::telemetry_sdk_language(),
        #[emit::key("telemetry.sdk.name")]
        sdk: emit_otlp::telemetry_sdk_name(),
        #[emit::key("telemetry.sdk.version")]
        version: emit_otlp::telemetry_sdk_version(),
    })

§Configuring from environment variables

You can configure emit_otlp from OpenTelemetry’s environment variables using the from_env function:

emit_otlp::from_env().spawn()

The from_env function will create a builder with configuration for all signals and a resource. You can also configure individual signals from the environment if you want to further tweak them, or only configure a subset:

emit_otlp::new()
    .logs(emit_otlp::logs_from_env())
    .traces(emit_otlp::traces_from_env())
    .metrics(emit_otlp::metrics_from_env())
    .resource(emit_otlp::resource_from_env())
    .spawn()

The following table lists currently supported environment variables:

Variable NameDefault ValueValid ValuesNotes
OTEL_EXPORTER_OTLP_PROTOCOLgrpcgrpc, http/proto, http/json-
OTEL_EXPORTER_OTLP_LOGS_PROTOCOLOTEL_EXPORTER_OTLP_PROTOCOLgrpc, http/proto, http/json-
OTEL_EXPORTER_OTLP_TRACES_PROTOCOLOTEL_EXPORTER_OTLP_PROTOCOLgrpc, http/proto, http/json-
OTEL_EXPORTER_OTLP_METRICS_PROTOCOLOTEL_EXPORTER_OTLP_PROTOCOLgrpc, http/proto, http/json-
OTEL_EXPORTER_OTLP_ENDPOINThttp://localhost:4317 when OTEL_EXPORTER_OTLP_PROTOCOL is grpc, http://localhost:4318 when OTEL_EXPORTER_OTLP_PROTOCOL is httpAny valid HTTP/S URI-
OTEL_EXPORTER_OTLP_LOGS_ENDPOINThttp://localhost:4317 when OTEL_EXPORTER_OTLP_LOGS_PROTOCOL is grpc, http://localhost:4318 when OTEL_EXPORTER_OTLP_LOGS_PROTOCOL is http*Any valid HTTP/S URI-
OTEL_EXPORTER_OTLP_TRACES_ENDPOINThttp://localhost:4317 when OTEL_EXPORTER_OTLP_TRACES_PROTOCOL is grpc, http://localhost:4318 when OTEL_EXPORTER_OTLP_TRACES_PROTOCOL is http*Any valid HTTP/S URI-
OTEL_EXPORTER_OTLP_METRICS_ENDPOINThttp://localhost:4317 when OTEL_EXPORTER_OTLP_METRICS_PROTOCOL is grpc, http://localhost:4318 when OTEL_EXPORTER_OTLP_METRICS_PROTOCOL is http*Any valid HTTP/S URI-
OTEL_EXPORTER_OTLP_HEADERSEmptyW3C Baggage without ;-separated properties-
OTEL_EXPORTER_OTLP_LOGS_HEADERSOTEL_EXPORTER_OTLP_HEADERSW3C Baggage without ;-separated propertiesIf defined, headers are merged with OTEL_EXPORTER_OTLP_HEADERS, preferring those in OTEL_EXPORTER_OTLP_LOGS_HEADERS
OTEL_EXPORTER_OTLP_TRACES_HEADERSOTEL_EXPORTER_OTLP_HEADERSW3C Baggage without ;-separated propertiesIf defined, headers are merged with OTEL_EXPORTER_OTLP_HEADERS, preferring those in OTEL_EXPORTER_OTLP_TRACES_HEADERS
OTEL_EXPORTER_OTLP_METRICS_HEADERSOTEL_EXPORTER_OTLP_HEADERSW3C Baggage without ;-separated propertiesIf defined, headers are merged with OTEL_EXPORTER_OTLP_HEADERS, preferring those in OTEL_EXPORTER_OTLP_METRICS_HEADERS
OTEL_SERVICE_NAMEunknown_serviceAny stringWhen set, the service name sets the service.name property in OTEL_RESOURCE_ATTRIBUTES, overriding any that’s already there.
OTEL_RESOURCE_ATTRIBUTESEmptyW3C Baggage without ;-separated propertiesThe resource will also include values for telemetry.sdk.name, telemetry.sdk.version, and telemetry.sdk.language.

New environment variables that affect configuration may be added in the future.

§Logs

All emit::Events can be represented as OTLP log records. You should at least configure the logs signal to make sure all diagnostics are captured in some way. A minimal logging configuration for gRPC+Protobuf is:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .logs(emit_otlp::logs_grpc_proto("http://localhost:4318"))
    .spawn()

The following diagnostic:

emit::info!("Hello, OTLP!");

will produce the following HTTP+JSON export request:

http://localhost:4318/v1/logs
{
   "resourceLogs": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeLogs": [
            {
               "scope": {
                  "name": "my_app"
               },
               "logRecords": [
                  {
                     "timeUnixNano": 1716804019165847000,
                     "observedTimeUnixNano": 1716804019165847000,
                     "body": {
                        "stringValue": "Hello, OTLP!"
                     },
                     "attributes": [],
                     "severityNumber": 9,
                     "severityText": "info"
                  }
               ]
            }
         ]
      }
   ]
}

When the traces signal is not configured, diagnostic events for spans are represented as regular OTLP log records. The following diagnostic:

#[emit::span("Compute {a} + {b}")]
fn add(a: i32, b: i32) -> i32 {
    let r = a + b;

    emit::info!("Produced {r}", r);

    r
}

add(1, 3);

will produce the following HTTP+JSON export request:

http://localhost:4318/v1/logs
{
   "resourceLogs": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeLogs": [
            {
               "scope": {
                  "name": "my_app"
               },
               "logRecords": [
                  {
                     "timeUnixNano": 1716804240222377000,
                     "observedTimeUnixNano": 1716804240222377000,
                     "body": {
                        "stringValue": "Produced 4"
                     },
                     "attributes": [
                        {
                           "key": "a",
                           "value": {
                              "intValue": 1
                           }
                        },
                        {
                           "key": "b",
                           "value": {
                              "intValue": 3
                           }
                        },
                        {
                           "key": "r",
                           "value": {
                              "intValue": 4
                           }
                        }
                     ],
                     "severityNumber": 9,
                     "severityText": "info",
                     "traceId": "489571cc6b94414ceb4a32ccc2c7df09",
                     "spanId": "a93239061c12aa4c"
                  },
                  {
                     "timeUnixNano": 1716804240222675000,
                     "observedTimeUnixNano": 1716804240222675000,
                     "body": {
                        "stringValue": "Compute 1 + 3"
                     },
                     "attributes": [
                        {
                           "key": "a",
                           "value": {
                              "intValue": 1
                           }
                        },
                        {
                           "key": "b",
                           "value": {
                              "intValue": 3
                           }
                        },
                        {
                           "key": "evt_kind",
                           "value": {
                              "stringValue": "span"
                           }
                        },
                        {
                           "key": "span_name",
                           "value": {
                              "stringValue": "Compute {a} + {b}"
                           }
                        }
                     ],
                     "severityNumber": 9,
                     "severityText": "info",
                     "traceId": "489571cc6b94414ceb4a32ccc2c7df09",
                     "spanId": "a93239061c12aa4c"
                  }
               ]
            }
         ]
      }
   ]
}

When the metrics signal is not configured, diagnostic events for metric samples are represented as regular OTLP log records. The following diagnostic:

emit::emit!(
    evt: emit::Metric::new(
        emit::mdl!(),
        "my_metric",
        "count",
        emit::Empty,
        42,
        emit::Empty,
    )
);

will produce the following HTTP+JSON export request:

http://localhost:4318/v1/logs
{
   "resourceLogs": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeLogs": [
            {
               "scope": {
                  "name": "my_app"
               },
               "logRecords": [
                  {
                     "timeUnixNano": 1716876516012074000,
                     "observedTimeUnixNano": 1716876516012074000,
                     "body": {
                        "stringValue": "count of my_metric is 42"
                     },
                     "attributes": [
                        {
                           "key": "evt_kind",
                           "value": {
                              "stringValue": "metric"
                           }
                        },
                        {
                           "key": "metric_agg",
                           "value": {
                              "stringValue": "count"
                           }
                        },
                        {
                           "key": "metric_name",
                           "value": {
                              "stringValue": "my_metric"
                           }
                        },
                        {
                           "key": "metric_value",
                           "value": {
                              "intValue": 42
                           }
                        }
                     ],
                     "severityNumber": 9,
                     "severityText": "info"
                  }
               ]
            }
         ]
      }
   ]
}

§Traces

When the traces signal is configured, emit::Events can be represented as OTLP spans so long as they satisfy the following conditions:

If any condition is not met, the event will be represented as an OTLP log record. If the logs signal is not configured then it will be discarded.

A minimal logging configuration for gRPC+Protobuf is:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .traces(emit_otlp::traces_grpc_proto("http://localhost:4318"))
    .logs(emit_otlp::logs_grpc_proto("http://localhost:4318"))
    .spawn()

The following diagnostic:

#[emit::span("Compute {a} + {b}")]
fn add(a: i32, b: i32) -> i32 {
    let r = a + b;

    emit::info!("Produced {r}", r);

    r
}

add(1, 3);

will produce the following HTTP+JSON export requests:

http://localhost:4318/v1/traces
{
   "resourceSpans": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeSpans": [
            {
               "scope": {
                  "name": "my_app"
               },
               "spans": [
                  {
                     "name": "Compute {a} + {b}",
                     "kind": 0,
                     "startTimeUnixNano": 1716888416629816000,
                     "endTimeUnixNano": 1716888416630814000,
                     "attributes": [
                        {
                           "key": "a",
                           "value": {
                              "intValue": 1
                           }
                        },
                        {
                           "key": "b",
                           "value": {
                              "intValue": 3
                           }
                        }
                     ],
                     "traceId": "0a85ccaf666e11aaca6bd5d469e2850d",
                     "spanId": "2b9caa35eaefed3a"
                  }
               ]
            }
         ]
      }
   ]
}
http://localhost:4318/v1/logs
{
   "resourceLogs": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeLogs": [
            {
               "scope": {
                  "name": "my_app"
               },
               "logRecords": [
                  {
                     "timeUnixNano": 1716888416630507000,
                     "observedTimeUnixNano": 1716888416630507000,
                     "body": {
                        "stringValue": "Produced 4"
                     },
                     "attributes": [
                        {
                           "key": "a",
                           "value": {
                              "intValue": 1
                           }
                        },
                        {
                           "key": "b",
                           "value": {
                              "intValue": 3
                           }
                        },
                        {
                           "key": "r",
                           "value": {
                              "intValue": 4
                           }
                        }
                     ],
                     "severityNumber": 9,
                     "severityText": "info",
                     "traceId": "0a85ccaf666e11aaca6bd5d469e2850d",
                     "spanId": "2b9caa35eaefed3a"
                  }
               ]
            }
         ]
      }
   ]
}

If the emit::well_known::KEY_ERR property is set, then the resulting OTLP span will carry the semantic exception event:

#[emit::span(guard: span, "Compute {a} + {b}")]
fn add(a: i32, b: i32) -> i32 {
   let r = a + b;

   if r == 4 {
      span.complete_with(emit::span::completion::from_fn(|evt| {
            emit::error!(
               evt,
               "Compute {a} + {b} failed",
               a,
               b,
               r,
               err: "Invalid result",
            );
      }));
   }

   r
}

add(1, 3);
http://localhost:4318/v1/traces
{
   "resourceSpans": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeSpans": [
            {
               "scope": {
                  "name": "my_app"
               },
               "spans": [
                  {
                     "name": "Compute {a} + {b}",
                     "kind": 0,
                     "startTimeUnixNano": 1716936430882852000,
                     "endTimeUnixNano": 1716936430883250000,
                     "attributes": [
                        {
                           "key": "a",
                           "value": {
                              "intValue": 1
                           }
                        },
                        {
                           "key": "b",
                           "value": {
                              "intValue": 3
                           }
                        },
                        {
                           "key": "r",
                           "value": {
                              "intValue": 4
                           }
                        }
                     ],
                     "traceId": "6499bc190add060dad8822600ba65226",
                     "spanId": "b72c5152c32cc432",
                     "events": [
                        {
                           "name": "exception",
                           "timeUnixNano": 1716936430883250000,
                           "attributes": [
                              {
                                 "key": "exception.message",
                                 "value": {
                                    "stringValue": "Invalid result"
                                 }
                              }
                           ]
                        }
                     ],
                     "status": {
                        "message": "Invalid result",
                        "code": 2
                     }
                  }
               ]
            }
         ]
      }
   ]
}

§Metrics

When the metrics signal is configured, emit::Events can be represented as OTLP metrics so long as they satisfy the following conditions:

If any condition is not met, the event will be represented as an OTLP log record. If the logs signal is not configured then it will be discarded.

A minimal logging configuration for gRPC+Protobuf is:

emit_otlp::new()
    .resource(emit::props! {
        #[emit::key("service.name")]
        service_name: emit::pkg!(),
    })
    .metrics(emit_otlp::metrics_grpc_proto("http://localhost:4318"))
    .logs(emit_otlp::logs_grpc_proto("http://localhost:4318"))
    .spawn()

If the metric aggregation is "count" then the resulting OTLP metric is a monotonic sum:

emit::emit!(
    evt: emit::Metric::new(
        emit::mdl!(),
        "my_metric",
        "count",
        emit::Empty,
        42,
        emit::props! {
            a: true
        },
    )
);
http://localhost:4318/v1/metrics
{
   "resourceMetrics": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeMetrics": [
            {
               "scope": {
                  "name": "my_app"
               },
               "metrics": [
                  {
                     "name": "my_metric",
                     "unit": null,
                     "sum": {
                        "dataPoints": [
                           {
                              "attributes": [
                                 {
                                    "key": "a",
                                    "value": {
                                       "boolValue": true
                                    }
                                 }
                              ],
                              "startTimeUnixNano": 1716889540249854000,
                              "timeUnixNano": 1716889540249854000,
                              "value": 42
                           }
                        ],
                        "aggregationTemporality": 2,
                        "isMonotonic": true
                     }
                  }
               ]
            }
         ]
      }
   ]
}

If the metric aggregation is "sum" then the resulting OTLP metric is a non-monotonic sum:

emit::emit!(
    evt: emit::Metric::new(
        emit::mdl!(),
        "my_metric",
        "sum",
        emit::Empty,
        -8,
        emit::props! {
            a: true
        },
    )
);
http://localhost:4318/v1/metrics
{
   "resourceMetrics": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeMetrics": [
            {
               "scope": {
                  "name": "my_app"
               },
               "metrics": [
                  {
                     "name": "my_metric",
                     "unit": null,
                     "sum": {
                        "dataPoints": [
                           {
                              "attributes": [
                                 {
                                    "key": "a",
                                    "value": {
                                       "boolValue": true
                                    }
                                 }
                              ],
                              "startTimeUnixNano": 1716889891391075000,
                              "timeUnixNano": 1716889891391075000,
                              "value": -8
                           }
                        ],
                        "aggregationTemporality": 2,
                        "isMonotonic": false
                     }
                  }
               ]
            }
         ]
      }
   ]
}

Any other aggregation will be represented as an OTLP gauge:

emit::emit!(
    evt: emit::Metric::new(
        emit::mdl!(),
        "my_metric",
        "last",
        emit::Empty,
        615,
        emit::props! {
            a: true
        },
    )
);
http://localhost:4318/v1/metrics
{
   "resourceMetrics": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeMetrics": [
            {
               "scope": {
                  "name": "my_app"
               },
               "metrics": [
                  {
                     "name": "my_metric",
                     "unit": null,
                     "gauge": {
                        "dataPoints": [
                           {
                              "attributes": [
                                 {
                                    "key": "a",
                                    "value": {
                                       "boolValue": true
                                    }
                                 }
                              ],
                              "startTimeUnixNano": 1716890230856380000,
                              "timeUnixNano": 1716890230856380000,
                              "value": 615
                           }
                        ]
                     }
                  }
               ]
            }
         ]
      }
   ]
}

If the metric aggregation is "count" or "sum", and value is a sequence, then each value will be summed to produce a single data point:

let start = emit::Timestamp::from_unix(std::time::Duration::from_secs(1716890420));
let end = emit::Timestamp::from_unix(std::time::Duration::from_secs(1716890425));

emit::emit!(
    evt: emit::Metric::new(
        emit::mdl!(),
        "my_metric",
        "count",
        start..end,
        &[
            1.0,
            1.0,
            1.0,
            1.0,
            1.0,
        ],
        emit::props! {
            a: true
        },
    )
);
http://localhost:4318/v1/metrics
{
   "resourceMetrics": [
      {
         "resource": {
            "attributes": [
               {
                  "key": "service.name",
                  "value": {
                     "stringValue": "my_app"
                  }
               }
            ]
         },
         "scopeMetrics": [
            {
               "scope": {
                  "name": "my_app"
               },
               "metrics": [
                  {
                     "name": "my_metric",
                     "unit": null,
                     "sum": {
                        "dataPoints": [
                           {
                              "attributes": [
                                 {
                                    "key": "a",
                                    "value": {
                                       "boolValue": true
                                    }
                                 }
                              ],
                              "startTimeUnixNano": 1716890420000000000,
                              "timeUnixNano": 1716890425000000000,
                              "value": 5
                           }
                        ],
                        "aggregationTemporality": 1,
                        "isMonotonic": true
                     }
                  }
               ]
            }
         ]
      }
   ]
}

§Limitations

This library is not an alternative to the OpenTelemetry SDK. It’s specifically targeted at emitting diagnostic events to OTLP-compatible services. It has some intentional limitations:

  • No propagation. This is the responsibility of the application to manage.
  • No histogram metrics. emit’s data model for metrics is simplistic compared to OpenTelemetry’s, so it doesn’t support histograms or exponential histograms.
  • No span events. Only the conventional exception event is supported. Standalone log events are not converted into span events. They’re sent via the logs endpoint instead.
  • No tracestate. emit’s data model for spans doesn’t include the W3C tracestate.

§Troubleshooting

If you’re not seeing diagnostics appear in your OTLP receiver, you can rule out configuration issues in emit_otlp by configuring emit’s internal logger, and collect metrics from it:

use emit::metric::Source;

fn main() {
    // 1. Initialize the internal logger
    //    Diagnostics produced by `emit_otlp` itself will go here
    let internal = emit::setup()
        .emit_to(emit_term::stdout())
        .init_internal();

    let mut reporter = emit::metric::Reporter::new();

    let rt = emit::setup()
        .emit_to({
            let otlp = emit_otlp::new()
                .resource(emit::props! {
                    #[emit::key("service.name")]
                    service_name: emit::pkg!(),
                })
                .logs(emit_otlp::logs_grpc_proto("http://localhost:4319"))
                .traces(emit_otlp::traces_grpc_proto("http://localhost:4319"))
                .metrics(emit_otlp::metrics_grpc_proto("http://localhost:4319"))
                .spawn();

            // 2. Add `emit_otlp`'s metrics to a reporter so we can see what it's up to
            //    You can do this independently of the internal emitter
            reporter.add_source(otlp.metric_source());

            otlp
        })
        .init();

    // Your app code goes here

    rt.blocking_flush(std::time::Duration::from_secs(30));

    // 3. Report metrics after attempting to flush
    //    You could also do this periodically as your application runs
    reporter.emit_metrics(&internal.emitter());
}

Diagnostics include when batches are emitted, and any failures observed along the way.

Structs§

Error
An error attempting to configure a crate::Otlp instance.
Otlp
An emit::Emitter that sends diagnostic events via the OpenTelemetry Protocol (OTLP).
OtlpBuilder
A builder for Otlp.
OtlpLogsBuilder
A builder for the logs signal.
OtlpMetrics
Metrics produced by an OTLP emitter itself.
OtlpMetricsBuilder
A builder for the metrics signal.
OtlpTracesBuilder
A builder for the traces signal.
OtlpTransportBuilder
A builder for an OTLP transport channel, either HTTP or gRPC.

Functions§

from_env
Start a builder for an Otlp emitter with configuration from OpenTelemetry’s environment variables for all signals.
grpc
Get a transport builder for gRPC.
http
Get a transport builder for HTTP.
logs_from_env
Get a logs signal builder from OpenTelemetry’s environment variables.
logs_grpc_proto
Get a logs signal builder for gRPC+protobuf.
logs_http_json
Get a logs signal builder for HTTP+JSON.
logs_http_proto
Get a logs signal builder for HTTP+protobuf.
logs_json
Get a logs signal builder for the given transport with JSON encoding.
logs_proto
Get a logs signal builder for the given transport with protobuf encoding.
metrics_from_env
Get a metrics signal builder from OpenTelemetry’s environment variables.
metrics_grpc_proto
Get a metrics signal builder for gRPC+protobuf.
metrics_http_json
Get a metrics signal builder for HTTP+JSON.
metrics_http_proto
Get a metrics signal builder for HTTP+protobuf.
metrics_json
Get a metrics signal builder for the given transport with JSON encoding.
metrics_proto
Get a metrics signal builder for the given transport with protobuf encoding.
new
Start a builder for an Otlp emitter.
resource_from_env
Read resource attributes from OpenTelemetry’s environment variables.
telemetry_sdk_language
A value to use as telemetry.sdk.language in OtlpBuilder::resource.
telemetry_sdk_name
A value to use as telemetry.sdk.name in OtlpBuilder::resource.
telemetry_sdk_version
A value to use as telemetry.sdk.version in OtlpBuilder::resource.
traces_from_env
Get a traces signal builder from OpenTelemetry’s environment variables.
traces_grpc_proto
Get a traces signal builder for gRPC+protobuf.
traces_http_json
Get a traces signal builder for HTTP+JSON.
traces_http_proto
Get a traces signal builder for HTTP+protobuf.
traces_json
Get a traces signal builder for the given transport with JSON encoding.
traces_proto
Get a traces signal builder for the given transport with protobuf encoding.