Skip to content

feat: add opentelemetry-otlp-file crate implementing the OTLP File Exporter - #3763

Open
garysassano wants to merge 20 commits into
open-telemetry:mainfrom
garysassano:feat/otlp-file-exporter
Open

garysassano wants to merge 20 commits into
open-telemetry:mainfrom
garysassano:feat/otlp-file-exporter

Conversation

@garysassano

@garysassano garysassano commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Closes #2602
Design discussion issue (if applicable) #2602

Changes

Adds a new crate, opentelemetry-otlp-file, that implements the OTLP File Exporter specification (Development status) for traces, metrics and logs.

  • Format: SpanExporter, MetricExporter and LogExporter write each export as one line of compact OTLP JSON: a TracesData, MetricsData or LogsData object followed by \n.
  • Destinations: output defaults to stdout, as the spec requires. with_file(path) appends to a file, creating it if needed, and opens it in build() so a bad path fails early. with_writer(w) accepts any std::io::Write + Send + 'static.
  • Writes: each line is serialized first, then written and flushed under the writer's lock (and the stdout lock on stdout), so it is complete when export returns and does not interleave with other exports or println! output. If a write fails partway through a line, the export returns an error and the rest of the line is written before the next one, so later lines stay valid. Empty batches write nothing.
  • Shutdown: flushes and drops the writer; later exports return AlreadyShutdown.
  • Configuration: MetricExporter reads OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE with the same precedence as opentelemetry-otlp (builder, then environment, then cumulative).
  • Encoding: reuses the opentelemetry-proto with-serde types and transforms, the same path as http-json. No new dependencies are added to the workspace.
  • Docs: the README covers destinations, configuration, the Collector receiver's max_log_size limit, blocking writes with the async-runtime processors, and running on AWS Lambda, including the trade-offs between flushing strategies.

Consistency with other SDKs

  • Python (opentelemetry-exporter-otlp-json-file): writes to a path, a stream or stdout, and flushes after every export. This crate does the same.
  • Java (OtlpStdout*Exporter): writes to any OutputStream and never closes stdout. with_writer and the stdout handling match this.
  • C++: also offers file rotation. It is not included here because the spec does not ask for it.

Why a separate crate

The exporter needs none of the transport configuration in opentelemetry-otlp (endpoint, headers, timeout, retry, TLS), and opentelemetry-otlp is being stabilized while this spec is still Development, so a separate crate keeps it out of that API. Python and Java also ship it as a separate package. In review, @lalitb agreed with keeping it as a separate crate in this repo.

Not covered

Testing

  • Unit tests per signal: encoding, a round trip through opentelemetry-proto deserialization, empty batches, shutdown, file create/append/open errors and concurrent writes.
  • Lints: Clippy passes with -D warnings for each feature on its own and with all features, and the workspace lib tests pass.
  • Collector interop: output covering every attribute type, events, links, error status, all metric kinds including exponential histograms (cumulative and delta) and structured log bodies was read by the otlp_json_file receiver in Collector contrib 0.161.0. Diffing what its file exporter wrote back against the input showed no differences for any signal.
  • AWS Lambda (provided.al2023, arm64) with all three exporters on stdout and force_flush at the end of each invocation: every line arrived in CloudWatch Logs as a single log event with both the Text and JSON log formats, and the extracted lines passed the same Collector check. Lines longer than the roughly 256 KiB CloudWatch event limit are split across events, which the README points out.
  • Collector line limit: with Collector contrib 0.162.0, a 2.3 MiB traces line was dropped without an error under the receiver's default 1 MiB max_log_size and received in full with max_log_size: 4MiB, which the README now documents.
  • Async runtimes: with the async-runtime batch span processor on Tokio and a writer that blocks for 3 s, a 500 ms export timeout did not fire, which the README now documents.
  • MSRV and public API: the crate builds with Rust 1.75, and cargo check-external-types (nightly-2025-05-04) passes with the new allowed-external-types.toml, which I added to the CI matrix.

Notes

This was developed with AI assistance (Claude); I reviewed and tested the changes.

Merge requirement checklist

  • CONTRIBUTING guidelines followed
  • Unit tests added/updated (if applicable)
  • Appropriate CHANGELOG.md files updated for non-trivial, user-facing changes
  • Changes in public API reviewed (if applicable)

…porter

SpanExporter, MetricExporter and LogExporter write each export as one
line of OTLP JSON (TracesData, MetricsData or LogsData) to stdout, to a
file opened for appending, or to any std::io::Write implementation, as
described by the OpenTelemetry Protocol File Exporter specification.
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Pull request dashboard status

Waiting on reviewers · refreshed 2026-10-23 20:38 UTC

Review the latest changes.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

@codecov

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.86434% with 53 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.4%. Comparing base (0fe5989) to head (06c305a).

Files with missing lines Patch % Lines
opentelemetry-otlp-file/src/metrics.rs 90.9% 22 Missing ⚠️
opentelemetry-otlp-file/src/output.rs 96.7% 13 Missing ⚠️
opentelemetry-otlp-file/src/logs.rs 92.8% 12 Missing ⚠️
opentelemetry-otlp-file/src/trace.rs 97.3% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##            main   #3763     +/-   ##
=======================================
+ Coverage   84.4%   85.4%   +0.9%     
=======================================
  Files        124     128      +4     
  Lines      30708   31740   +1032     
