Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .agents/skills/convert-otel-integration/EXAMPLES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# express

- OTEL Instrumentation Package: @opentelemetry/instrumentation-express
- Core integration: packages/core/src/integrations/express/
- Unit tests: packages/core/test/lib/integrations/express/
- Node integration: packages/node/src/integrations/tracing/express.ts

# connect

- OTEL Instrumentation Package: @opentelemetry/instrumentation-connect
- Core integration: packages/core/src/integrations/connect/
- Unit tests: packages/core/test/lib/integrations/connect/
- Node integration: packages/node/src/integrations/tracing/connect.ts
58 changes: 58 additions & 0 deletions .agents/skills/convert-otel-integration/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
name: convert-otel-integration
description: Add a new portable integration which can be used by all JavaScript SDKs, by converting an `@opentelemetry/instrumentation-*` implementation that was previously limited to only the Node SDK.
argument-hint: <module-name>
---

# Converting an OpenTelemetry Instrumentation to Portable Sentry SDK Integration

This skill converts the node-specific instrumentation found in an `@opentelemetry/instrumentation-<module-name>` package, and creates a standalone integration that either leverages existing TracingChannel support, or takes the module exports as an option argument for patching, instead of relying on intercepting the import.

## Instructions

### Step 1: Analysis

- Read the code in `@opentelemetry/instrumentation-<module-name>` dependency, to see what is being patched.
- Read the code that uses it in `packages/node/src/integrations`. Especially, take note of what functionality is injected in callbacks or options, which provide Sentry-specific behaviors.

### Step 2: Standalone Implementation

- Use the integrations listed in `.agents/skills/convert-otel-integration/EXAMPLES.md` as examples for guidance.
- **Create New Integration**
- Create the new portable integration in `packages/core/src/integrations/<module-name>`.
- This code must encapsulate the relevant patching, but importantly, _not_ any module-loading interception.
- Expect to receive the module's exported object as an argument.
- Capture data using Sentry-specific methods and functions rather than OpenTelemetry APIs.
- Export the new standalone integration methods from the `@sentry/core` package for use in other SDKs.
- **DO NOT** port any "unpatch" or "uninstrument" methods. Once applied, the Sentry patch is permanent, so we can save on bundle size.
- Make sure to include a header on each code file, attributing the source appropriately in accordance with its license, and declaring the new implemetation as a derivative work.
- **Unit Tests**
- Create unit tests at `packages/core/test/lib/integrations/<module-name>`, with a corresponding `.test.ts` file for each `.ts` file created in `packages/core/src/integrations/<module-name>`.
- Each created test file should cover its corresponding source file to 100% of statements, lines, and branches, with a minimum of tests.
- Mock internal and external interfaces to target all parts of the new code.
- **Check**
- Ensure that all unit tests pass: `cd packages/core && vitest run test/lib/integrations/<module-name>`
- Ensure that the build suceeds: `yarn build`

### Step 3: Node Integration

- Use the integrations listed in `.agents/skills/convert-otel-integration/EXAMPLES.md` as examples for guidance.
- Locate the file in `packages/node` that uses the `@opentelemetry/instrumentation-<module-name>` dependency.
- Replace this integration code with a new integration. The new code should create a class that extends `InstrumentationBase<{CONFIG_TYPE}>`, and call the patching method created in the previous step.
- Remove the dependency on `@opentelemetry/instrumentation-<module-name>` in `packages/node/package.json`.
- Verify that all unit tests in `packages/node` still pass: `cd packages/node ; yarn test`.
- Add integration points if needed to maintain compatibility with existing OpenTelemetry features of the node SDK, but keep any divergence to a minimum.
- If the added functionality for OTEL compatibility is more than 10 lines of custom code, pause and ask for human guidance.

### Step 4: Integration Tests

- Run `yarn fix` to detect and address any lint issues.
- Run `yarn build` to build all the code.
- Run `yarn install` to update the lockfile, removing the now-unnecessary otel instrumentation dependency.
- Run `cd dev-packages/node-integration-tests; yarn test` to run node integration tests.
- Debug and fix any issues that this process uncovers.

### Step 5: Summarize and Record

- Write a summary of what was done to `.agents/skills/convert-otel-integration/logs/<module-name>.md`.
- Write a new entry to `.agents/skills/convert-otel-integration/EXAMPLES.md` for the new integration's replaced OTEL intestrumentation package, core integration location, unit test location, and node integration location.
81 changes: 81 additions & 0 deletions .agents/skills/convert-otel-integration/logs/connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Convert OTel Integration: connect

## Overview

