-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
tracingHelpers.ts
95 lines (82 loc) · 2.61 KB
/
tracingHelpers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { getTracer } from "@azure/core-tracing";
import { Span, SpanOptions, SpanKind, CanonicalCode } from "@opentelemetry/types";
import { RestError } from "@azure/core-http";
/**
* @internal
* @ignore
*/
export interface Spannable {
spanOptions?: SpanOptions;
}
/**
* @internal
* @ignore
*/
export class Spanner<TClient> {
constructor(private baseOperationName: string, private componentName: string) {}
/**
* Traces an operation and properly handles reporting start, end and errors for a given span
*
* @param operationName Name of a method in the TClient type
* @param options An options class, typically derived from @azure/core-http/RequestOptionsBase
* @param fn The function to call with an options class that properly propagates the span context
* @param translateToCanonicalCodeFn An optional function to translate thrown errors into a CanonicalCode for the span
*/
async trace<OptionsT extends Spannable, ReturnT>(
operationName: keyof TClient,
options: OptionsT,
fn: (options: OptionsT, span: Span) => Promise<ReturnT>,
translateToCanonicalCodeFn: (err: Error) => CanonicalCode = Spanner.getCanonicalCode
): Promise<ReturnT> {
const { newOptions, span } = this.createSpan<OptionsT>(options, operationName);
try {
return await fn(newOptions, span);
} catch (err) {
span.setStatus({
code: translateToCanonicalCodeFn(err),
message: err.message
});
throw err;
} finally {
span.end();
}
}
private createSpan<T extends Spannable>(options: T, operationName: keyof TClient) {
const span = getTracer().startSpan(`${this.baseOperationName}.${operationName}`, {
...options.spanOptions,
kind: SpanKind.INTERNAL
});
let newOptions = options;
if (span.isRecording()) {
newOptions = Spanner.addParentToOptions<T>(options, span);
}
return { span, newOptions };
}
static getCanonicalCode(err: Error) {
if (Spanner.isRestError(err)) {
switch (err.statusCode) {
case 401:
return CanonicalCode.PERMISSION_DENIED;
case 404:
return CanonicalCode.NOT_FOUND;
case 412:
return CanonicalCode.FAILED_PRECONDITION;
}
}
return CanonicalCode.UNKNOWN;
}
static isRestError(err: Error): err is RestError {
return err instanceof RestError;
}
static addParentToOptions<T extends Spannable>(options: T, span: Span) {
return {
...options,
spanOptions: {
...options.spanOptions,
parent: span
}
};
}
}