Skip to content

Commit

Permalink
Add stateful implementation of metric and trace service
Browse files Browse the repository at this point in the history
  • Loading branch information
wcalderipe committed Oct 29, 2024
1 parent 39efad1 commit 066b789
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 115 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigModule } from '@narval/config-module'
import { LoggerModule, secret } from '@narval/nestjs-shared'
import { LoggerModule, OpenTelemetryModule, secret } from '@narval/nestjs-shared'
import {
Action,
AuthorizationRequest,
Expand Down Expand Up @@ -99,6 +99,7 @@ describe(AuthorizationRequestProcessingConsumer.name, () => {
BullModule.registerQueue({
name: AUTHORIZATION_REQUEST_PROCESSING_QUEUE
}),
OpenTelemetryModule.registerTest(),
HttpModule,
PolicyEngineModule
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigModule } from '@narval/config-module'
import { LoggerModule } from '@narval/nestjs-shared'
import { LoggerModule, OpenTelemetryModule } from '@narval/nestjs-shared'
import {
Action,
AuthorizationRequest,
Expand Down Expand Up @@ -56,12 +56,13 @@ describe(AuthorizationRequestProcessingProducer.name, () => {
load: [load],
isGlobal: true
}),
QueueModule.forRoot(),
PersistenceModule,
PolicyEngineModule,
QueueModule.forRoot(),
OpenTelemetryModule.registerTest(),
BullModule.registerQueue({
name: AUTHORIZATION_REQUEST_PROCESSING_QUEUE
})
}),
PolicyEngineModule
],
providers: [AuthorizationRequestProcessingProducer, AuthorizationRequestRepository]
}).compile()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ConfigModule, ConfigService } from '@narval/config-module'
import { LoggerModule, secret } from '@narval/nestjs-shared'
import { LoggerModule, OpenTelemetryModule, secret } from '@narval/nestjs-shared'
import {
DataStoreConfiguration,
Decision,
Expand Down Expand Up @@ -43,7 +43,8 @@ describe(ClusterService.name, () => {
ConfigModule.forRoot({
load: [load],
isGlobal: true
})
}),
OpenTelemetryModule.registerTest()
],
providers: [ClusterService, PolicyEngineClient, PolicyEngineNodeRepository]
}).compile()
Expand Down
6 changes: 6 additions & 0 deletions packages/nestjs-shared/src/lib/module/open-telemetry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@ export * from './open-telemetry.type'

export * from './service/metric.service'
export * from './service/trace.service'

export * from './service/open-telemetry-metric.service'
export * from './service/open-telemetry-trace.service'

export * from './service/stateful-metric.service'
export * from './service/stateful-trace.service'
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,23 @@ import { OpenTelemetryException } from './open-telemetry.exception'
import { ASYNC_OPTIONS_TYPE, ConfigurableModuleClass, OPTIONS_TYPE } from './open-telemetry.module-definition'
import { OpenTelemetryModuleOption } from './open-telemetry.type'
import { MetricService } from './service/metric.service'
import { OpenTelemetryMetricService } from './service/open-telemetry-metric.service'
import { OpenTelemetryTraceService } from './service/open-telemetry-trace.service'
import { StatefulMetricService } from './service/stateful-metric.service'
import { StatefulTraceService } from './service/stateful-trace.service'
import { TraceService } from './service/trace.service'

