Skip to content

Commit 41d4b00

Browse files
[pkg/translator/jaeger] migrate semantic convention from v1.25.0 to v1.40.0 (#49301)
Fixes #45036 `http.response.status_code` is the replacement atttribute for `http.status_code` Here is the reference link: https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/#http-status-code #### Authorship - [x] I, a human, wrote this pull request description myself. --------- Signed-off-by: singhvibhanshu <find.vibhanshu@gmail.com>
1 parent 4cf61e3 commit 41d4b00

13 files changed

Lines changed: 306 additions & 31 deletions

.chloggen/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ components:
159159
- pkg/faro
160160
- pkg/fileconsumer
161161
- pkg/golden
162+
- pkg/jaeger
162163
- pkg/kafka/configkafka
163164
- pkg/otelarrow
164165
- pkg/ottl
@@ -173,7 +174,6 @@ components:
173174
- pkg/status
174175
- pkg/topic
175176
- pkg/translator/azure
176-
- pkg/translator/jaeger
177177
- pkg/translator/loki
178178
- pkg/translator/pprof
179179
- pkg/translator/prometheusremotewrite

.chloggen/migrateHTTP-45036.yaml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. receiver/filelog)
7+
component: pkg/jaeger
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Migrate http.status_code (v1.25.0) semantic convention to http.response.status_code (v1.40.0)
11+
12+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
13+
issues: [45036]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
The migration is gated behind two alpha (disabled by default) feature gates:
20+
enable `pkg.translator.jaeger.EmitV1HttpConventions` to emit `http.response.status_code`, and
21+
additionally enable `pkg.translator.jaeger.DontEmitV0HttpConventions` to stop emitting the
22+
deprecated `http.status_code`.
23+
Both gates will graduate to beta (enabled by default) and eventually be removed, after which the
24+
translator will only emit `http.response.status_code`. That removal is a breaking change for
25+
anything still relying on `http.status_code`, so please migrate your dashboards, alerts and
26+
queries now.
27+
28+
# If your change doesn't affect end users or the exported elements of any package,
29+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
30+
# Optional: The change log or logs in which this entry should be included.
31+
# e.g. '[user]' or '[user, api]'
32+
# Include 'user' if the change is relevant to end users.
33+
# Include 'api' if there is a change to a library API.
34+
# Default: '[user]'
35+
change_logs: []
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)
2+
3+
# jaeger
4+
5+
## Feature Gates
6+
7+
This component has the following feature gates:
8+
9+
| Feature Gate | Stage | Description | From Version | To Version | Reference |
10+
| ------------ | ----- | ----------- | ------------ | ---------- | --------- |
11+
| `pkg.translator.jaeger.DontEmitV0HttpConventions` | alpha | When enabled, the Jaeger translator no longer emits the deprecated http.status_code attribute (semconv v1.25.0). Requires pkg.translator.jaeger.EmitV1HttpConventions to also be enabled. | v0.158.0 | N/A | [Link](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/45036) |
12+
| `pkg.translator.jaeger.EmitV1HttpConventions` | alpha | When enabled, the Jaeger translator emits http.response.status_code (semconv v1.40.0) instead of the deprecated http.status_code (semconv v1.25.0). | v0.158.0 | N/A | [Link](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/45036) |
13+
14+
For more information about feature gates, see the [Feature Gates](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md) documentation.

pkg/translator/jaeger/package_test.go renamed to pkg/translator/jaeger/generated_package_test.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/translator/jaeger/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.157.0
1010
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils v0.157.0
1111
github.com/stretchr/testify v1.11.1
12+
go.opentelemetry.io/collector/featuregate v1.63.1-0.20260723141305-52e6bf4aaaba
1213
go.opentelemetry.io/collector/pdata v1.63.1-0.20260723141305-52e6bf4aaaba
1314
go.opentelemetry.io/otel v1.44.0
1415
go.uber.org/goleak v1.3.0
@@ -26,7 +27,6 @@ require (
2627
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
2728
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
2829
github.com/rogpeppe/go-internal v1.10.0 // indirect
29-
go.opentelemetry.io/collector/featuregate v1.63.1-0.20260723141305-52e6bf4aaaba // indirect
3030
go.opentelemetry.io/collector/pdata/pprofile v0.157.1-0.20260723141305-52e6bf4aaaba // indirect
3131
go.uber.org/multierr v1.11.0 // indirect
3232
golang.org/x/net v0.55.0 // indirect

pkg/translator/jaeger/internal/metadata/generated_feature_gates.go

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/translator/jaeger/jaegerproto_to_traces.go

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/occonventions"
2121
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/tracetranslator"
2222
idutils "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils"
23+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/internal/metadata"
2324
)
2425

