-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathindex.ts
138 lines (121 loc) · 4.19 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { isWrapped } from '@opentelemetry/core';
import { HapiInstrumentation } from '@opentelemetry/instrumentation-hapi';
import {
SDK_VERSION,
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SPAN_STATUS_ERROR,
captureException,
defineIntegration,
getActiveSpan,
getClient,
getDefaultIsolationScope,
getIsolationScope,
getRootSpan,
isEnabled,
spanToJSON,
} from '@sentry/core';
import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry';
import type { IntegrationFn, Span } from '@sentry/types';
import { consoleSandbox, logger } from '@sentry/utils';
import { DEBUG_BUILD } from '../../../debug-build';
import type { Boom, RequestEvent, ResponseObject, Server } from './types';
const _hapiIntegration = (() => {
return {
name: 'Hapi',
setupOnce() {
addOpenTelemetryInstrumentation(new HapiInstrumentation());
},
};
}) satisfies IntegrationFn;
/**
* Hapi integration
*
* Capture tracing data for Hapi.
* If you also want to capture errors, you need to call `setupHapiErrorHandler(server)` after you set up your server.
*/
export const hapiIntegration = defineIntegration(_hapiIntegration);
function isBoomObject(response: ResponseObject | Boom): response is Boom {
return response && (response as Boom).isBoom !== undefined;
}
function isErrorEvent(event: RequestEvent): event is RequestEvent {
return event && (event as RequestEvent).error !== undefined;
}
function sendErrorToSentry(errorData: object): void {
captureException(errorData, {
mechanism: {
type: 'hapi',
handled: false,
data: {
function: 'hapiErrorPlugin',
},
},
});
}
export const hapiErrorPlugin = {
name: 'SentryHapiErrorPlugin',
version: SDK_VERSION,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
register: async function (serverArg: Record<any, any>) {
const server = serverArg as unknown as Server;
server.events.on('request', (request, event) => {
if (getIsolationScope() !== getDefaultIsolationScope()) {
const route = request.route;
if (route && route.path) {
getIsolationScope().setTransactionName(`${route.method?.toUpperCase() || 'GET'} ${route.path}`);
}
} else {
DEBUG_BUILD &&
logger.warn('Isolation scope is still the default isolation scope - skipping setting transactionName');
}
const activeSpan = getActiveSpan();
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
if (request.response && isBoomObject(request.response)) {
sendErrorToSentry(request.response);
} else if (isErrorEvent(event)) {
sendErrorToSentry(event.error);
}
if (rootSpan) {
rootSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
rootSpan.end();
}
});
},
};
/**
* Add a Hapi plugin to capture errors to Sentry.
*/
export async function setupHapiErrorHandler(server: Server): Promise<void> {
await server.register(hapiErrorPlugin);
// Sadly, middleware spans do not go through `requestHook`, so we handle those here
// We register this hook in this method, because if we register it in the integration `setup`,
// it would always run even for users that are not even using hapi
const client = getClient();
if (client) {
client.on('spanStart', span => {
addHapiSpanAttributes(span);
});
}
// eslint-disable-next-line @typescript-eslint/unbound-method
if (!isWrapped(server.register) && isEnabled()) {
consoleSandbox(() => {
// eslint-disable-next-line no-console
console.warn(
'[Sentry] Hapi is not instrumented. This is likely because you required/imported hapi before calling `Sentry.init()`.',
);
});
}
}
function addHapiSpanAttributes(span: Span): void {
const attributes = spanToJSON(span).data || {};
// this is one of: router, plugin, server.ext
const type = attributes['hapi.type'];
// If this is already set, or we have no Hapi span, no need to process again...
if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) {
return;
}
span.setAttributes({
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.hapi',
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.hapi`,
});
}