A tracing library for node and the browser that conforms to DataDog's APM Tracing API, where there "resource" and "service" concepts are first-class citizens. It also includes support for context objects to avoid continuation-local-storage (CLS).
# using yarn
yarn add @convoy/tracer
# using npm
npm install --save @convoy/tracer
In order to start tracing, a project (client or server) should start by creating a new instance of a Reporter, which defines how a trace should get reported to Datadog or other APM service. For front-end clients, this typically involves a service call which passes the trace along to the APM agent. For NodeJS services, consider using @convoy/datadog.
import { Reporter } from '@convoy/tracer';
const apiReporter = new Reporter({
flushHandler: async (timings, traces) => {
return await fetch({...});
},
});
The reporter will call flushHandler
if there are any traces to report on the next requestIdleCallback
(browser) after recording the trace or every flushIntervalSeconds
.
Decorators are a convenient way to add tracing to an app or service. This library exposes a decorator factory function to generate a decorator that can be reused through your app or service. It establishes a default service
name to use for all traces, specifies the reporter, the sample rate (recommended for frequently called functions) and the ability to configure which argument in the traced function a context object will be read from or created at (nice when the function you are tracing uses a framework that puts the context object at different positions, such as a GQL resolver or Express middleware).
Given JavaScript's asynchronous control flow via an event loop, a context object is an important mechanism to tie APM spans together into one trace without colliding with other async actions that may overlap. This avoids the need to introduce continuation-local-storage which can be difficult to implement and requires some complex monkeypatching.
import { createTraceDecorator } from '@convoy/tracer';
export const trace = createTraceDecorator({
service: __CONFIG__.service,
tracerConfig: {
fullTraceSampleRate: 1 / 20,
reporter: apiReporter,
},
contextArgumentPosition: 1, // if not specified, a new context will be created
});
class Something {
@trace()
function doSomething({ foo, bar }, context) {
// The context object now includes a Tracer object
// and can generally be ignored
}
}
The trace decorator is a function itself because its supports the ability to override the attributes of the span it generates.
class Something {
@trace({
resource: 'setThing',
service: 'my-library',
name: 'Something.doSomething',
annotator: (span, { foo }) => {
if (foo) {
span.setMeta({ isFoo: true });
},
tags: {
clientId,
},
})
function doSomething({ foo, bar }, context) {
// Traced function
}
}