[tracing] Moving spanOptions to `tracingOptions` in PipelineRequest (#14094)

Creating a `tracingOptions` property on PipelineRequest and using createSpan internally rather than starting spans directly.

Also, allows callers to use createSpanFunction() without a package prefix or namespace (needed for core-http and lower level libraries).
This commit is contained in:
Richard Park 2021-03-04 12:08:33 -08:00 коммит произвёл GitHub
Родитель 227ac59b57
Коммит a6ebd85fde
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 122 добавлений и 39 удалений

Просмотреть файл

@ -164,8 +164,8 @@ export class ServiceClient {
request.abortSignal = options.abortSignal; request.abortSignal = options.abortSignal;
} }
if (options.tracingOptions?.spanOptions) { if (options.tracingOptions) {
request.spanOptions = options.tracingOptions.spanOptions; request.tracingOptions = options.tracingOptions;
} }
} }

Просмотреть файл

@ -6,7 +6,7 @@
import { AbortSignalLike } from '@azure/abort-controller'; import { AbortSignalLike } from '@azure/abort-controller';
import { Debugger } from '@azure/logger'; import { Debugger } from '@azure/logger';
import { SpanOptions } from '@azure/core-tracing'; import { OperationTracingOptions } from '@azure/core-tracing';
import { TokenCredential } from '@azure/core-auth'; import { TokenCredential } from '@azure/core-auth';
// @public // @public
@ -162,9 +162,9 @@ export interface PipelineRequest {
onUploadProgress?: (progress: TransferProgressEvent) => void; onUploadProgress?: (progress: TransferProgressEvent) => void;
proxySettings?: ProxySettings; proxySettings?: ProxySettings;
requestId: string; requestId: string;
spanOptions?: SpanOptions;
streamResponseStatusCodes?: Set<number>; streamResponseStatusCodes?: Set<number>;
timeout: number; timeout: number;
tracingOptions?: OperationTracingOptions;
url: string; url: string;
withCredentials: boolean; withCredentials: boolean;
} }
@ -181,9 +181,9 @@ export interface PipelineRequestOptions {
onUploadProgress?: (progress: TransferProgressEvent) => void; onUploadProgress?: (progress: TransferProgressEvent) => void;
proxySettings?: ProxySettings; proxySettings?: ProxySettings;
requestId?: string; requestId?: string;
spanOptions?: SpanOptions;
streamResponseStatusCodes?: Set<number>; streamResponseStatusCodes?: Set<number>;
timeout?: number; timeout?: number;
tracingOptions?: OperationTracingOptions;
url: string; url: string;
withCredentials?: boolean; withCredentials?: boolean;
} }

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT license. // Licensed under the MIT license.
import { AbortSignalLike } from "@azure/abort-controller"; import { AbortSignalLike } from "@azure/abort-controller";
import { SpanOptions } from "@azure/core-tracing"; import { OperationTracingOptions } from "@azure/core-tracing";
// eslint-disable-next-line @azure/azure-sdk/ts-no-namespaces // eslint-disable-next-line @azure/azure-sdk/ts-no-namespaces
declare global { declare global {
@ -149,9 +149,9 @@ export interface PipelineRequest {
abortSignal?: AbortSignalLike; abortSignal?: AbortSignalLike;
/** /**
* Options used to create a span when tracing is enabled. * Tracing options to use for any created Spans.
*/ */
spanOptions?: SpanOptions; tracingOptions?: OperationTracingOptions;
/** /**
* Callback which fires upon upload progress. * Callback which fires upon upload progress.

Просмотреть файл

@ -13,7 +13,7 @@ import {
import { createHttpHeaders } from "./httpHeaders"; import { createHttpHeaders } from "./httpHeaders";
import { AbortSignalLike } from "@azure/abort-controller"; import { AbortSignalLike } from "@azure/abort-controller";
import { generateUuid } from "./util/uuid"; import { generateUuid } from "./util/uuid";
import { SpanOptions } from "@azure/core-tracing"; import { OperationTracingOptions } from "@azure/core-tracing";
/** /**
* Settings to initialize a request. * Settings to initialize a request.
@ -86,7 +86,7 @@ export interface PipelineRequestOptions {
/** /**
* Options used to create a span when tracing is enabled. * Options used to create a span when tracing is enabled.
*/ */
spanOptions?: SpanOptions; tracingOptions?: OperationTracingOptions;
/** /**
* Callback which fires upon upload progress. * Callback which fires upon upload progress.
@ -110,7 +110,7 @@ class PipelineRequestImpl implements PipelineRequest {
public disableKeepAlive: boolean; public disableKeepAlive: boolean;
public abortSignal?: AbortSignalLike; public abortSignal?: AbortSignalLike;
public requestId: string; public requestId: string;
public spanOptions?: SpanOptions; public tracingOptions?: OperationTracingOptions;
public onUploadProgress?: (progress: TransferProgressEvent) => void; public onUploadProgress?: (progress: TransferProgressEvent) => void;
public onDownloadProgress?: (progress: TransferProgressEvent) => void; public onDownloadProgress?: (progress: TransferProgressEvent) => void;
@ -126,7 +126,7 @@ class PipelineRequestImpl implements PipelineRequest {
this.streamResponseStatusCodes = options.streamResponseStatusCodes; this.streamResponseStatusCodes = options.streamResponseStatusCodes;
this.withCredentials = options.withCredentials ?? false; this.withCredentials = options.withCredentials ?? false;
this.abortSignal = options.abortSignal; this.abortSignal = options.abortSignal;
this.spanOptions = options.spanOptions; this.tracingOptions = options.tracingOptions;
this.onUploadProgress = options.onUploadProgress; this.onUploadProgress = options.onUploadProgress;
this.onDownloadProgress = options.onDownloadProgress; this.onDownloadProgress = options.onDownloadProgress;
this.requestId = options.requestId || generateUuid(); this.requestId = options.requestId || generateUuid();

Просмотреть файл

@ -48,9 +48,7 @@ export function bearerTokenAuthenticationPolicy(
async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> { async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
const token = await getToken({ const token = await getToken({
abortSignal: request.abortSignal, abortSignal: request.abortSignal,
tracingOptions: { tracingOptions: request.tracingOptions
spanOptions: request.spanOptions
}
}); });
request.headers.set("Authorization", `Bearer ${token}`); request.headers.set("Authorization", `Bearer ${token}`);
return next(request); return next(request);

Просмотреть файл

@ -1,13 +1,22 @@
// Copyright (c) Microsoft Corporation. // Copyright (c) Microsoft Corporation.
// Licensed under the MIT license. // Licensed under the MIT license.
import { getTracer, getTraceParentHeader } from "@azure/core-tracing"; import {
import { SpanOptions, SpanKind } from "@opentelemetry/api"; getTraceParentHeader,
OperationTracingOptions,
createSpanFunction
} from "@azure/core-tracing";
import { SpanKind } from "@opentelemetry/api";
import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces"; import { PipelineResponse, PipelineRequest, SendRequest } from "../interfaces";
import { PipelinePolicy } from "../pipeline"; import { PipelinePolicy } from "../pipeline";
import { URL } from "../util/url"; import { URL } from "../util/url";
import { getUserAgentValue } from "../util/userAgent"; import { getUserAgentValue } from "../util/userAgent";
const createSpan = createSpanFunction({
packagePrefix: "",
namespace: ""
});
/** /**
* The programmatic identifier of the tracingPolicy. * The programmatic identifier of the tracingPolicy.
*/ */
@ -37,19 +46,24 @@ export function tracingPolicy(options: TracingPolicyOptions = {}): PipelinePolic
return { return {
name: tracingPolicyName, name: tracingPolicyName,
async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> { async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
if (!request.spanOptions || !request.spanOptions.parent) { if (!request.tracingOptions?.spanOptions || !request.tracingOptions.spanOptions?.parent) {
return next(request); return next(request);
} }
// create a new span // create a new span
const tracer = getTracer(); const tracingOptions: OperationTracingOptions = {
const spanOptions: SpanOptions = { ...request.tracingOptions,
...request.spanOptions, spanOptions: {
kind: SpanKind.CLIENT ...request.tracingOptions.spanOptions,
kind: SpanKind.CLIENT
}
}; };
const url = new URL(request.url); const url = new URL(request.url);
const path = url.pathname || "/"; const path = url.pathname || "/";
const span = tracer.startSpan(path, spanOptions);
const { span } = createSpan(path, { tracingOptions });
span.setAttributes({ span.setAttributes({
"http.method": request.method, "http.method": request.method,
"http.url": request.url, "http.url": request.url,

Просмотреть файл

@ -41,7 +41,7 @@ describe("BearerTokenAuthenticationPolicy", function() {
assert( assert(
fakeGetToken.calledWith(tokenScopes, { fakeGetToken.calledWith(tokenScopes, {
abortSignal: undefined, abortSignal: undefined,
tracingOptions: { spanOptions: undefined } tracingOptions: undefined
}), }),
"fakeGetToken called incorrectly." "fakeGetToken called incorrectly."
); );

Просмотреть файл

@ -118,8 +118,10 @@ describe("tracingPolicy", function() {
const request = createPipelineRequest({ const request = createPipelineRequest({
url: "https://bing.com", url: "https://bing.com",
spanOptions: { tracingOptions: {
parent: ROOT_SPAN.context() spanOptions: {
parent: ROOT_SPAN.context()
}
} }
}); });
const response: PipelineResponse = { const response: PipelineResponse = {
@ -155,8 +157,10 @@ describe("tracingPolicy", function() {
const request = createPipelineRequest({ const request = createPipelineRequest({
url: "https://bing.com", url: "https://bing.com",
spanOptions: { tracingOptions: {
parent: ROOT_SPAN.context() spanOptions: {
parent: ROOT_SPAN.context()
}
} }
}); });
const response: PipelineResponse = { const response: PipelineResponse = {
@ -192,8 +196,10 @@ describe("tracingPolicy", function() {
const request = createPipelineRequest({ const request = createPipelineRequest({
url: "https://bing.com", url: "https://bing.com",
spanOptions: { tracingOptions: {
parent: ROOT_SPAN.context() spanOptions: {
parent: ROOT_SPAN.context()
}
} }
}); });
const response: PipelineResponse = { const response: PipelineResponse = {
@ -229,8 +235,10 @@ describe("tracingPolicy", function() {
const request = createPipelineRequest({ const request = createPipelineRequest({
url: "https://bing.com", url: "https://bing.com",
spanOptions: { tracingOptions: {
parent: ROOT_SPAN.context() spanOptions: {
parent: ROOT_SPAN.context()
}
} }
}); });
const response: PipelineResponse = { const response: PipelineResponse = {
@ -267,8 +275,10 @@ describe("tracingPolicy", function() {
const request = createPipelineRequest({ const request = createPipelineRequest({
url: "https://bing.com", url: "https://bing.com",
spanOptions: { tracingOptions: {
parent: ROOT_SPAN.context() spanOptions: {
parent: ROOT_SPAN.context()
}
} }
}); });
const response: PipelineResponse = { const response: PipelineResponse = {

Просмотреть файл

@ -13,11 +13,15 @@ import { OperationTracingOptions } from "./interfaces";
*/ */
export interface CreateSpanFunctionArgs { export interface CreateSpanFunctionArgs {
/** /**
* Package name prefix * Package name prefix.
*
* NOTE: if this is empty no prefix will be applied to created Span names.
*/ */
packagePrefix: string; packagePrefix: string;
/** /**
* Service namespace * Service namespace
*
* NOTE: if this is empty no `az.namespace` attribute will be added to created Spans.
*/ */
namespace: string; namespace: string;
} }
@ -52,12 +56,16 @@ export function createSpanFunction(args: CreateSpanFunctionArgs) {
...tracingOptions.spanOptions ...tracingOptions.spanOptions
}; };
const span = tracer.startSpan(`${args.packagePrefix}.${operationName}`, spanOptions); const spanName = args.packagePrefix ? `${args.packagePrefix}.${operationName}` : operationName;
const span = tracer.startSpan(spanName, spanOptions);
span.setAttribute("az.namespace", args.namespace); if (args.namespace) {
span.setAttribute("az.namespace", args.namespace);
}
let newSpanOptions = tracingOptions.spanOptions || {}; let newSpanOptions = tracingOptions.spanOptions || {};
if (span.isRecording()) {
if (span.isRecording() && args.namespace) {
newSpanOptions = { newSpanOptions = {
...tracingOptions.spanOptions, ...tracingOptions.spanOptions,
parent: span.context(), parent: span.context(),

Просмотреть файл

@ -108,6 +108,59 @@ describe("createSpan", () => {
assert.deepEqual(updatedOptions, expected); assert.deepEqual(updatedOptions, expected);
}); });
it("namespace and packagePrefix can be empty (and thus ignored)", () => {
const tracer = new TestTracer();
const testSpan = new TestSpan(
tracer,
"testing",
{ traceId: "", spanId: "", traceFlags: TraceFlags.NONE },
SpanKind.INTERNAL // this isn't used by anything in our test.
);
const setAttributeSpy = sinon.spy(testSpan, "setAttribute");
const startSpanStub = sinon.stub(tracer, "startSpan");
startSpanStub.returns(testSpan);
setTracer(tracer);
const cf = createSpanFunction({
namespace: "",
packagePrefix: ""
});
const { span, updatedOptions } = cf("myVerbatimOperationName", {
tracingOptions: {
spanOptions: {
attributes: {
testAttribute: "testValue"
}
}
}
});
assert.ok(span);
assert.ok(startSpanStub.called);
const [name] = startSpanStub.firstCall.args;
assert.equal(
name,
"myVerbatimOperationName",
"operation name should be exactly as passed in (no prefix)"
);
assert.ok(!setAttributeSpy.called, "When the namespace is undefined it should not be set");
assert.deepEqual(updatedOptions, {
tracingOptions: {
spanOptions: {
attributes: {
testAttribute: "testValue"
}
}
}
});
});
afterEach(() => { afterEach(() => {
sinon.restore(); sinon.restore();
}); });

Просмотреть файл

@ -146,7 +146,7 @@ export class TableBatchImpl implements TableBatch {
method: "POST", method: "POST",
body, body,
headers: createHttpHeaders(headers), headers: createHttpHeaders(headers),
spanOptions: updatedOptions.tracingOptions?.spanOptions tracingOptions: updatedOptions.tracingOptions
}); });
if (this.credential) { if (this.credential) {