Skip to content

feat(opentelemetry-exporter-prometheus): add translation strategy support#6653

Open
cjihrig wants to merge 1 commit into
open-telemetry:mainfrom
cjihrig:prom-translation-strategy
Open

feat(opentelemetry-exporter-prometheus): add translation strategy support#6653
cjihrig wants to merge 1 commit into
open-telemetry:mainfrom
cjihrig:prom-translation-strategy

Conversation

@cjihrig
Copy link
Copy Markdown
Contributor

@cjihrig cjihrig commented Apr 30, 2026

Which problem is this PR solving?

The Prometheus exporter does not currently support Translation Strategy.

Related to #6605

Short description of the changes

This PR adds support for Translation Strategy in a way that can also work with Content Negotiation in the future if necessary.

I am opening as a draft because I need to add more tests for the non-default behavior. There is also the issue of adding unit suffixes. I have another branch that implements unit suffixes, but turning them on by default (which is the spec behavior) would be a breaking change, so that will need to be figured out too.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Existing and new tests

Checklist:

  • Followed the style guidelines of this project
  • Unit tests have been added
  • Documentation has been updated

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 89.23077% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.50%. Comparing base (acc9ecd) to head (f8d727a).

Files with missing lines Patch % Lines
...ry-exporter-prometheus/src/PrometheusSerializer.ts 90.90% 5 Missing ⚠️
...etry-exporter-prometheus/src/PrometheusExporter.ts 77.77% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6653      +/-   ##
==========================================
- Coverage   95.54%   95.50%   -0.04%     
==========================================
  Files         372      373       +1     
  Lines       12339    12377      +38     
  Branches     2829     2850      +21     
==========================================
+ Hits        11789    11821      +32     
- Misses        550      556       +6     
Files with missing lines Coverage Δ
...ntelemetry-exporter-prometheus/src/export/types.ts 100.00% <100.00%> (ø)
...etry-exporter-prometheus/src/PrometheusExporter.ts 94.94% <77.77%> (-1.76%) ⬇️
...ry-exporter-prometheus/src/PrometheusSerializer.ts 94.35% <90.90%> (-1.43%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cjihrig cjihrig force-pushed the prom-translation-strategy branch from 966df4c to f8d727a Compare May 4, 2026 01:57
}

function escapeQuotes(str: string) {
return str.replace(/"/g, '\\"');
@cjihrig cjihrig marked this pull request as ready for review May 4, 2026 02:01
@cjihrig cjihrig requested a review from a team as a code owner May 4, 2026 02:01
Copy link
Copy Markdown
Member

@ArthurSens ArthurSens left a comment

Choose a reason for hiding this comment

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

Hello, hello, thanks for starting the effort!

I don't know JS at all, so I can't do a very complete review.

Looking at your tests, and comparing them with the ones we did for go (https://github.com/prometheus/otlptranslator/blob/main/metric_namer_test.go), it seems like a lot of edge cases aren't covered here. Is it worth covering them?

Comment on lines +30 to +38
// These should be sanitized, even if escaping is disabled.
const ATTR_OTEL_SCOPE_NAME_LABEL =
sanitizePrometheusMetricName(ATTR_OTEL_SCOPE_NAME);
const ATTR_OTEL_SCOPE_VERSION_LABEL = sanitizePrometheusMetricName(
ATTR_OTEL_SCOPE_VERSION
);
const ATTR_OTEL_SCOPE_SCHEMA_URL_LABEL = sanitizePrometheusMetricName(
ATTR_OTEL_SCOPE_SCHEMA_URL
);
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.

Could you clarify why this needs to be sanitized even if the translation strategy says no escaping?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure. These constants (ATTR_OTEL_SCOPE_NAME, etc.) have dots in them. According to this part of the spec:

Prometheus exporters MUST by default add the scope name as the otel_scope_name label, the scope version as the otel_scope_version label, the scope schema URL as the otel_scope_schema_url label, the scope attributes as labels with otel_scope_ prefix and following the rules described in the Metric Attributes section below, on all metric points, based on the scope the original data point was nested in.

To me, that is a bit ambiguous, but seems like the names should have underscores. It also seems that Golang unconditionally sanitizes these. See this snapshot for example, where dots are preserved everywhere but the scope attributes.

@cjihrig
Copy link
Copy Markdown
Contributor Author

cjihrig commented May 14, 2026

it seems like a lot of edge cases aren't covered here. Is it worth covering them?

I can add more tests. Are there any in particular that you think are worth porting (since that file is 1200+ lines and this PR doesn't implement all of that functionality)? It's also worth noting that I have a separate branch to add unit suffix support which would bring additional tests.

@ArthurSens
Copy link
Copy Markdown
Member

Yep, 1200 LOC is a lot 😅. Let me try to parse that into human-readable text:

Underscore Scaping:

  • metric@with#special$chars -> metric_with_special_chars
  • 123metric -> _123metric (metric names starting with digit are unnallowed unless UTF-8 is enabled)
  • `` (empty) -> Should error
  • metric@@##$$name -> metric_name (multiple special characters become a single underscore)
  • @#$% -> Should error since it's only special characters, translating into a single underscore

No underscore scaping:

  • The cases above are allowed untransformed, besides the empty string

if (hasAttribute) {
if (hasNonLegacyCharacters(metricName)) {
if (hasAttribute) {
metricName = `{${quoteName(metricName)} ${attributesStr}}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the separator inside the braces might be off here. When the metric name is quoted (UTF-8 form), Prometheus expects a comma between the name and the rest of the labels, not a space:

{"test.dotted_total",otel_scope_name="test"} 1

The current output (and what the new tests assert) is:

{"test.dotted_total" otel_scope_name="test"} 1

which I don't think Prometheus/PromQL parsers will accept. Worth double checking against the exposition format spec and updating both stringify() and the new test fixtures if it's indeed wrong.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I'll fix this.

@cjihrig
Copy link
Copy Markdown
Contributor Author

cjihrig commented May 19, 2026

@ArthurSens thank you for the feedback. I've added those tests, but I did it in a separate PR (#6727) because even without the translation strategy, I believe there may be a few edge case bugs in the existing implementation.

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.

4 participants