2526
var blankJaegerProtoSpan = new(model.Span)
@@ -234,19 +235,32 @@ func jSpanToInternal(span *model.Span, dest ptrace.Span) {
234235

235236
func jTagsToInternalAttributes(tags []model.KeyValue, dest pcommon.Map) {
236237
for _, tag := range tags {
237-
switch tag.GetVType() {
238-
case model.ValueType_STRING:
239-
dest.PutStr(tag.Key, tag.GetVStr())
240-
case model.ValueType_BOOL:
241-
dest.PutBool(tag.Key, tag.GetVBool())
242-
case model.ValueType_INT64:
243-
dest.PutInt(tag.Key, tag.GetVInt64())
244-
case model.ValueType_FLOAT64:
245-
dest.PutDouble(tag.Key, tag.GetVFloat64())
246-
case model.ValueType_BINARY:
247-
dest.PutEmptyBytes(tag.Key).FromRaw(tag.GetVBinary())
248-
default:
249-
dest.PutStr(tag.Key, fmt.Sprintf("<Unknown Jaeger TagType %q>", tag.GetVType()))
238+
keys := []string{tag.Key}
239+
if tag.Key == string(conventionsv125.HTTPStatusCodeKey) || tag.Key == string(conventions.HTTPResponseStatusCodeKey) {
240+
keys = nil
241+
if !metadata.PkgTranslatorJaegerDontEmitV0HTTPConventionsFeatureGate.IsEnabled() {
242+
keys = append(keys, string(conventionsv125.HTTPStatusCodeKey))
243+
}
244+
if metadata.PkgTranslatorJaegerEmitV1HTTPConventionsFeatureGate.IsEnabled() {
245+
keys = append(keys, string(conventions.HTTPResponseStatusCodeKey))
246+
}
247+
}
248+
249+
for _, key := range keys {
250+
switch tag.GetVType() {
251+
case model.ValueType_STRING:
252+
dest.PutStr(key, tag.GetVStr())
253+
case model.ValueType_BOOL:
254+
dest.PutBool(key, tag.GetVBool())
255+
case model.ValueType_INT64:
256+
dest.PutInt(key, tag.GetVInt64())
257+
case model.ValueType_FLOAT64:
258+
dest.PutDouble(key, tag.GetVFloat64())
259+
case model.ValueType_BINARY:
260+
dest.PutEmptyBytes(key).FromRaw(tag.GetVBinary())
261+
default:
262+
dest.PutStr(key, fmt.Sprintf("<Unknown Jaeger TagType %q>", tag.GetVType()))
263+
}
250264
}
251265
}
252266
}
@@ -292,7 +306,7 @@ func setInternalSpanStatus(attrs pcommon.Map, span ptrace.Span) {
292306
// otel.status_message tag will have already been removed if
293307
// statusExists is true.
294308
attrs.Remove(string(conventions.OTelStatusCodeKey))
295-
} else if httpCodeAttr, ok := attrs.Get(string(conventionsv125.HTTPStatusCodeKey)); !statusExists && ok {
309+
} else if httpCodeAttr, ok := getHTTPStatusCodeAttr(attrs); !statusExists && ok {
296310
// Fallback to introspecting if this span represents a failed HTTP
297311
// request or response, but again, only do so if the `error` tag was
298312
// not set to true and no explicit status was sent.
@@ -347,6 +361,18 @@ func codeFromAttr(attrVal pcommon.Value) (int64, error) {
347361
return val, nil
348362
}
349363

364+
// getHTTPStatusCodeAttr returns the HTTP status code attribute value, checking
365+
// both the deprecated http.status_code (semconv v1.25.0) and the current
366+
// http.response.status_code (semconv v1.40.0) keys. This ensures span status is
367+
// still derived from the HTTP status code regardless of which HTTP semantic
368+
// conventions the Jaeger translator is configured to emit.
369+
func getHTTPStatusCodeAttr(attrs pcommon.Map) (pcommon.Value, bool) {
370+
if attr, ok := attrs.Get(string(conventionsv125.HTTPStatusCodeKey)); ok {
371+
return attr, true
372+
}
373+
return attrs.Get(string(conventions.HTTPResponseStatusCodeKey))
374+
}
375+
350376
func getStatusCodeFromHTTPStatusAttr(attrVal pcommon.Value, kind ptrace.SpanKind) (ptrace.StatusCode, error) {
351377
statusCode, err := codeFromAttr(attrVal)
352378
if err != nil {

pkg/translator/jaeger/jaegerproto_to_traces_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/jaegertracing/jaeger-idl/model/v1"
1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/require"
16+
"go.opentelemetry.io/collector/featuregate"
1617
"go.opentelemetry.io/collector/pdata/pcommon"
1718
"go.opentelemetry.io/collector/pdata/ptrace"
1819

@@ -446,6 +447,14 @@ func TestSetInternalSpanStatus(t *testing.T) {
446447
status: errorStatus,
447448
attrsModifiedLen: 1,
448449
},
450+
{
451+
name: "http.response.status_code tag is set as string (v1 conventions)",
452+
attrs: map[string]any{
453+
"http.response.status_code": "404",
454+
},
455+
status: errorStatus,
456+
attrsModifiedLen: 1,
457+
},
449458
{
450459
name: "http.status_code, http.status_message and error tags are set",
451460
attrs: map[string]any{
@@ -1093,3 +1102,39 @@ func generateTracesTwoSpansFromTwoLibraries() ptrace.Traces {
10931102

10941103
return td
10951104
}
1105+
1106+
func TestJTagsToInternalAttributesHTTPConventionsMigration(t *testing.T) {
1107+
cases := []struct {
1108+
name string
1109+
emitV1 bool
1110+
dontEmitV0 bool
1111+
expectsV0 bool
1112+
expectsV1 bool
1113+
}{
1114+
{name: "v0 only", emitV1: false, dontEmitV0: false, expectsV0: true, expectsV1: false},
1115+
{name: "double publish", emitV1: true, dontEmitV0: false, expectsV0: true, expectsV1: true},
1116+
{name: "v1 only", emitV1: true, dontEmitV0: true, expectsV0: false, expectsV1: true},
1117+
}
1118+
1119+
for _, tc := range cases {
1120+
t.Run(tc.name, func(t *testing.T) {
1121+
err := featuregate.GlobalRegistry().Set("pkg.translator.jaeger.EmitV1HttpConventions", tc.emitV1)
1122+
assert.NoError(t, err)
1123+
err = featuregate.GlobalRegistry().Set("pkg.translator.jaeger.DontEmitV0HttpConventions", tc.dontEmitV0)
1124+
assert.NoError(t, err)
1125+
t.Cleanup(func() {
1126+
assert.NoError(t, featuregate.GlobalRegistry().Set("pkg.translator.jaeger.DontEmitV0HttpConventions", false))
1127+
assert.NoError(t, featuregate.GlobalRegistry().Set("pkg.translator.jaeger.EmitV1HttpConventions", false))
1128+
})
1129+
1130+
tags := []model.KeyValue{{Key: "http.status_code", VType: model.ValueType_STRING, VStr: "500"}}
1131+
dest := pcommon.NewMap()
1132+
jTagsToInternalAttributes(tags, dest)
1133+
1134+
_, hasV0 := dest.Get("http.status_code")
1135+
_, hasV1 := dest.Get("http.response.status_code")
1136+
assert.Equal(t, tc.expectsV0, hasV0)
1137+
assert.Equal(t, tc.expectsV1, hasV1)
1138+
})
1139+
}
1140+
}

pkg/translator/jaeger/jaegerthrift_to_traces.go

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import (
1111
"github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger"
1212
"go.opentelemetry.io/collector/pdata/pcommon"
1313
"go.opentelemetry.io/collector/pdata/ptrace"
14+
conventionsv125 "go.opentelemetry.io/otel/semconv/v1.25.0"
1415
conventions "go.opentelemetry.io/otel/semconv/v1.40.0"
1516

1617
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/tracetranslator"
1718
idutils "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/core/xidutils"
19+
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger/internal/metadata"
1820
)
1921

2022
var blankJaegerThriftSpan = new(jaeger.Span)
@@ -144,19 +146,32 @@ func jThriftSpanToInternal(span *jaeger.Span, dest ptrace.Span) {
144146
// jThriftTagsToInternalAttributes sets internal span links based on jaeger span references skipping excludeParentID
145147
func jThriftTagsToInternalAttributes(tags []*jaeger.Tag, dest pcommon.Map) {
146148
for _, tag := range tags {
147-
switch tag.GetVType() {
148-
case jaeger.TagType_STRING:
149-
dest.PutStr(tag.Key, tag.GetVStr())
150-
case jaeger.TagType_BOOL:
151-
dest.PutBool(tag.Key, tag.GetVBool())
152-
case jaeger.TagType_LONG:
153-
dest.PutInt(tag.Key, tag.GetVLong())
154-
case jaeger.TagType_DOUBLE:
155-
dest.PutDouble(tag.Key, tag.GetVDouble())
156-
case jaeger.TagType_BINARY:
157-
dest.PutEmptyBytes(tag.Key).FromRaw(tag.GetVBinary())
158-
default:
159-
dest.PutStr(tag.Key, fmt.Sprintf("<Unknown Jaeger TagType %q>", tag.GetVType()))
149+
keys := []string{tag.Key}
150+
if tag.Key == string(conventionsv125.HTTPStatusCodeKey) || tag.Key == string(conventions.HTTPResponseStatusCodeKey) {
151+
keys = nil
152+
if !metadata.PkgTranslatorJaegerDontEmitV0HTTPConventionsFeatureGate.IsEnabled() {
153+
keys = append(keys, string(conventionsv125.HTTPStatusCodeKey))
154+
}
155+
if metadata.PkgTranslatorJaegerEmitV1HTTPConventionsFeatureGate.IsEnabled() {
156+
keys = append(keys, string(conventions.HTTPResponseStatusCodeKey))
157+
}
158+
}
159+
160+
for _, key := range keys {
161+
switch tag.GetVType() {
162+
case jaeger.TagType_STRING:
163+
dest.PutStr(key, tag.GetVStr())
164+
case jaeger.TagType_BOOL:
165+
dest.PutBool(key, tag.GetVBool())
166+
case jaeger.TagType_LONG:
167+
dest.PutInt(key, tag.GetVLong())
168+
case jaeger.TagType_DOUBLE:
169+
dest.PutDouble(key, tag.GetVDouble())
170+
case jaeger.TagType_BINARY:
171+
dest.PutEmptyBytes(key).FromRaw(tag.GetVBinary())
172+
default:
173+
dest.PutStr(key, fmt.Sprintf("<Unknown Jaeger TagType %q>", tag.GetVType()))
174+
}
160175
}
161176
}
162177
}

pkg/translator/jaeger/jaegerthrift_to_traces_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/jaegertracing/jaeger-idl/thrift-gen/jaeger"
1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
13+
"go.opentelemetry.io/collector/featuregate"
1314
"go.opentelemetry.io/collector/pdata/pcommon"
1415
"go.opentelemetry.io/collector/pdata/ptrace"
1516

@@ -343,3 +344,41 @@ func BenchmarkThriftBatchToInternalTraces(b *testing.B) {
343344
assert.NoError(b, err)
344345
}
345346
}
347+
348+
func TestJThriftTagsToInternalAttributesHTTPConventionsMigration(t *testing.T) {
349+
cases := []struct {
350+
name string
351+
emitV1 bool
352+
dontEmitV0 bool
353+
expectsV0 bool
354+
expectsV1 bool
355+
}{
356+
{name: "v0 only", emitV1: false, dontEmitV0: false, expectsV0: true, expectsV1: false},
357+
{name: "double publish", emitV1: true, dontEmitV0: false, expectsV0: true, expectsV1: true},
358+
{name: "v1 only", emitV1: true, dontEmitV0: true, expectsV0: false, expectsV1: true},
359+
}
360+
361+
for _, tc := range cases {
362+
t.Run(tc.name, func(t *testing.T) {
363+
err := featuregate.GlobalRegistry().Set("pkg.translator.jaeger.EmitV1HttpConventions", tc.emitV1)
364+
assert.NoError(t, err)
365+
err = featuregate.GlobalRegistry().Set("pkg.translator.jaeger.DontEmitV0HttpConventions", tc.dontEmitV0)
366+
assert.NoError(t, err)
367+
t.Cleanup(func() {
368+
assert.NoError(t, featuregate.GlobalRegistry().Set("pkg.translator.jaeger.DontEmitV0HttpConventions", false))
369+
assert.NoError(t, featuregate.GlobalRegistry().Set("pkg.translator.jaeger.EmitV1HttpConventions", false))
370+
})
371+
372+
key := "http.status_code"
373+
val := "500"
374+
tags := []*jaeger.Tag{{Key: key, VType: jaeger.TagType_STRING, VStr: &val}}
375+
dest := pcommon.NewMap()
376+
jThriftTagsToInternalAttributes(tags, dest)
377+
378+
_, hasV0 := dest.Get("http.status_code")
379+
_, hasV1 := dest.Get("http.response.status_code")
380+
assert.Equal(t, tc.expectsV0, hasV0)
381+
assert.Equal(t, tc.expectsV1, hasV1)
382+
})
383+
}
384+
}

0 commit comments

Comments
 (0)