diff --git a/.chloggen/span-rpocessor-keep-original-name.yaml b/.chloggen/span-rpocessor-keep-original-name.yaml new file mode 100644 index 000000000000..d3e229b933a5 --- /dev/null +++ b/.chloggen/span-rpocessor-keep-original-name.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: processor/spanprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Add a new configuration option to keep the original span name when extracting attributes from the span name." + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36120] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/processor/spanprocessor/README.md b/processor/spanprocessor/README.md index 454b01fa0a07..10eae85904d3 100644 --- a/processor/spanprocessor/README.md +++ b/processor/spanprocessor/README.md @@ -81,6 +81,9 @@ span name that is the output after processing the previous rule. - `break_after_match` (default = false): specifies if processing of rules should stop after the first match. If it is false rule processing will continue to be performed over the modified span name. +- `keep_original_name` (default = false): specifies if the original span name should be kept after +processing the rules. If it is true, the original span name will be kept, +otherwise it will be replaced with the placeholders of the captured attributes. ```yaml span/to_attributes: @@ -92,7 +95,7 @@ span/to_attributes: - regexp-rule3 ... break_after_match: - + keep_original_name: ``` Example: @@ -106,6 +109,15 @@ span/to_attributes: to_attributes: rules: - ^\/api\/v1\/document\/(?P.*)\/update$ + +# This example will add the same new "documentId"="12345678" attribute, +# but now resulting in an unchanged span name (/api/v1/document/12345678/update). +span/to_attributes_keep_original_name: + name: + to_attributes: + keep_original_name: true + rules: + - ^\/api\/v1\/document\/(?P.*)\/update$ ``` ### Set status for span diff --git a/processor/spanprocessor/config.go b/processor/spanprocessor/config.go index 1f9c536a33de..b96d4dbbbc19 100644 --- a/processor/spanprocessor/config.go +++ b/processor/spanprocessor/config.go @@ -68,6 +68,11 @@ type ToAttributes struct { // match. If it is false rule processing will continue to be performed over the // modified span name. BreakAfterMatch bool `mapstructure:"break_after_match"` + + // KeepOriginalName specifies if the original span name should be kept after + // processing the rules. If it is true the original span name will be kept, + // otherwise it will be replaced with the placeholders of the captured attributes. + KeepOriginalName bool `mapstructure:"keep_original_name"` } type Status struct { diff --git a/processor/spanprocessor/config_test.go b/processor/spanprocessor/config_test.go index f0488fb1c0df..b4f3aa0e1d24 100644 --- a/processor/spanprocessor/config_test.go +++ b/processor/spanprocessor/config_test.go @@ -46,7 +46,19 @@ func TestLoadingConfig(t *testing.T) { expected: &Config{ Rename: Name{ ToAttributes: &ToAttributes{ - Rules: []string{`^\/api\/v1\/document\/(?P.*)\/update$`}, + Rules: []string{`^\/api\/v1\/document\/(?P.*)\/update$`}, + KeepOriginalName: false, + }, + }, + }, + }, + { + id: component.MustNewIDWithName("span", "to_attributes_keep_original_name"), + expected: &Config{ + Rename: Name{ + ToAttributes: &ToAttributes{ + Rules: []string{`^\/api\/v1\/document\/(?P.*)\/update$`}, + KeepOriginalName: true, }, }, }, diff --git a/processor/spanprocessor/span.go b/processor/spanprocessor/span.go index ec629879703d..0f2992dc4abe 100644 --- a/processor/spanprocessor/span.go +++ b/processor/spanprocessor/span.go @@ -213,7 +213,9 @@ func (sp *spanProcessor) processToAttributes(span ptrace.Span) { } // Set new span name. - span.SetName(sb.String()) + if !sp.config.Rename.ToAttributes.KeepOriginalName { + span.SetName(sb.String()) + } if sp.config.Rename.ToAttributes.BreakAfterMatch { // Stop processing, break after first match is requested. diff --git a/processor/spanprocessor/span_test.go b/processor/spanprocessor/span_test.go index c759feb344ad..c25a059d1f6d 100644 --- a/processor/spanprocessor/span_test.go +++ b/processor/spanprocessor/span_test.go @@ -414,8 +414,9 @@ func TestSpanProcessor_NilName(t *testing.T) { // TestSpanProcessor_ToAttributes func TestSpanProcessor_ToAttributes(t *testing.T) { testCases := []struct { - rules []string - breakAfterMatch bool + rules []string + breakAfterMatch bool + keepOriginalName bool testCase }{ { @@ -460,9 +461,24 @@ func TestSpanProcessor_ToAttributes(t *testing.T) { { rules: []string{ - `^\/api\/v1\/document\/(?P.*)\/update\/4$`, - `^\/api\/(?P.*)\/document\/(?P.*)\/update\/4$`, + `^\/api\/.*\/document\/(?P.*)\/update\/3$`, + `^\/api\/(?P.*)\/document\/.*\/update\/3$`}, + testCase: testCase{ + inputName: "/api/v1/document/321083210/update/3", + outputName: "/api/v1/document/321083210/update/3", + outputAttributes: map[string]any{ + "documentId": "321083210", + "version": "v1", + }, }, + breakAfterMatch: false, + keepOriginalName: true, + }, + + { + rules: []string{ + `^\/api\/v1\/document\/(?P.*)\/update\/4$`, + `^\/api\/(?P.*)\/document\/(?P.*)\/update\/4$`}, testCase: testCase{ inputName: "/api/v1/document/321083210/update/4", outputName: "/api/v1/document/{documentId}/update/4", @@ -491,6 +507,7 @@ func TestSpanProcessor_ToAttributes(t *testing.T) { for _, tc := range testCases { oCfg.Rename.ToAttributes.Rules = tc.rules oCfg.Rename.ToAttributes.BreakAfterMatch = tc.breakAfterMatch + oCfg.Rename.ToAttributes.KeepOriginalName = tc.keepOriginalName tp, err := factory.CreateTraces(context.Background(), processortest.NewNopSettings(), oCfg, consumertest.NewNop()) require.NoError(t, err) require.NotNil(t, tp) diff --git a/processor/spanprocessor/testdata/config.yaml b/processor/spanprocessor/testdata/config.yaml index 26c1b3fd2f68..c86e4c7b5d48 100644 --- a/processor/spanprocessor/testdata/config.yaml +++ b/processor/spanprocessor/testdata/config.yaml @@ -61,6 +61,15 @@ span/to_attributes: rules: - ^\/api\/v1\/document\/(?P.*)\/update$ +# This example will add the same new "documentId"="12345678" attribute, +# but now resulting in an unchanged span name (/api/v1/document/12345678/update). +span/to_attributes_keep_original_name: + name: + to_attributes: + keep_original_name: true + rules: + - ^\/api\/v1\/document\/(?P.*)\/update$ + # The following demonstrates renaming the span name to `{operation_website}` # and adding the attribute {Key: operation_website, Value: } # when the span has the following properties