Open Telemetry Component for Midway.js
ESM build only, requires @midwayjs >= 3.12
and set "type": "module"
in packages.json
npm i @mwcp/otel
Update project src/configuration.ts
import { Configuration } from '@midwayjs/decorator'
import * as koa from '@midwayjs/koa'
import * as otel from '@mwcp/otel'
@Configuration({
imports: [
koa,
otel,
],
importConfigs: [join(__dirname, 'config')],
})
export class ContainerConfiguration implements ILifeCycle {
}
To try out the OTLPTraceExporter quickly, you can run Jaeger in a docker container:
docker run -d --name jaeger \
-e COLLECTOR_OTLP_ENABLED=true \
-p 4317:4317 \
-p 4318:4318 \
-p 5778:5778 \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 16686:16686 \
jaegertracing/all-in-one:latest
Start project:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
npm start
Jaeger Web UI address:
import { Trace } from '@mwcp/otel'
@Controller('/')
export class FooController {
@Inject() readonly svc: FooService
/* span name will be `{class name}/{method name}` => "FooController/hello" */
@Trace()
async hello(): Promise<string> {
return 'hello'
}
/* span name will be "hello" */
@Trace('hello')
async world(): Promise<string> {
return 'world'
}
@Trace({
spanName: 'hello'
})
async world2(): Promise<string> {
return 'world'
}
}
Pass scope
to avoid the confusion of call chain relationship when async methods are called concurrently
import { Trace } from '@mwcp/otel'
@Controller('/')
export class FooController {
@Trace()
async hello(): Promise<string> {
await Promise.all([
this._simple1(),
this._simple2(),
])
return 'OK'
}
@Trace({ scope: 'hello1' })
async _hello1(): Promise<string> {
return 'world'
}
@Trace({ scope: 'hello2' })
async _hello2(): Promise<string> {
return 'world'
}
@Trace({ scope: 'hello1' })
async _hello1a(): Promise<string> {
return 'world'
}
@Trace({ scope: 'hello2' })
async _hello2a(): Promise<string> {
return 'world'
}
}
// src/configuration.ts
import { TraceInit } from '@mwcp/otel'
export class AutoConfiguration implements ILifeCycle {
@TraceInit({ namespace: 'Foo' })
async onReady(container: IMidwayContainer): Promise<void> {
// some code
}
}
@Controller('/')
export class FooController {
@Inject() readonly svc: FooService
hello(): string {
// spanName should be 'foo-124-abc'
const msg = this.svc.concat(123, 'abc')
return msg
}
}
@Provide()
export class FooService {
@Trace<FooService['concat']>({
spanName: ([v1, v2]) => `foo-${v1 + 1}-${v2}`,
})
concat(v1: number, v2: string): string {
return `${v1.toString()}-${v2}`
}
@Trace<FooService['concat2']>({
spanName: (args) => `foo-${args[0] + 1}-${args[1]}`,
})
concat2(v1: number, v2: string): string {
return `${v1.toString()}-${v2}`
}
}