opentelemetry-js icon indicating copy to clipboard operation
opentelemetry-js copied to clipboard

http instrumentation does not work when running jest

Open karlbohlmark opened this issue 2 years ago • 7 comments
trafficstars

What happened?

Steps to Reproduce

The following script correctly sets traceparent when run using ts-node (it will not work with plain node because of module hoisting) but the http request is not instrumented if run by just

import { NodeSDK } from '@opentelemetry/sdk-node'
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'

const traceExporter = new OTLPTraceExporter()
const sdk = new NodeSDK({
    traceExporter,
    instrumentations: [new HttpInstrumentation()],
})
sdk.start()

import * as http from 'http'

http.get('http://localhost:8080')

Expected Result

traceparent is added to the outgoing http headers when running using jest

Actual Result

traceparent is not added

Additional Details

I'm not sure if this is intended to work, but it's a nice use case to be able to trace from integration tests to the services tested. I pushed a small repro to https://github.com/karlbohlmark/otel-jest

OpenTelemetry Setup Code

import { NodeSDK } from '@opentelemetry/sdk-node'
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'

const traceExporter = new OTLPTraceExporter()
const sdk = new NodeSDK({
    traceExporter,
    instrumentations: [new HttpInstrumentation()],
})
sdk.start()

package.json

{
  "scripts": {
    "ts-node": "ts-node src/otel.test.ts",
    "jest": "jest otel"
  },
  "dependencies": {
    "@opentelemetry/exporter-trace-otlp-proto": "^0.38.0",
    "@opentelemetry/instrumentation-http": "^0.38.0",
    "@opentelemetry/sdk-node": "^0.38.0",
    "@types/jest": "^29.5.0",
    "@types/node": "^18.15.11",
    "jest": "^29.5.0",
    "ts-jest": "^29.1.0",
    "ts-node": "^10.9.1",
    "typescript": "^5.0.4"
  }
}

Relevant log output

No response

karlbohlmark avatar Apr 14 '23 11:04 karlbohlmark

After digging a little bit, this seems to be because of https://github.com/elastic/require-in-the-middle/issues/50

karlbohlmark avatar Apr 14 '23 12:04 karlbohlmark

Maybe the solution is just to note this in the docs and recommend using another testing framework

karlbohlmark avatar Apr 14 '23 12:04 karlbohlmark

Thank you for reporting this.

I think the bug label here is valid. Assigning p2 as it causes telemetry to be incorrectly exported. Relevant issue on the jest repo: https://github.com/facebook/jest/issues/11295

pichlermarc avatar Apr 19 '23 15:04 pichlermarc

Maybe it would be possible to get someone on the jest team to take an interest here. I think it could make sense for jest to have tracing built in.

I have now put the necessary workarounds in place for my use case, but I still have some minor issues like

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down. From src/tests/global-settings.test.ts.
      at Immediate._onImmediate (node_modules/@opentelemetry/otlp-proto-exporter-base/src/platform/node/OTLPProtoExporterNodeBase.ts:78:26)

Also I'm not sure if all tracing data is properly exported before jest shuts down. If tracing was a first class citizen of jest this would be taken care of.

karlbohlmark avatar Apr 20 '23 10:04 karlbohlmark

@karlbohlmark Could you elaborate a bit on the workarounds you used?

ddeboer avatar Jul 21 '23 14:07 ddeboer

I am actually able to get http traces with quite easy setup, my problem is with instrumentation-pg that is missing, probably due to require-in-the-middle issue.

Here is my setup with auto-instrumentation:

# package.json
  ...
  "scripts": {
    ...
    "test": "OTEL_SERVICE_NAME=jest-tests NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" jest'"
  },
  "dependencies": {
    "@opentelemetry/auto-instrumentations-node": "^0.40.2",
    "@opentelemetry/instrumentation": "^0.36.0",
    ...
  }
  
  # test.spec
  # test are straight forward
    endpoints.map(url => it(`${url} (GET)`, async function() {
    await request(app)
      .get(url)
      .expect(200);

Make sure to wait a bit after the tests to make sure all spans were sent. I am running collector available on default path localhost:4318.

Hope that helps

issackr avatar Jan 30 '24 08:01 issackr