Converted `@opentelemetry/instrumentation-connect` from a Node-only OTel-based integration to a portable, OTel-free implementation that works in any JavaScript environment.

## Changes

### New: `packages/core/src/integrations/connect/index.ts`

Portable integration derived from the OTel connect instrumentation. Key exports:

- **`patchConnectModule(connect, options?)`** — wraps the connect factory so every app it creates is automatically patched. Returns the wrapped factory.
- **`patchConnectApp(app, options?)`** — patches an already-created connect app instance's `use` and `handle` methods directly.
- **`setupConnectErrorHandler(app)`** — adds a 4-argument error middleware that captures exceptions via `captureException`.
- **`ConnectIntegrationOptions`** — interface with optional `onRouteResolved` callback, used by platform integrations (e.g. Node.js) to propagate the resolved route to OTel RPC metadata.

Implementation notes:

- Route stack tracking is ported from OTel's `utils.js` — a per-request symbol property holds a stack of route path segments that is combined into a full HTTP route string via `generateRoute`.
- Spans are created with `startSpanManual` and Sentry attributes are set directly (`sentry.op`, `sentry.origin`) — no `spanStart` hook needed.
- Origin changed from `'auto.http.otel.connect'` → `'auto.http.connect'` (no longer OTel-based).
- `withActiveSpan(parentSpan, ...)` is called when invoking `next()` so subsequent middleware spans are siblings of the parent rather than children of the current span.
- Middleware arity (`length`) is preserved on the patched function so connect can distinguish error middlewares (4 args) from regular ones (3 args).

### New: `packages/core/test/lib/integrations/connect/index.test.ts`

20 unit tests covering:

- `patchConnectModule` factory wrapping
- Anonymous and named middleware span creation
- `onRouteResolved` callback behavior
- Span lifecycle (ends on `next()` or response `close` event, not both)
- No span when no active parent span
- Error middleware argument positions
- Handle route stack tracking
- Double-patch debug error logging
- `setupConnectErrorHandler` error capture

### Modified: `packages/core/src/index.ts`

Added exports:

```typescript
export { patchConnectModule, setupConnectErrorHandler } from './integrations/connect/index';
export type { ConnectIntegrationOptions, ConnectModule } from './integrations/connect/index';
```

### Rewritten: `packages/node/src/integrations/tracing/connect.ts`

Replaced the OTel-instrumentation-based implementation with one that:

- Defines `ConnectInstrumentation extends InstrumentationBase` whose `init()` method calls `patchConnectModule` via `InstrumentationNodeModuleDefinition`.
- Sets OTel RPC metadata route via the `onRouteResolved` callback (preserving existing OTel route-tracking behavior for Node users).
- `setupConnectErrorHandler` delegates to `coreSetupConnectErrorHandler` and calls `ensureIsWrapped` to verify the instrumentation is active.
- Removed the `spanStart` hook (`addConnectSpanAttributes`) — no longer needed since attributes are set directly when spans are created.

### Modified: `packages/node/package.json`

Removed dependency:

```
"@opentelemetry/instrumentation-connect": "0.56.0"
```

### Modified: `dev-packages/node-integration-tests/suites/tracing/connect/test.ts`

Updated expected `sentry.origin` value:

- Before: `'auto.http.otel.connect'`
- After: `'auto.http.connect'`

## Verification

- `yarn fix` — no lint or format issues
- `yarn build` — full production build succeeds
- `yarn install` — lockfile updated, removing the OTel connect package
- `packages/core` connect unit tests: **20/20 passed**
- `packages/node` unit tests: **324/325 passed** (1 pre-existing skip)
- `node-integration-tests` connect suite: **6/6 passed** (ESM + CJS)
- Hono test failures confirmed pre-existing on base branch, unrelated to this change
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ describe('connect auto-instrumentation', () => {
'connect.name': '/',
'connect.type': 'request_handler',
'http.route': '/',
'sentry.origin': 'auto.http.otel.connect',
'sentry.origin': 'auto.http.connect',
'sentry.op': 'request_handler.connect',
}),
description: '/',
origin: 'auto.http.otel.connect',
origin: 'auto.http.connect',
op: 'request_handler.connect',
status: 'ok',
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ export type {
ExpressMiddleware,
ExpressErrorMiddleware,
} from './integrations/express/types';
export { patchConnectModule, setupConnectErrorHandler } from './integrations/connect/index';
export type { ConnectIntegrationOptions, ConnectModule } from './integrations/connect/index';
export { dedupeIntegration } from './integrations/dedupe';
export { extraErrorDataIntegration } from './integrations/extraerrordata';
export { rewriteFramesIntegration } from './integrations/rewriteframes';
Expand Down
Loading
Loading