=======================================
+ Hits       25925   27111   +1186     
+ Misses      4783    4629    -154     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@garysassano garysassano changed the title feat: add opentelemetry-otlp-file crate implementing the OTLP File Exporter feat: add opentelemetry-otlp-file crate implementing the OTLP File Exporter Sep 29, 2026
SystemTime has 100 ns resolution on Windows, so a start time ending in
...321 ns came back as ...300 and failed the encoding assertion there.
@garysassano
garysassano force-pushed the feat/otlp-file-exporter branch from 2166952 to e430738 Compare September 29, 2026 20:04
@garysassano
garysassano marked this pull request as ready for review September 29, 2026 20:30
@garysassano
garysassano requested a review from a team as a code owner September 29, 2026 20:30

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @garysassano thanks for the PR! -i've put some initial feedback inline but it's generally looking good! I reckon the biggest thing to agree on to move forward is where this belongs - contrib, here, or as an experimental feature on the OTLP crate. I'm kind of leaning towards the latter to avoid duplication.

Have tagged the rest of the maintainers to express opinions so we can unblock you

@@ -0,0 +1,53 @@
[package]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should host this in contrib or as an unstable feature in the OTLP crate (which will help with re-use), in particular because the spec is still in development.

Reviewing it inline anyhow as I don't think it should affect the actual contents much, but it would be good to align on this - @open-telemetry/rust-maintainers thoughts?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to move it wherever you prefer. If it goes into opentelemetry-otlp, I'd suggest a separate file module behind its own feature flag (e.g. opentelemetry_otlp::file::SpanExporter) rather than a third option next to with_tonic()/with_http() on the existing builders, since it shares none of the endpoint, headers, timeout or TLS configuration. That would also let it reuse the env-var helpers you mention in the other comment. The move itself is mostly mechanical, since the encoding already goes through the same opentelemetry-proto with-serde types as http-json, so I'll wait for the decision before changing anything.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d keep this as a separate crate in this repo, unless other maintainers have concerns. The file exporter has a distinct configuration and dependency surface from HTTP/gRPC, and it already reuses the proto transforms. The small amount of duplicated environment-variable handling doesn’t seem sufficient reason to put it in opentelemetry-otlp or move that policy into the SDK. From my side, @garysassano can proceed with the current placement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @lalitb, I'll keep it as a separate crate here then. The Development status is called out at the top of the README and in the crate docs. I'll leave this thread open in case @scottgerring wants to weigh in, since he leaned towards the OTLP crate.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for me !

Comment thread opentelemetry-otlp-file/README.md Outdated
Comment thread opentelemetry-otlp-file/Cargo.toml Outdated
Comment thread opentelemetry-otlp-file/src/error.rs Outdated
Comment thread opentelemetry-otlp-file/src/metrics.rs
Comment thread opentelemetry-otlp-file/src/output.rs Outdated
Comment thread opentelemetry-otlp-file/README.md Outdated
Comment thread opentelemetry-otlp-file/README.md Outdated
@@ -0,0 +1,53 @@
[package]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d keep this as a separate crate in this repo, unless other maintainers have concerns. The file exporter has a distinct configuration and dependency surface from HTTP/gRPC, and it already reuses the proto transforms. The small amount of duplicated environment-variable handling doesn’t seem sufficient reason to put it in opentelemetry-otlp or move that policy into the SDK. From my side, @garysassano can proceed with the current placement.

@opentelemetry-pr-dashboard

This comment has been minimized.

@garysassano

Copy link
Copy Markdown
Member Author

/dashboard route:reviewers

@opentelemetry-pr-dashboard

Copy link
Copy Markdown

@garysassano, this pull request was routed to reviewers. The handoff remains active across pushes until newer actionable human feedback arrives. Top-level feedback through this request will not return; unresolved review threads remain open.

Comment thread opentelemetry-otlp-file/src/output.rs
Comment thread opentelemetry-otlp-file/src/output.rs Outdated
Comment thread opentelemetry-otlp-file/src/output.rs
Comment thread opentelemetry-otlp-file/src/logs.rs
@opentelemetry-pr-dashboard

This comment has been minimized.

Comment thread opentelemetry-otlp-file/src/metrics.rs Outdated
.with_service_name("checkout")
.build(),
)
.with_periodic_exporter(exporter)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use with_periodic_exporter, so they inherit OTEL_METRIC_EXPORT_INTERVAL. With OTEL_METRIC_EXPORT_INTERVAL=0, timer collections make three metric tests fail, including this one producing [3, 3, 5, 5] instead of [3, 5]. Could the tests build a PeriodicReader explicitly with a fixed long interval, or otherwise isolate the environment variable, so their assertions are deterministic in developer and CI environments?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — reproduced it: with OTEL_METRIC_EXPORT_INTERVAL=0 three metric tests failed. The tests now build the PeriodicReader explicitly with a one-hour interval (with_interval documents that it overrides the environment variable), so only force_flush and shutdown collect. Fixed in 9fd33e2.

@scottgerring

Copy link
Copy Markdown
Member

One release-integration item: scripts/publish.sh explicitly enumerates the published crates but does not include opentelemetry-otlp-file, so the documented release automation would skip this new crate. Could you add it to the package list after opentelemetry-appender-tracing, once its runtime dependencies and appender dev-dependency have been published?

The tests built the provider with `with_periodic_exporter`, so they inherited
`OTEL_METRIC_EXPORT_INTERVAL`. With the variable set to 0 the background timer
collected on its own and the assertions saw extra exports. Build the
`PeriodicReader` explicitly with a long interval, which overrides the variable,
so only `force_flush` and `shutdown` collect.
Add the crate to the release script, after `opentelemetry-appender-tracing`:
the last of its runtime dependencies and its dev-dependency to be published.
@garysassano

Copy link
Copy Markdown
Member Author

scripts/publish.sh explicitly enumerates the published crates but does not include opentelemetry-otlp-file

Added in 9099bf6, right after opentelemetry-appender-tracing — the last of its runtime dependencies and its dev-dependency to be published.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: OTLP File Exporter

4 participants