Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(deno): add Deno.serve integration#12460
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
d918d8bff9c169c9d38829246f9fd120824065406fFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| import { | ||
| SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, | ||
| SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, | ||
| captureException, | ||
| continueTrace, | ||
| defineIntegration, | ||
| setHttpStatus, | ||
| startSpan, | ||
| withIsolationScope, | ||
| } from '@sentry/core'; | ||
| import type { IntegrationFn, SpanAttributes } from '@sentry/types'; | ||
| import { getSanitizedUrlString, parseUrl } from '@sentry/utils'; | ||
| type RawHandler = (request: Request, info: Deno.ServeHandlerInfo) => Response | Promise<Response>; | ||
| const INTEGRATION_NAME = 'DenoServer'; | ||
| const _denoServerIntegration = (() => { | ||
| return { | ||
| name: INTEGRATION_NAME, | ||
| setupOnce() { | ||
| instrumentDenoServe(); | ||
| }, | ||
| }; | ||
| }) satisfies IntegrationFn; | ||
| /** | ||
| * Instruments `Deno.serve` to automatically create transactions and capture errors. | ||
| * | ||
| * Enabled by default in the Deno SDK. | ||
| * | ||
| * ```js | ||
| * Sentry.init({ | ||
| * integrations: [ | ||
| * Sentry.denoServerIntegration(), | ||
| * ], | ||
| * }) | ||
| * ``` | ||
| */ | ||
| export const denoServerIntegration = defineIntegration(_denoServerIntegration); | ||
| /** | ||
| * Instruments Deno.serve by patching it's options. | ||
| */ | ||
| export function instrumentDenoServe(): void { | ||
| Deno.serve = new Proxy(Deno.serve, { | ||
| apply(serveTarget, serveThisArg, serveArgs: unknown[]) { | ||
| const [arg1, arg2] = serveArgs; | ||
| if (typeof arg1 === 'function') { | ||
| serveArgs[0] = instrumentDenoServeOptions(arg1 as RawHandler); | ||
| } else if (typeof arg2 === 'function') { | ||
| serveArgs[1] = instrumentDenoServeOptions(arg2 as RawHandler); | ||
| } else if (arg1 && typeof arg1 === 'object' && 'handler' in arg1 && typeof arg1.handler === 'function') { | ||
| arg1.handler = instrumentDenoServeOptions(arg1.handler as RawHandler); | ||
| } | ||
| return serveTarget.apply(serveThisArg, serveArgs as Parameters<typeof Deno.serve>); | ||
| }, | ||
| }); | ||
| } | ||
| /** | ||
| * Instruments Deno.serve handler to automatically create spans and capture errors. | ||
| */ | ||
| function instrumentDenoServeOptions(handler: RawHandler): RawHandler { | ||
| return new Proxy(handler, { | ||
| apply(handlerTarget, handlerThisArg, handlerArgs: Parameters<RawHandler>) { | ||
| return withIsolationScope(isolationScope => { | ||
| isolationScope.clear(); | ||
Author 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. also this line should be removed when #12450 (comment) is resolved | ||
| const request = handlerArgs[0]; | ||
| if (request.method === 'OPTIONS' || request.method === 'HEAD') { | ||
| return handlerTarget.apply(handlerThisArg, handlerArgs); | ||
| } | ||
| const parsedUrl = parseUrl(request.url); | ||
| const attributes: SpanAttributes = { | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.deno.serve', | ||
| 'http.request.method': request.method || 'GET', | ||
| [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', | ||
| }; | ||
| if (parsedUrl.search) { | ||
| attributes['http.query'] = parsedUrl.search; | ||
| } | ||
| const url = getSanitizedUrlString(parsedUrl); | ||
| isolationScope.setSDKProcessingMetadata({ | ||
| request: { | ||
| url, | ||
| method: request.method, | ||
| headers: Object.fromEntries(request.headers), | ||
| }, | ||
| }); | ||
| return continueTrace( | ||
| { sentryTrace: request.headers.get('sentry-trace') || '', baggage: request.headers.get('baggage') }, | ||
| () => { | ||
| return startSpan( | ||
| { | ||
| attributes, | ||
| op: 'http.server', | ||
| name: `${request.method} ${parsedUrl.path || '/'}`, | ||
| }, | ||
| async span => { | ||
| try { | ||
| const response = await (handlerTarget.apply(handlerThisArg, handlerArgs) as ReturnType<RawHandler>); | ||
| if (response && response.status) { | ||
| setHttpStatus(span, response.status); | ||
| isolationScope.setContext('response', { | ||
| headers: Object.fromEntries(response.headers), | ||
| status_code: response.status, | ||
| }); | ||
| } | ||
| return response; | ||
| } catch (e) { | ||
| captureException(e, { | ||
| mechanism: { | ||
| type: 'deno', | ||
| handled: false, | ||
| data: { | ||
| function: 'serve', | ||
| }, | ||
| }, | ||
| }); | ||
| throw e; | ||
| } | ||
| }, | ||
| ); | ||
| }, | ||
| ); | ||
| }); | ||
| }, | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import type { sentryTypes } from '../build-test/index.js'; | ||
| import { sentryUtils } from '../build-test/index.js'; | ||
| import { DenoClient, getCurrentScope, getDefaultIntegrations } from '../build/index.mjs'; | ||
| import { getNormalizedEvent } from './normalize.ts'; | ||
| import { makeTestTransport } from './transport.ts'; | ||
| export function getTestClient( | ||
| callback: (event?: sentryTypes.Event) => void, | ||
| integrations: sentryTypes.Integration[] = [], | ||
| ): DenoClient { | ||
| const client = new DenoClient({ | ||
| dsn: 'https://233a45e5efe34c47a3536797ce15dafa@nothing.here/5650507', | ||
| debug: true, | ||
| integrations: [...getDefaultIntegrations({}), ...integrations], | ||
| stackParser: sentryUtils.createStackParser(sentryUtils.nodeStackLineParser()), | ||
| transport: makeTestTransport(envelope => { | ||
| callback(getNormalizedEvent(envelope)); | ||
| }), | ||
| }); | ||
| client.init(); | ||
| getCurrentScope().setClient(client); | ||
| return client; | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jsr wasn't supported on deno 1.38. deno.land/std/... won't receive non-critical updates and jsr is the recommended way
if needed we can lock it to some minor
re-running CI should pass I guess 👀