Skip to content

[pull] master from apify:master - #293

Merged
pull[bot] merged 2 commits into
threatcode:masterfrom
apify:master
Sep 1, 2026
Merged

[pull] master from apify:master#293
pull[bot] merged 2 commits into
threatcode:masterfrom
apify:master

Conversation

@pull

@pull pull Bot commented Sep 1, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

NathanSavageKaimai and others added 2 commits September 1, 2026 15:23
Adds `@crawlee/otel`, an OpenTelemetry instrumentation for Crawlee v4.
It patches the crawler classes as they are imported, so an existing
crawler produces traces and logs without any change to its own code.

Closes #2955

## What it does

**Automatic instrumentation.** `CrawleeInstrumentation` plugs into the
OpenTelemetry SDK like any other instrumentation and patches these
methods:

| Module | Method | Span name | Kind |
| --- | --- | --- | --- |
| `@crawlee/basic` | `BasicCrawler.run` | `crawlee.crawler.run` |
internal |
| `@crawlee/basic` | `BasicCrawler.handleRequest` |
`crawlee.crawler.handleRequest` | internal |
| `@crawlee/basic` | `BasicCrawler.runRequestHandler` |
`crawlee.crawler.runRequestHandler` | internal |
| `@crawlee/basic` | `BasicCrawler.requestFunctionErrorHandler` |
`crawlee.crawler.requestFunctionErrorHandler` | internal |
| `@crawlee/basic` | `BasicCrawler.handleFailedRequestHandler` |
`crawlee.crawler.handleFailedRequestHandler` | internal |
| `@crawlee/http` | `HttpCrawler.makeHttpRequest` |
`crawlee.http.makeHttpRequest` | client |
| `@crawlee/browser` | `BrowserCrawler.navigate` |
`crawlee.browser.navigate` | client |

Every span carries `code.function.name`, the `run` span carries
`crawlee.crawler.type`, and the methods that receive a crawling context
also carry `url.full`, `http.request.method`, `crawlee.request.id`, and
`crawlee.request.retry_count`. `url.full` and `http.request.method` are
the stable semantic conventions, so the traces stay comparable with
other instrumented HTTP clients; Crawlee data with no convention keeps
the `crawlee.` prefix. A method that is missing from the installed
Crawlee version is skipped with a warning instead of breaking the module
load.

**Log forwarding.** Patches the logging methods Crawlee derives in
`BaseCrawleeLogger`, so `log.info()`, `log.exception()`, and the rest
are emitted as OpenTelemetry log records with the Crawlee level mapped
onto the OpenTelemetry severity. Because every v4 logger derives from
that base, a Winston, Pino, or hand-written adapter is forwarded just
like the default one. The structured `data` of a log call becomes the
record attributes, and an `Error` in it becomes `exception.type`,
`exception.message`, and `exception.stacktrace`.

**Manual spans.** `wrapWithSpan()` wraps a request handler, a hook, or
any other function. The span name and options are either static or
computed from the call arguments. A synchronous function stays
synchronous.

**Custom instrumentation.** `customInstrumentation` takes module, class,
and method triples, so a class the package does not know about can be
patched the same way. `requestHandlingInstrumentation` and
`logInstrumentation` switch the two built-in groups off.

Errors set `SpanStatusCode.ERROR` and record an exception event. Success
is left `UNSET`, following the OpenTelemetry guidance that an
instrumentation should not claim a span succeeded.

## Setup

Automatic instrumentation needs the OpenTelemetry module hook installed
before anything imports Crawlee, and ESM imports are hoisted, so the
hook and the SDK setup have to sit in preloaded files rather than at the
top of the crawler:

```js
// otel-hook.js
import { register } from 'node:module';
import { pathToFileURL } from 'node:url';

register('@opentelemetry/instrumentation/hook.mjs', pathToFileURL('./'));
```

```bash
node --import ./otel-hook.js --import ./otel-setup.js ./main.js
```

`otel-setup.js` builds the `NodeSDK` with `CrawleeInstrumentation` and
owns the shutdown flush. The guide has both files in full. The example
in this repo runs with:

```bash
pnpm exec tsx \
  --import ./docs/guides/trace_and_monitor_register_hook.ts \
  --import ./docs/guides/trace_and_monitor_setup.ts \
  ./docs/guides/trace_and_monitor_basic.ts
```

Verified end to end against Jaeger over OTLP/gRPC. One thing worth
knowing: Jaeger is a tracing backend and rejects OTLP logs, so the
forwarded logs need a collector or a backend that ingests them. The
guide calls that out.

## Docs

New guide at `docs/guides/trace-and-monitor-crawlers.mdx`, with runnable
snippets for the setup, `wrapWithSpan`, and custom instrumentation, the
configuration reference, and the full list of automatically instrumented
methods and attributes.

## Dependencies

`@opentelemetry/instrumentation` and
`@opentelemetry/semantic-conventions` are dependencies.
`@opentelemetry/api` and `@opentelemetry/api-logs` are peer
dependencies, so the package shares the host application's API instance.

---------

Co-authored-by: Josef Procházka <jos.prochazka@post.cz>
Co-authored-by: Josef Prochazka <josef.prochazka@apify.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jan Buchar <jan@buchar.dev>
@pull pull Bot locked and limited conversation to collaborators Sep 1, 2026
@pull pull Bot added the ⤵️ pull label Sep 1, 2026
@pull
pull Bot merged commit e872b81 into threatcode:master Sep 1, 2026
7 of 8 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants