-
Notifications
You must be signed in to change notification settings - Fork 98
Expand file tree
/
Copy pathtracing.interceptor.ts
More file actions
executable file
·105 lines (87 loc) · 3.23 KB
/
Copy pathtracing.interceptor.ts
File metadata and controls
executable file
·105 lines (87 loc) · 3.23 KB
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
96
97
98
99
100
101
102
103
104
105
/**
* @see https://github.com/mikemajesty/nestjs-microservice-boilerplate-api/blob/master/guides/middlewares/tracing.interceptor.md
*/
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import opentelemetry, {
Attributes,
AttributeValue,
Context,
SpanStatus,
SpanStatusCode,
TimeInput,
Tracer
} from '@opentelemetry/api'
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
import axiosBetterStacktrace from 'axios-better-stacktrace'
import { Observable, tap } from 'rxjs'
import { ILoggerAdapter } from '@/infra/logger'
import { AxiosUtils } from '@/utils/axios'
import { generalizePath, TracingType } from '@/utils/request'
import { name, version } from '../../../package.json'
@Injectable()
export class TracingInterceptor implements NestInterceptor {
private tracer: Tracer
constructor(private readonly logger: ILoggerAdapter) {
this.tracer = opentelemetry.trace.getTracer(name, version)
}
intercept(executionContext: ExecutionContext, next: CallHandler): Observable<unknown> {
const request = executionContext.switchToHttp().getRequest()
const res = executionContext.switchToHttp().getResponse()
const requestId = request.headers.traceid ?? request.id
const controller = `${executionContext.getClass().name}.${executionContext.getHandler().name}`
const span = this.tracer.startSpan(generalizePath(request.path))
const createTracingInstance = (): TracingType => {
return {
span,
tracer: this.tracer,
tracerId: requestId,
axios: (options?: Omit<AxiosRequestConfig, 'headers'>): AxiosInstance => {
request.headers.traceid = requestId
const http = axios.create({
...options,
headers: { traceid: request.id, authorization: request.headers.authorization }
})
axiosBetterStacktrace(http)
AxiosUtils.requestRetry({ axios: http, logger: this.logger })
http.interceptors.response.use(
(response) => response,
(error) => {
AxiosUtils.interceptAxiosResponseError(error)
return Promise.reject(error)
}
)
return http
},
setStatus: (status: SpanStatus) => {
span.setStatus(status)
},
logEvent: (key, value) => {
span.addEvent(key, value as Attributes | TimeInput)
},
addAttribute: (key: string, value: AttributeValue) => {
span.setAttribute(key, value)
},
finish: () => {
span.end()
},
createSpan: (name, parent?: Context) => {
return this.tracer.startSpan(name, { root: false }, parent)
}
}
}
request.tracing = createTracingInstance()
request.tracing.addAttribute('http.method', request.method)
request.tracing.addAttribute('http.url', request.path)
request.tracing.addAttribute('context', controller)
if (requestId) {
request.tracing.addAttribute('traceid', requestId)
}
return next.handle().pipe(
tap(() => {
request.tracing.setStatus({ code: SpanStatusCode.OK })
request.tracing.addAttribute('http.status_code', res.statusCode)
request.tracing.finish()
})
)
}
}