-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(javascript): Add Claude Code Agent SDK instrumentation #17844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
codyde
wants to merge
8
commits into
develop
Choose a base branch
from
claude-code-agent-instrumentation
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+670
−1
Draft
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7862429
feat(node): Add Claude Code Agent SDK instrumentation
codyde 6373d01
fix(node): Reset init state on Claude Code instrumentation failure to…
codyde e0e651b
fix(node): Use SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN constant instead of s…
codyde 736b0ef
fix(node): Add SEMANTIC_ATTRIBUTE_SENTRY_OP and improve error handlin…
codyde dbcf981
fix(node): Fix TypeScript types for createInstrumentedClaudeQuery
codyde d0c2c3b
feat(nextjs): Export Claude Code integration types
codyde dcc42a8
feat(nextjs): Add explicit runtime exports for Claude Code integration
codyde c897e76
fix(nextjs): Import Claude Code exports before re-exporting to preven…
codyde File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
packages/node/src/integrations/tracing/claude-code/helpers.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { getClient } from '@sentry/core'; | ||
import { patchClaudeCodeQuery } from './instrumentation'; | ||
import type { ClaudeCodeOptions } from './index'; | ||
|
||
const CLAUDE_CODE_INTEGRATION_NAME = 'ClaudeCode'; | ||
|
||
// Global singleton - only patch once per application instance | ||
let _globalPatchedQuery: ((...args: unknown[]) => AsyncGenerator<unknown, void, unknown>) | null = null; | ||
let _initPromise: Promise<void> | null = null; | ||
|
||
/** | ||
* Lazily loads and patches the Claude Code SDK. | ||
* Ensures only one patched instance exists globally. | ||
*/ | ||
async function ensurePatchedQuery(): Promise<void> { | ||
if (_globalPatchedQuery) { | ||
return; | ||
} | ||
|
||
if (_initPromise) { | ||
return _initPromise; | ||
} | ||
|
||
_initPromise = (async () => { | ||
try { | ||
// Use webpackIgnore to prevent webpack from trying to resolve this at build time | ||
// The import resolves at runtime from the user's node_modules | ||
const sdkPath = '@anthropic-ai/claude-agent-sdk'; | ||
const claudeSDK = await import(/* webpackIgnore: true */ sdkPath); | ||
|
||
if (!claudeSDK || typeof claudeSDK.query !== 'function') { | ||
throw new Error( | ||
`Failed to find 'query' function in @anthropic-ai/claude-agent-sdk.\n` + | ||
`Make sure you have version >=0.1.0 installed.`, | ||
); | ||
} | ||
|
||
const client = getClient(); | ||
const integration = client?.getIntegrationByName<ClaudeCodeOptions>(CLAUDE_CODE_INTEGRATION_NAME); | ||
const options = integration?.options || {}; | ||
|
||
_globalPatchedQuery = patchClaudeCodeQuery(claudeSDK.query, options); | ||
} catch (error) { | ||
// Reset state on failure to allow retry on next call | ||
_initPromise = null; | ||
|
||
const errorMessage = | ||
error instanceof Error | ||
? error.message | ||
: 'Unknown error occurred while loading @anthropic-ai/claude-agent-sdk'; | ||
|
||
throw new Error( | ||
`Failed to instrument Claude Code SDK:\n${errorMessage}\n\n` + | ||
`Make sure @anthropic-ai/claude-agent-sdk is installed:\n` + | ||
` npm install @anthropic-ai/claude-agent-sdk\n` + | ||
` # or\n` + | ||
` yarn add @anthropic-ai/claude-agent-sdk`, | ||
); | ||
} | ||
})(); | ||
|
||
return _initPromise; | ||
} | ||
|
||
/** | ||
* Creates a Sentry-instrumented query function for the Claude Code SDK. | ||
* | ||
* This is a convenience helper that reduces boilerplate to a single line. | ||
* The SDK is lazily loaded on first query call, and the patched version is cached globally. | ||
* | ||
* **Important**: This helper is NOT automatic. You must call it in your code. | ||
* The Claude Code SDK cannot be automatically instrumented due to ESM module | ||
* and webpack bundling limitations. | ||
* | ||
* @returns An instrumented query function ready to use | ||
* | ||
* @example | ||
* ```typescript | ||
* import { createInstrumentedClaudeQuery } from '@sentry/node'; | ||
* import type { SDKUserMessage } from '@anthropic-ai/claude-agent-sdk'; | ||
* | ||
* const query = createInstrumentedClaudeQuery(); | ||
* | ||
* // Use as normal - automatically instrumented! | ||
* for await (const message of query({ | ||
* prompt: 'Hello', | ||
* options: { model: 'claude-sonnet-4-5' } | ||
* })) { | ||
* console.log(message); | ||
* } | ||
* ``` | ||
* | ||
* Configuration is automatically pulled from your `claudeCodeIntegration()` setup: | ||
* | ||
* @example | ||
* ```typescript | ||
* Sentry.init({ | ||
* integrations: [ | ||
* Sentry.claudeCodeIntegration({ | ||
* recordInputs: true, // These options are used | ||
* recordOutputs: true, // by createInstrumentedClaudeQuery() | ||
* }) | ||
* ] | ||
* }); | ||
* ``` | ||
*/ | ||
export function createInstrumentedClaudeQuery(): (...args: unknown[]) => AsyncGenerator<unknown, void, unknown> { | ||
return async function* query(...args: unknown[]): AsyncGenerator<unknown, void, unknown> { | ||
await ensurePatchedQuery(); | ||
|
||
if (!_globalPatchedQuery) { | ||
throw new Error('[Sentry] Failed to initialize instrumented Claude Code query function'); | ||
} | ||
|
||
yield* _globalPatchedQuery(...args); | ||
}; | ||
} |
130 changes: 130 additions & 0 deletions
130
packages/node/src/integrations/tracing/claude-code/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import type { IntegrationFn } from '@sentry/core'; | ||
import { defineIntegration } from '@sentry/core'; | ||
import { patchClaudeCodeQuery } from './instrumentation'; | ||
|
||
export interface ClaudeCodeOptions { | ||
/** | ||
* Whether to record prompt messages. | ||
* Defaults to Sentry client's `sendDefaultPii` setting. | ||
*/ | ||
recordInputs?: boolean; | ||
|
||
/** | ||
* Whether to record response text, tool calls, and tool outputs. | ||
* Defaults to Sentry client's `sendDefaultPii` setting. | ||
*/ | ||
recordOutputs?: boolean; | ||
} | ||
|
||
const CLAUDE_CODE_INTEGRATION_NAME = 'ClaudeCode'; | ||
|
||
const _claudeCodeIntegration = ((options: ClaudeCodeOptions = {}) => { | ||
return { | ||
name: CLAUDE_CODE_INTEGRATION_NAME, | ||
options, | ||
setupOnce() { | ||
// Note: Automatic patching via require hooks doesn't work for ESM modules | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually i believe InstrumentationModuleDefinition will automatically patch Node.js modules when they're loaded via import, you can find some patterns in other AI integrations e.g anthropic AI |
||
// or webpack-bundled dependencies. Users must manually patch using patchClaudeCodeQuery() | ||
// in their route files. | ||
}, | ||
}; | ||
}) satisfies IntegrationFn; | ||
|
||
/** | ||
* Adds Sentry tracing instrumentation for the Claude Code SDK. | ||
* | ||
* **Important**: Due to ESM module and bundler limitations, this integration requires | ||
* using the `createInstrumentedClaudeQuery()` helper function in your code. | ||
* See the example below for proper usage. | ||
* | ||
* This integration captures telemetry data following OpenTelemetry Semantic Conventions | ||
* for Generative AI, including: | ||
* - Agent invocation spans (`invoke_agent`) | ||
* - LLM chat spans (`chat`) | ||
* - Tool execution spans (`execute_tool`) | ||
* - Token usage, model info, and session tracking | ||
* | ||
* @example | ||
* ```typescript | ||
* // Step 1: Configure the integration | ||
* import * as Sentry from '@sentry/node'; | ||
* | ||
* Sentry.init({ | ||
* dsn: 'your-dsn', | ||
* integrations: [ | ||
* Sentry.claudeCodeIntegration({ | ||
* recordInputs: true, | ||
* recordOutputs: true | ||
* }) | ||
* ], | ||
* }); | ||
* | ||
* // Step 2: Use the helper in your routes | ||
* import { createInstrumentedClaudeQuery } from '@sentry/node'; | ||
* | ||
* const query = createInstrumentedClaudeQuery(); | ||
* | ||
* // Use query as normal - automatically instrumented! | ||
* for await (const message of query({ | ||
* prompt: 'Hello', | ||
* options: { model: 'claude-sonnet-4-5' } | ||
* })) { | ||
* console.log(message); | ||
* } | ||
* ``` | ||
* | ||
* ## Options | ||
* | ||
* - `recordInputs`: Whether to record prompt messages (default: respects `sendDefaultPii` client option) | ||
* - `recordOutputs`: Whether to record response text, tool calls, and outputs (default: respects `sendDefaultPii` client option) | ||
* | ||
* ### Default Behavior | ||
* | ||
* By default, the integration will: | ||
* - Record inputs and outputs ONLY if `sendDefaultPii` is set to `true` in your Sentry client options | ||
* - Otherwise, inputs and outputs are NOT recorded unless explicitly enabled | ||
* | ||
* @example | ||
* ```typescript | ||
* // Record inputs and outputs when sendDefaultPii is false | ||
* Sentry.init({ | ||
* integrations: [ | ||
* Sentry.claudeCodeIntegration({ | ||
* recordInputs: true, | ||
* recordOutputs: true | ||
* }) | ||
* ], | ||
* }); | ||
* | ||
* // Never record inputs/outputs regardless of sendDefaultPii | ||
* Sentry.init({ | ||
* sendDefaultPii: true, | ||
* integrations: [ | ||
* Sentry.claudeCodeIntegration({ | ||
* recordInputs: false, | ||
* recordOutputs: false | ||
* }) | ||
* ], | ||
* }); | ||
* ``` | ||
* | ||
* @see https://docs.sentry.io/platforms/javascript/guides/node/ai-monitoring/ | ||
*/ | ||
export const claudeCodeIntegration = defineIntegration(_claudeCodeIntegration); | ||
|
||
/** | ||
* Manually patch the Claude Code SDK query function with Sentry instrumentation. | ||
* | ||
* **Note**: Most users should use `createInstrumentedClaudeQuery()` instead, | ||
* which is simpler and handles option retrieval automatically. | ||
* | ||
* This low-level function is exported for advanced use cases where you need | ||
* explicit control over the patching process. | ||
* | ||
* @param queryFunction - The original query function from @anthropic-ai/claude-agent-sdk | ||
* @param options - Instrumentation options (recordInputs, recordOutputs) | ||
* @returns Instrumented query function | ||
* | ||
* @see createInstrumentedClaudeQuery for the recommended high-level helper | ||
*/ | ||
export { patchClaudeCodeQuery }; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.