Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
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
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ import {
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import type { TracingChannelLifeCycleOptions } from '../../../tracing-channel';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import { AWS_SDK_ORIGIN } from './constants';
import { ServicesExtensions } from './services';
import type { NormalizedRequest, NormalizedResponse, RequestMetadata } from './types';
Expand DownExpand Up@@ -49,16 +49,6 @@ interface AwsV3Command {
constructor?: { name?: string };
}

/** Runs a span-building callback so a throw inside it can never break the user's aws-sdk call. */
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:aws-sdk] error building span', error);
return undefined;
}
}

// `metadata` is smithy's `ResponseMetadata`, read off the untyped channel result/error (`any` for the
// same reason as `CommandInput`, see types.ts).
function setMetadataAttributes(span: Span, metadata: Record<string, any> | undefined): void {
Expand DownExpand Up@@ -91,7 +81,7 @@ const _awsChannelIntegration = (() => {
}

const getSpan = (data: AwsSendChannelContext): Span | undefined =>
safe(() => {
safeChannelCallback(() => {
const command = data.arguments[0] as AwsV3Command | undefined;
const commandName = command?.constructor?.name;
if (!command || !commandName) {
Expand DownExpand Up@@ -140,7 +130,7 @@ const _awsChannelIntegration = (() => {
// so `cloud.region` cannot be lost when `send` settles first (e.g. an early failure).
//
// The provider call is guarded separately: the span is already started, so a synchronous
// throw bubbling into the enclosing `safe` would discard it without ending it (a leaked
// throw bubbling into the enclosing `safeChannelCallback` would discard it without ending it (a leaked
// open span).
let regionResult: string | Promise<string> | undefined;
try {
Expand DownExpand Up@@ -169,7 +159,7 @@ const _awsChannelIntegration = (() => {

// Inject trace-propagation headers into outgoing messages (SQS/SNS/Lambda). Runs before
// `send` proceeds, so the mutated `commandInput` is used to build the request.
safe(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));
safeChannelCallback(() => servicesExtensions.requestPostSpanHook(normalizedRequest, span));

return span;
});
Expand All@@ -186,7 +176,7 @@ const _awsChannelIntegration = (() => {

// The channel `result`/`error` are untyped; the `$metadata` casts below name smithy's
// `ResponseMetadata` shape (`any`-valued, see `setMetadataAttributes`).
safe(() => {
safeChannelCallback(() => {
if (failed) {
const err = data.error as
| { $metadata?: Record<string, any>; RequestId?: string; extendedRequestId?: string }
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import { debug } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import type { FirestoreReference } from './firestore-types';
import { startFirestoreSpan } from './firestore';
import { wrapFunctionsRegistration } from './functions';
Expand DownExpand Up@@ -43,24 +41,10 @@ const FUNCTIONS_TRIGGERS: Array<{ channel: string; triggerType: string }> = [

const NOOP = (): void => {};

/**
* Runs a span-building callback so a throw inside it can never break the user's firebase call: these run
* inside the `tracingChannel(...)` machinery wrapping the real function, where an unguarded throw would
* propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:firebase] error handling channel event', error);
return undefined;
}
}

export function instrumentFirebase() {
for (const { channel, spanName, useParent } of FIRESTORE_OPERATIONS) {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<FirestoreChannelContext>(channel), data =>
safe(() => {
safeChannelCallback(() => {
const reference = data.arguments[0] as FirestoreReference | undefined;
if (!reference) {
return undefined;
Expand All@@ -76,7 +60,8 @@ export function instrumentFirebase() {
// registration call, so we only rewrap the handler argument here (in `start`) and open the
// span inside that wrapper. The other lifecycle events are irrelevant, so no-op them.
diagnosticsChannel.tracingChannel(channel).subscribe({
start: data => void safe(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
start: data =>
void safeChannelCallback(() => wrapFunctionsRegistration(data as { arguments: unknown[] }, triggerType)),
end: NOOP,
asyncStart: NOOP,
asyncEnd: NOOP,
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
import * as diagnosticsChannel from 'node:diagnostics_channel';
import type { IntegrationFn } from '@sentry/core';
import { debug, defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { DEBUG_BUILD } from '../../../debug-build';
import { defineIntegration, extendIntegration, waitForTracingChannelBinding } from '@sentry/core';
import { graphqlIntegration as graphqlNativeIntegration } from '../../../graphql';
import type { GraphqlDiagnosticChannelsOptions } from '../../../graphql/graphql-dc-subscriber';
import { CHANNELS } from '../../../orchestrion/channels';
import { bindTracingChannelToSpan } from '../../../tracing-channel';
import { bindTracingChannelToSpan, safeChannelCallback } from '../../../tracing-channel';
import {
finalizeExecuteSpan,
finalizeValidateSpan,
Expand DownExpand Up@@ -35,20 +34,6 @@ function getOptionsWithDefaults(options: GraphqlDiagnosticChannelsOptions): Grap
};
}

/**
* Runs a span-building callback so a throw inside it can never break the user's graphql call: these
* run inside the `tracingChannel(...).trace*` machinery wrapping the real function (as the `getSpan`
* producer / `beforeSpanEnd` handler), where an unguarded throw would propagate into the traced call.
*/
function safe<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion:graphql] error building span', error);
return undefined;
}
}

const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions = {}) => {
const config = getOptionsWithDefaults(options);
const getConfig = (): GraphqlResolvedConfig => config;
Expand All@@ -62,19 +47,19 @@ const _graphqlChannelIntegration = ((options: GraphqlDiagnosticChannelsOptions =

waitForTracingChannelBinding(() => {
bindTracingChannelToSpan(diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_PARSE), () =>
safe(() => startParseSpan()),
safeChannelCallback(() => startParseSpan()),
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_VALIDATE),
data => safe(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeValidateSpan(span, data.result)) },
data => safeChannelCallback(() => startValidateSpan(data.arguments[1])),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeValidateSpan(span, data.result)) },
);

bindTracingChannelToSpan(
diagnosticsChannel.tracingChannel<GraphqlChannelContext>(CHANNELS.GRAPHQL_EXECUTE),
data => safe(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safe(() => finalizeExecuteSpan(span, data.result)) },
data => safeChannelCallback(() => startExecuteSpan(data.arguments, data.self, config, getConfig)),
{ beforeSpanEnd: (span, data) => void safeChannelCallback(() => finalizeExecuteSpan(span, data.result)) },
);
});
},
Expand Down
10 changes: 10 additions & 0 deletions packages/server-utils/src/tracing-channel.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,6 +90,16 @@ export interface TracingChannelBindingHandle<TData extends object = object> {

const NOOP = (): void => {};

/** Runs a span-building callback so a throw inside it can never break the user's traced call. */
export function safeChannelCallback<T>(fn: () => T): T | undefined {
try {
return fn();
} catch (error) {
DEBUG_BUILD && debug.warn('[orchestrion] error handling channel event', error);
return undefined;
}
}

/**
* Bind a span and its lifecycle to a tracing channel so the span becomes the active async context
* for the traced operation and is ended when the operation completes.
Expand Down
Loading