@Module({
providers: [TraceService, MetricService],
providers: [
{
provide: TraceService,
useClass: OpenTelemetryTraceService
},
{
provide: MetricService,
useClass: OpenTelemetryMetricService
}
],
exports: [TraceService, MetricService]
})
export class OpenTelemetryModule extends ConfigurableModuleClass {
Expand All @@ -20,7 +33,7 @@ export class OpenTelemetryModule extends ConfigurableModuleClass {

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async beforeApplicationShutdown(_signal?: string | undefined) {
// Gracefuly shutdown the SDK to flush data to the collector.
// Gracefully shutdown the SDK to flush data to the collector.
await this.sdk.shutdown()
}

Expand All @@ -44,6 +57,7 @@ export class OpenTelemetryModule extends ConfigurableModuleClass {

return {
...module,
global: true,
providers: [
...(module.providers || []),
{
Expand Down Expand Up @@ -83,6 +97,7 @@ export class OpenTelemetryModule extends ConfigurableModuleClass {

return {
...module,
global: true,
providers: [
...(module.providers || []),
{
Expand All @@ -101,4 +116,26 @@ export class OpenTelemetryModule extends ConfigurableModuleClass {
]
}
}

static registerTest(): DynamicModule {
const module = this.register({
serviceName: 'test-service',
isEnabled: false
})

return {
...module,
providers: [
...(module.providers || []),
{
provide: TraceService,
useClass: StatefulTraceService
},
{
provide: MetricService,
useClass: StatefulMetricService
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Inject, Injectable } from '@nestjs/common'
import {
Attributes,
BatchObservableCallback,
Expand All @@ -11,13 +10,11 @@ import {
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
UpDownCounter,
metrics
UpDownCounter
} from '@opentelemetry/api'
import { OPEN_TELEMETRY_MODULE_OPTION } from '../open-telemetry.constant'
import { OpenTelemetryModuleOption } from '../open-telemetry.type'

@Injectable()
export const MetricService = Symbol('MetricService')

/**
* OpenTelemetry Metric Naming Conventions
*
Expand Down Expand Up @@ -49,64 +46,36 @@ import { OpenTelemetryModuleOption } from '../open-telemetry.type'
*
* @see https://opentelemetry.io/docs/specs/otel/metrics/semantic_conventions
*/
export class MetricService {
constructor(@Inject(OPEN_TELEMETRY_MODULE_OPTION) private readonly config: OpenTelemetryModuleOption) {}

public getMeter(): Meter {
return metrics.getMeter(this.config.serviceName)
}
export interface MetricService {
getMeter(): Meter

public createCounter<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Counter<T> {
return this.getMeter().createCounter(name, options)
}
createCounter<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Counter<T>

public createHistogram<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Histogram<T> {
return this.getMeter().createHistogram(name, options)
}
createHistogram<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Histogram<T>

public createGauge<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Gauge<T> {
return this.getMeter().createGauge(name, options)
}
createGauge<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Gauge<T>

public createUpDownCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): UpDownCounter<T> {
return this.getMeter().createUpDownCounter(name, options)
}
createUpDownCounter<T extends Attributes = Attributes>(name: string, options?: MetricOptions): UpDownCounter<T>

public createObservableGauge<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableGauge<T> {
return this.getMeter().createObservableGauge(name, options)
}
createObservableGauge<T extends Attributes = Attributes>(name: string, options?: MetricOptions): ObservableGauge<T>

public createObservableCounter<T extends Attributes = Attributes>(
createObservableCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableCounter<T> {
return this.getMeter().createObservableCounter(name, options)
}
): ObservableCounter<T>

public createObservableUpDownCounter<T extends Attributes = Attributes>(
createObservableUpDownCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableUpDownCounter<T> {
return this.getMeter().createObservableUpDownCounter(name, options)
}
): ObservableUpDownCounter<T>

public addBatchObservableCallback<T extends Attributes = Attributes>(
addBatchObservableCallback<T extends Attributes = Attributes>(
callback: BatchObservableCallback<T>,
observables: Observable<T>[]
): void {
this.getMeter().addBatchObservableCallback(callback, observables)
}
): void

public removeBatchObservableCallback<T extends Attributes = Attributes>(
removeBatchObservableCallback<T extends Attributes = Attributes>(
callback: BatchObservableCallback<T>,
observables: Observable<T>[]
): void {
this.getMeter().removeBatchObservableCallback(callback, observables)
}
): void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Inject, Injectable } from '@nestjs/common'
import {
Attributes,
BatchObservableCallback,
Counter,
Gauge,
Histogram,
Meter,
MetricOptions,
Observable,
ObservableCounter,
ObservableGauge,
ObservableUpDownCounter,
UpDownCounter,
metrics
} from '@opentelemetry/api'
import { OPEN_TELEMETRY_MODULE_OPTION } from '../open-telemetry.constant'
import { OpenTelemetryModuleOption } from '../open-telemetry.type'
import { MetricService } from './metric.service'

@Injectable()
export class OpenTelemetryMetricService implements MetricService {
constructor(@Inject(OPEN_TELEMETRY_MODULE_OPTION) private readonly config: OpenTelemetryModuleOption) {}

public getMeter(): Meter {
return metrics.getMeter(this.config.serviceName)
}

public createCounter<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Counter<T> {
return this.getMeter().createCounter(name, options)
}

public createHistogram<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Histogram<T> {
return this.getMeter().createHistogram(name, options)
}

public createGauge<T extends Attributes = Attributes>(name: string, options?: MetricOptions): Gauge<T> {
return this.getMeter().createGauge(name, options)
}

public createUpDownCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): UpDownCounter<T> {
return this.getMeter().createUpDownCounter(name, options)
}

public createObservableGauge<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableGauge<T> {
return this.getMeter().createObservableGauge(name, options)
}

public createObservableCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableCounter<T> {
return this.getMeter().createObservableCounter(name, options)
}

public createObservableUpDownCounter<T extends Attributes = Attributes>(
name: string,
options?: MetricOptions
): ObservableUpDownCounter<T> {
return this.getMeter().createObservableUpDownCounter(name, options)
}

public addBatchObservableCallback<T extends Attributes = Attributes>(
callback: BatchObservableCallback<T>,
observables: Observable<T>[]
): void {
this.getMeter().addBatchObservableCallback(callback, observables)
}

public removeBatchObservableCallback<T extends Attributes = Attributes>(
callback: BatchObservableCallback<T>,
observables: Observable<T>[]
): void {
this.getMeter().removeBatchObservableCallback(callback, observables)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Inject, Injectable } from '@nestjs/common'
import { Context, Span, SpanOptions, context, trace } from '@opentelemetry/api'
import { OPEN_TELEMETRY_MODULE_OPTION } from '../open-telemetry.constant'
import { OpenTelemetryException } from '../open-telemetry.exception'
import { OpenTelemetryModuleOption } from '../open-telemetry.type'
import { TraceService } from './trace.service'

@Injectable()
export class OpenTelemetryTraceService implements TraceService {
constructor(@Inject(OPEN_TELEMETRY_MODULE_OPTION) private readonly config: OpenTelemetryModuleOption) {}

public getTracer() {
return trace.getTracer(this.config.serviceName)
}

public getSpan(context: Context): Span | undefined {
return trace.getSpan(context)
}

public getActiveSpan(): Span | undefined {
return this.getSpan(context.active())
}

public startSpan(name: string, options?: SpanOptions, context?: Context): Span {
const tracer = this.getTracer()

return tracer.startSpan(name, options, context)
}

public startActiveSpan<F extends (span: Span) => ReturnType<F>>(name: string, fn: F): ReturnType<F>
public startActiveSpan<F extends (span: Span) => ReturnType<F>>(
name: string,
options: SpanOptions,
fn: F
): ReturnType<F>
public startActiveSpan<F extends (span: Span) => ReturnType<F>>(
name: string,
options: SpanOptions,
context: Context,
fn: F
): ReturnType<F>
public startActiveSpan<F extends (span: Span) => ReturnType<F>>(
name: string,
optionsOrFnOrContext?: SpanOptions | F | Context,
fnOrContextOrNothing?: F | Context | undefined,
fnOrNothing?: F | undefined
): ReturnType<F> {
const tracer = this.getTracer()

if (typeof optionsOrFnOrContext === 'function') {
return tracer.startActiveSpan(name, optionsOrFnOrContext)
}

if (typeof fnOrContextOrNothing === 'function') {
return tracer.startActiveSpan(name, optionsOrFnOrContext as SpanOptions, fnOrContextOrNothing)
}

if (fnOrNothing) {
return tracer.startActiveSpan(
name,
optionsOrFnOrContext as SpanOptions,
fnOrContextOrNothing as Context,
fnOrNothing
)
}

throw new OpenTelemetryException('Invalid arguments provided to startActiveSpan')
}
}
Loading

0 comments on commit 066b789

Please sign in to comment.