open-telemetry / opentelemetry-js-contrib

OpenTelemetry instrumentation for JavaScript modules
https://opentelemetry.io
Apache License 2.0
699 stars 515 forks source link

Cucumber instrumentation not creating spans #2502

Open sergimola opened 1 week ago

sergimola commented 1 week ago

I am trying to instrument my cucumber tests in NodeJS but I can't make it trace anything at all.

I tried using both import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node' and import { CucumberInstrumentation } from '@opentelemetry/instrumentation-cucumber' but had no luck with either.

This is the file I have that sets up the telemetry:

/* eslint-disable @typescript-eslint/no-unused-vars */
import { After, Before, BeforeStep, AfterStep, Status, BeforeAll } from '@cucumber/cucumber'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { CucumberInstrumentation } from '@opentelemetry/instrumentation-cucumber'
import { Resource } from '@opentelemetry/resources'
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'
import * as api from '@opentelemetry/api'
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'
import * as grpc from '@grpc/grpc-js'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'

const collectorOptions = {
    url: 'localhost:4317',
    credentials: grpc.credentials.createInsecure(),
}

const sdk = new NodeSDK({
    resource: new Resource({
        [ATTR_SERVICE_NAME]: 'E2E System Tests'
    }),
    traceExporter: new OTLPTraceExporter(collectorOptions),
    instrumentations: [new CucumberInstrumentation()],
})

const contextManager = new AsyncHooksContextManager().enable()
api.context.setGlobalContextManager(contextManager)

try {
    sdk.start()
    console.log('Tracing initialized')
} catch (error) {
    console.log('Error initializing tracing', error)
}

// Executed after all scenarios
AfterAll(function () {
    sdk.shutdown()
        .then(() => console.log('Tracing terminated'))
        .catch((error) => console.log('Error terminating tracing', error))
})

It gets called and if I create spans manually they show up in the OTELCollector, so telemetry seems to be properly set up.

Does anyone have a real example of to set it up for a real cucumber project?

pichlermarc commented 1 week ago

do you compile your code to ESM or commonjs? :thinking: There's an extra step for ESM users that needs to be followed, because we only hook require calls by default but they don't exist in ESM, so we need a loader hook - details here https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/esm-support.md

sergimola commented 1 week ago

do you compile your code to ESM or commonjs? 🤔 There's an extra step for ESM users that needs to be followed, because we only hook require calls by default but they don't exist in ESM, so we need a loader hook - details here https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/esm-support.md

Yes, it turns out that was the problem. I followed your suggestion and after quite some trial and error I made it work.

If this is useful for anyone, this is what my execution command looks like to run cucumber-js tests: npm run build && node --experimental-loader=@opentelemetry/instrumentation/hook.mjs --import ./build/tracing.js ./node_modules/@cucumber/cucumber/bin/cucumber-js --tags "not @ignore"

And this is my tracing.ts file:

import { NodeSDK } from '@opentelemetry/sdk-node'
import { Resource } from '@opentelemetry/resources'
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'
import * as api from '@opentelemetry/api'
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'
import * as grpc from '@grpc/grpc-js'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import TracingConstants from './src/tracing/constants'
import { Constants } from './src/shared/constants'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'

const collectorOptions = {
    url: TracingConstants.OTEL_EXPORTER_OTLP_ENDPOINT,
    credentials: grpc.credentials.createInsecure(),
}

const spanProcessors = [new SimpleSpanProcessor(new OTLPTraceExporter(collectorOptions))]

if (Constants.ERROR_LEVEL === 'DEBUG') {
    spanProcessors.push(new SimpleSpanProcessor(new ConsoleSpanExporter()))
}

export const sdk = new NodeSDK({
    resource: new Resource({
        [ATTR_SERVICE_NAME]: TracingConstants.SERVICE_NAME,
        [ATTR_SERVICE_VERSION]: TracingConstants.SERVICE_VERSION,
    }),
    spanProcessors: spanProcessors,
    instrumentations: [getNodeAutoInstrumentations()],
})

const contextManager = new AsyncHooksContextManager().enable()
api.context.setGlobalContextManager(contextManager)

try {
    sdk.start()
    console.log('Tracing initialized')
} catch (error) {
    console.log('Error initializing tracing', error)
}

Edit: I used SimpleSpanProcessor also with the OTLPTraceExporter because otherwise the traces were not being pushed to the OTEL Collector before the process exited.