Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit a03e7b2

Browse files
authored
feat: add rootSpanNameOverride option (#826)
This PR introduces rootSpanNameOverride, which allows us to override root span names with either a string value or a mapping function.
1 parent fe50b44 commit a03e7b2

12 files changed

Lines changed: 132 additions & 3 deletions

src/config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ export interface Config {
6262
*/
6363
enhancedDatabaseReporting?: boolean;
6464

65+
/**
66+
* A value that can be used to override names of root spans. If specified as
67+
* a string, the string will be used to replace all such span names; if
68+
* specified as a function, the function will be invoked with the request path
69+
* as an argument, and its return value will be used as the span name.
70+
*/
71+
rootSpanNameOverride?: string|((name: string) => string);
72+
6573
/**
6674
* The maximum number of characters reported on a label value. This value
6775
* cannot exceed 16383, the maximum value accepted by the service.
@@ -193,6 +201,7 @@ export const defaultConfig = {
193201
logLevel: 1,
194202
enabled: true,
195203
enhancedDatabaseReporting: false,
204+
rootSpanNameOverride: (name: string) => name,
196205
maximumLabelValueSize: 512,
197206
plugins: {
198207
// enable all by default

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/** Constant values. */
2020
// tslint:disable-next-line:variable-name
2121
export const Constants = {
22-
/** The metadata key under which trace context */
22+
/** The metadata key under which trace context is stored as a binary value. */
2323
TRACE_CONTEXT_GRPC_METADATA_NAME: 'grpc-trace-bin',
2424

2525
/** Header that carries trace context across Google infrastructure. */

src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ function initConfig(projectConfig: Forceable<Config>):
7676
Constants.TRACE_SERVICE_LABEL_VALUE_LIMIT) {
7777
config.maximumLabelValueSize = Constants.TRACE_SERVICE_LABEL_VALUE_LIMIT;
7878
}
79+
// Make rootSpanNameOverride a function if not already.
80+
if (typeof config.rootSpanNameOverride === 'string') {
81+
const spanName = config.rootSpanNameOverride;
82+
config.rootSpanNameOverride = () => spanName;
83+
} else if (typeof config.rootSpanNameOverride !== 'function') {
84+
config.rootSpanNameOverride = (name: string) => name;
85+
}
7986

8087
// If the CLS mechanism is set to auto-determined, decide now what it should
8188
// be.

src/plugin-types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
// tslint:disable:no-any
1919

2020
import {Constants, SpanType} from './constants';
21+
import {StackdriverTracerConfig} from './trace-api';
2122
import {TraceLabels} from './trace-labels';
2223
import {TraceContext} from './util';
2324

@@ -116,6 +117,12 @@ export interface Tracer {
116117
*/
117118
enhancedDatabaseReportingEnabled(): boolean;
118119

120+
/**
121+
* Gets the current configuration, or throws if it can't be retrieved
122+
* because the Trace Agent was not disabled.
123+
*/
124+
getConfig(): StackdriverTracerConfig;
125+
119126
/**
120127
* Runs the given function in a root span corresponding to an incoming
121128
* request, passing it an object that exposes an interface for adding

src/trace-api.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface StackdriverTracerConfig extends
3535
TracingPolicy.TracePolicyConfig {
3636
enhancedDatabaseReporting: boolean;
3737
ignoreContextHeader: boolean;
38+
rootSpanNameOverride: (path: string) => string;
3839
}
3940

4041
interface IncomingTraceContext {
@@ -126,6 +127,13 @@ export class StackdriverTracer implements Tracer {
126127
return !!this.config && this.config.enhancedDatabaseReporting;
127128
}
128129

130+
getConfig(): StackdriverTracerConfig {
131+
if (!this.config) {
132+
throw new Error('Configuration is not available.');
133+
}
134+
return this.config;
135+
}
136+
129137
runInRootSpan<T>(options: RootSpanOptions, fn: (span: RootSpan) => T): T {
130138
if (!this.isActive()) {
131139
return fn(UNTRACED_ROOT_SPAN);
@@ -166,9 +174,10 @@ export class StackdriverTracer implements Tracer {
166174
const traceId =
167175
incomingTraceContext.traceId || (uuid.v4().split('-').join(''));
168176
const parentId = incomingTraceContext.spanId || '0';
177+
const name = this.config!.rootSpanNameOverride(options.name);
169178
rootContext = new RootSpanData(
170179
{projectId: '', traceId, spans: []}, /* Trace object */
171-
options.name, /* Span name */
180+
name, /* Span name */
172181
parentId, /* Parent's span ID */
173182
options.skipFrames || 0);
174183
}

test/plugins/common.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ shimmer.wrap(trace, 'start', function(original) {
7373
testTraceAgent.enable({
7474
enhancedDatabaseReporting: false,
7575
ignoreContextHeader: false,
76+
rootSpanNameOverride: (name: string) => name,
7677
samplingRate: 0
7778
}, logger());
7879
testTraceAgent.policy = new TracingPolicy.TraceAllPolicy();
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import * as util from 'util';
2222
import {TraceCLSConfig, TraceCLSMechanism} from '../src/cls';
2323

2424
import * as testTraceModule from './trace';
25+
import { NormalizedConfig } from '../src/tracing';
26+
import { StackdriverTracer } from '../src/trace-api';
2527

2628
describe('Behavior set by config for context propagation mechanism', () => {
2729
const useAH = semver.satisfies(process.version, '>=8');
@@ -90,3 +92,64 @@ describe('Behavior set by config for context propagation mechanism', () => {
9092
});
9193
}
9294
});
95+
96+
describe('Behavior set by config for overriding root span name', () => {
97+
let capturedConfig: NormalizedConfig|null;
98+
99+
class CaptureConfigTestTracing extends testTraceModule.TestTracing {
100+
constructor(config: NormalizedConfig, traceAgent: StackdriverTracer) {
101+
super(config, traceAgent);
102+
// Capture the config object passed into this constructor.
103+
capturedConfig = config;
104+
}
105+
}
106+
107+
beforeEach(() => {
108+
capturedConfig = null;
109+
});
110+
111+
before(() => {
112+
testTraceModule.setTracingForTest(CaptureConfigTestTracing);
113+
});
114+
115+
after(() => {
116+
testTraceModule.setTracingForTest(testTraceModule.TestTracing);
117+
});
118+
119+
it('should convert a string to a function', () => {
120+
testTraceModule.start({
121+
rootSpanNameOverride: 'hello'
122+
});
123+
assert.ok(capturedConfig!);
124+
// Avoid using the ! operator multiple times.
125+
const config = capturedConfig!;
126+
// If !config.enabled, then TSC does not permit access to other fields on
127+
// config. So use this structure instead of assert.ok(config.enabled).
128+
if (config.enabled) {
129+
assert.strictEqual(typeof config.rootSpanNameOverride, 'function');
130+
assert.strictEqual(config.rootSpanNameOverride(''), 'hello');
131+
} else {
132+
assert.fail('Configuration was not enabled.');
133+
}
134+
});
135+
136+
it('should convert a non-string, non-function to the identity fn', () => {
137+
testTraceModule.start({
138+
// We should make sure passing in unsupported values at least doesn't
139+
// result in a crash.
140+
// tslint:disable-next-line:no-any
141+
rootSpanNameOverride: 2 as any
142+
});
143+
assert.ok(capturedConfig!);
144+
// Avoid using the ! operator multiple times.
145+
const config = capturedConfig!;
146+
// If !config.enabled, then TSC does not permit access to other fields on
147+
// config. So use this structure instead of assert.ok(config.enabled).
148+
if (config.enabled) {
149+
assert.strictEqual(typeof config.rootSpanNameOverride, 'function');
150+
assert.strictEqual(config.rootSpanNameOverride('a'), 'a');
151+
} else {
152+
assert.fail('Configuration was not enabled.');
153+
}
154+
});
155+
});

test/test-plugin-loader.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe('Trace Plugin Loader', () => {
4747
ignoreUrls: [],
4848
enhancedDatabaseReporting: false,
4949
ignoreContextHeader: false,
50+
rootSpanNameOverride: (name: string) => name,
5051
projectId: '0'
5152
},
5253
config),

test/test-trace-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe('Trace Interface', () => {
3838
{
3939
enhancedDatabaseReporting: false,
4040
ignoreContextHeader: false,
41+
rootSpanNameOverride: (name: string) => name,
4142
samplingRate: 0
4243
},
4344
config),

test/test-trace-web-frameworks.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,27 @@ describe('Web framework tracing', () => {
334334
`span name ${serverSpan.name} includes query parameters`);
335335
});
336336
});
337+
338+
it('uses the span name override option', async () => {
339+
const oldSpanNameOverride =
340+
testTraceModule.get().getConfig().rootSpanNameOverride;
341+
testTraceModule.get().getConfig().rootSpanNameOverride =
342+
(path: string) => `${path}-goodbye`;
343+
try {
344+
await testTraceModule.get().runInRootSpan(
345+
{name: 'outer'}, async (span) => {
346+
assert.ok(testTraceModule.get().isRealSpan(span));
347+
await axios.get(`http://localhost:${port}/hello`);
348+
span!.endSpan();
349+
});
350+
assert.strictEqual(testTraceModule.getSpans().length, 3);
351+
const serverSpan = testTraceModule.getOneSpan(isServerSpan);
352+
assert.strictEqual(serverSpan.name, '/hello-goodbye');
353+
} finally {
354+
testTraceModule.get().getConfig().rootSpanNameOverride =
355+
oldSpanNameOverride;
356+
}
357+
});
337358
});
338359
});
339360
});

0 commit comments

Comments
 (0)