@discue/open-telemetry-tracing
TypeScript icon, indicating that this package has built-in type declarations

0.10.0 • Public • Published

Vue logo


GitHub tag Latest Stable Version License
NPM Downloads NPM Downloads
contributions - welcome Made with Node.js

open-telemetry-tracing

Kickstarts your OpenTelemetry implementation with first-class abstractions for

  • adding tracing capabilities to NodeJS environments
  • collecting and publishing of spans to local and cloud environments
  • creating spans and child spans
  • tracking successful completion of spans
  • recording exceptions in case of errors during span execution

Screenshot of traces collected by Jaeger.

Setup

OpenTelemetry and its Instrumentations will inject tracing functionality into well-known and supported packages.

To enable tracing, the tracing runtime needs to be loaded before your application. To do so, set the NODE_OPTIONS environment variable and require the file node_modules/@discue/open-telemetry-tracing/lib/instrumentation.cjs, which is the entry point for tracing.

  • NODE_OPTIONS=--require node_modules/@discue/open-telemetry-tracing/lib/instrumentation.cjs

Instrumentation

Instrumentations are the heart of tracing. Most of the tracing instrumentation is platform-agnostic. However, some parts of it can be platform-dependent.

This module features support for GCP and half-baked support for AWS (due to lack of usage of the platform in other projects). You can find the default instrumentations here:

If you feel instrumentations (or other features) are missing, please get in touch with us and / or open a PR 🙂.

How to create a span

Import the function createTracer and pass the filepath as an optional parameter.

import { createTracer } from '@discue/open-telemetry-tracing';

Call the function createTracer and pass the filepath to the current file as an optional parameter.

const { withActiveSpan } = createTracer({
    filepath: import.meta.url
})

Wrap existing or new code inside a call to withActiveSpan.

/**
 * 
 * @param {_types.Request} req 
 * @param {_types.Response} res 
 * @param {Object} options
 * @returns 
 */
async handleRequest(req, res, { resourceIds }) {
    // creates a new active span with name handle-delete-request
    // will watch execution and record failures
    //
    // pass an object with additional attributes as second parameter
    // to get more key value pairs added to the span
    await withActiveSpan('handle-delete-request', async (span) => {
        const resource = await this._service.get(resourceIds)
        if (resource == null) {
            // custom extension of the span with implementation-specific
            // event and status
            span.addEvent('Not found', { resourceIds })
                .setStatus({ code: SpanStatusCode.ERROR })

            return sendNotFound(res)
        } else {
            await this._service.delete(resourceIds)
            sendOk({ req, res, body: {}, links: {} })
        }
    })
}

See a full implementation e.g. in @stfsy/api-kit/http-post-resource-endpoint.

How to create a span synchronously

To wrap a synchronous function inside a span, use the withActiveSpanSync method. It also accepts a spanAttributes object as optional second parameter.

import { createTracer } from '@discue/open-telemetry-tracing';
import { nanoid } from "nanoid";

const { withActiveSpanSync } = createTracer({
    filepath: import.meta.url
})

/**
 * Creates a url-safe resource id.
 * 
 * @module newResourceId
 * @returns {String}
 */
export const newResourceId = () => {
    // wrap the sync call in a span and return the value
    // that way the tracing is transparent for all callers
    return withActiveSpanSync('create-resource-id', () => {
        return nanoId()
    })
}

See a full implementation e.g. in @stfsy/api-kit/resource-id.

How to create an orphaned span

That is a span that has no parent. Useful if you want to prevent deep nesting of spans. To create an orphan span call the withOrphanedSpan method of the module. The spanAttributes object is optional and can be omitted.

import { createTracer } from '@discue/open-telemetry-tracing';
import { SpanStatusCode } from '@opentelemetry/api';

const { withOrphanedSpan } = createTracer({
    filepath: import.meta.url
})

const { method, headers } = request
const incomingContentType = headers['content-type'] ?? ''
const spanAttributes = { method, incomingContentType }

// checks whether the content type is set
// sets span status accordingly
// adds content-type as a span attribute so it can be queried via UI e.g. Jaeger
await withOrphanedSpan('check-content-type-is-set', spanAttributes, (span) => {
    if (!incomingContentType ) {
        span.addEvent('Check failed').setStatus({ code: SpanStatusCode.ERROR })
        return sendUnsupportedMedia(response)

    } else {
        span.addEvent('Check succeeded').setStatus({ code: SpanStatusCode.OK })
    }

    return nextFunction()
})

See a full implementation e.g. in @stfsy/api-kit/content-type-middleware.

Configuration

The following environment variables will be used:

  • DSQ_OT_TRACING_SERVICE_NAME
    • default: api-kit
  • DSQ_OT_LOCAL_OLTP_URL
  • DSQ_OT_USE_SIMPLE_SPAN_PROCESSOR
    • default: false

Exports

  • /: The main export is the createTracer function. Import it via @discue/open-telemetry-tracing to create traces as shown above.
  • /status-codes: Returns valid status codes a span can have. Use this expor to not couple your application to the Open Telemetry libraries. Use it via @discue/open-telemetry-tracing/status-codes.
  • /instrumentation: The instrumentation script needs to be loaded via NODE_OPTIONS function. Internally it is also used via @discue/open-telemetry-tracing/instrumentation to get ahold of the current tracer. Implementation applications should not need to use this

Test

./test.sh

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i @discue/open-telemetry-tracing

Weekly Downloads

122

Version

0.10.0

License

MIT

Unpacked Size

37.9 kB

Total Files

30

Last publish

Collaborators

  • stfsy