forked from Azure/azure-sdk-for-js
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[core-http] Add createSpan helper (Azure#12525)
Add helper to create a span using the global tracer. Many of our Track2 SDKs implement their own createSpan, this helper should remove the need to re-implement it for every SDK
- Loading branch information
Showing
9 changed files
with
341 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { Span, SpanOptions, SpanKind } from "@opentelemetry/api"; | ||
import { getTracer } from "@azure/core-tracing"; | ||
import { OperationOptions, SpanConfig } from "./interfaces"; | ||
|
||
type OperationTracingOptions = OperationOptions["tracingOptions"]; | ||
|
||
/** | ||
* Creates a function called createSpan to create spans using the global tracer. | ||
* @ignore | ||
* @param spanConfig The name of the operation being performed. | ||
* @param tracingOptions The options for the underlying http request. | ||
*/ | ||
export function createSpanFunction({ packagePrefix, namespace }: SpanConfig) { | ||
return function<T extends OperationOptions>( | ||
operationName: string, | ||
operationOptions: T | ||
): { span: Span; updatedOptions: T } { | ||
const tracer = getTracer(); | ||
const tracingOptions = operationOptions.tracingOptions || {}; | ||
const spanOptions: SpanOptions = { | ||
...tracingOptions.spanOptions, | ||
kind: SpanKind.INTERNAL | ||
}; | ||
|
||
const span = tracer.startSpan(`${packagePrefix}.${operationName}`, spanOptions); | ||
|
||
span.setAttribute("az.namespace", namespace); | ||
|
||
let newSpanOptions = tracingOptions.spanOptions || {}; | ||
if (span.isRecording()) { | ||
newSpanOptions = { | ||
...tracingOptions.spanOptions, | ||
parent: span.context(), | ||
attributes: { | ||
...spanOptions.attributes, | ||
"az.namespace": namespace | ||
} | ||
}; | ||
} | ||
|
||
const newTracingOptions: OperationTracingOptions = { | ||
...tracingOptions, | ||
spanOptions: newSpanOptions | ||
}; | ||
|
||
const newOperationOptions: T = { | ||
...operationOptions, | ||
tracingOptions: newTracingOptions | ||
}; | ||
|
||
return { | ||
span, | ||
updatedOptions: newOperationOptions | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { assert } from "chai"; | ||
import { SpanKind, TraceFlags } from "@opentelemetry/api"; | ||
import { setTracer, TestSpan, TestTracer } from "@azure/core-tracing"; | ||
import sinon from "sinon"; | ||
import { createSpanFunction } from "../src/createSpan"; | ||
import { OperationOptions } from "../src/interfaces"; | ||
|
||
const createSpan = createSpanFunction({ namespace: "Microsoft.Test", packagePrefix: "Azure.Test" }); | ||
|
||
describe("createSpan", () => { | ||
it("returns a created span with the right metadata", () => { | ||
const tracer = new TestTracer(); | ||
const testSpan = new TestSpan( | ||
tracer, | ||
"testing", | ||
{ traceId: "", spanId: "", traceFlags: TraceFlags.NONE }, | ||
SpanKind.INTERNAL | ||
); | ||
const setAttributeSpy = sinon.spy(testSpan, "setAttribute"); | ||
const startSpanStub = sinon.stub(tracer, "startSpan"); | ||
startSpanStub.returns(testSpan); | ||
setTracer(tracer); | ||
const { span } = createSpan("testMethod", {}); | ||
assert.strictEqual(span, testSpan, "Should return mocked span"); | ||
assert.isTrue(startSpanStub.calledOnce); | ||
const [name, options] = startSpanStub.firstCall.args; | ||
assert.strictEqual(name, "Azure.Test.testMethod"); | ||
assert.deepEqual(options, { kind: SpanKind.INTERNAL }); | ||
assert.isTrue(setAttributeSpy.calledOnceWithExactly("az.namespace", "Microsoft.Test")); | ||
}); | ||
|
||
it("returns updated SpanOptions", () => { | ||
const options: OperationOptions = {}; | ||
const { span, updatedOptions } = createSpan("testMethod", options); | ||
assert.isEmpty(options, "original options should not be modified"); | ||
assert.notStrictEqual(updatedOptions, options, "should return new object"); | ||
const expected: OperationOptions = { | ||
tracingOptions: { | ||
spanOptions: { | ||
parent: span.context(), | ||
attributes: { | ||
"az.namespace": "Microsoft.Test" | ||
} | ||
} | ||
} | ||
}; | ||
assert.deepEqual(updatedOptions, expected); | ||
}); | ||
|
||
it("preserves existing attributes", () => { | ||
const options: OperationOptions = { | ||
tracingOptions: { | ||
spanOptions: { | ||
attributes: { | ||
foo: "bar" | ||
} | ||
} | ||
} | ||
}; | ||
const { span, updatedOptions } = createSpan("testMethod", options); | ||
assert.notStrictEqual(updatedOptions, options, "should return new object"); | ||
const expected: OperationOptions = { | ||
tracingOptions: { | ||
spanOptions: { | ||
parent: span.context(), | ||
attributes: { | ||
"az.namespace": "Microsoft.Test", | ||
foo: "bar" | ||
} | ||
} | ||
} | ||
}; | ||
assert.deepEqual(updatedOptions, expected); | ||
}); | ||
|
||
afterEach(() => { | ||
sinon.restore(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
import { Span, SpanOptions, SpanKind } from "@opentelemetry/api"; | ||
import { getTracer } from "@azure/core-tracing"; | ||
import { OperationOptions } from "./coreHttp"; | ||
|
||
type OperationTracingOptions = OperationOptions["tracingOptions"]; | ||
|
||
/** | ||
* Configuration for creating a new Tracing Span | ||
*/ | ||
export interface SpanConfig { | ||
/** | ||
* Package name prefix | ||
*/ | ||
packagePrefix: string; | ||
/** | ||
* Service namespace | ||
*/ | ||
namespace: string; | ||
} | ||
|
||
/** | ||
* Creates a function called createSpan to create spans using the global tracer. | ||
* @ignore | ||
* @param spanConfig The name of the operation being performed. | ||
* @param tracingOptions The options for the underlying http request. | ||
*/ | ||
export function createSpanFunction({ packagePrefix, namespace }: SpanConfig) { | ||
return function<T extends OperationOptions>( | ||
operationName: string, | ||
operationOptions: T | ||
): { span: Span; updatedOptions: T } { | ||
const tracer = getTracer(); | ||
const tracingOptions = operationOptions.tracingOptions || {}; | ||
const spanOptions: SpanOptions = { | ||
...tracingOptions.spanOptions, | ||
kind: SpanKind.INTERNAL | ||
}; | ||
|
||
const span = tracer.startSpan(`${packagePrefix}.${operationName}`, spanOptions); | ||
|
||
span.setAttribute("az.namespace", namespace); | ||
|
||
let newSpanOptions = tracingOptions.spanOptions || {}; | ||
if (span.isRecording()) { | ||
newSpanOptions = { | ||
...tracingOptions.spanOptions, | ||
parent: span.context(), | ||
attributes: { | ||
...spanOptions.attributes, | ||
"az.namespace": namespace | ||
} | ||
}; | ||
} | ||
|
||
const newTracingOptions: OperationTracingOptions = { | ||
...tracingOptions, | ||
spanOptions: newSpanOptions | ||
}; | ||
|
||
const newOperationOptions: T = { | ||
...operationOptions, | ||
tracingOptions: newTracingOptions | ||
}; | ||
|
||
return { | ||
span, | ||
updatedOptions: newOperationOptions | ||
}; | ||
}; | ||
} |
Oops, something went wrong.