@logtape/sentry 2.3.0-dev.844 → 2.3.0-dev.846
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mod.cjs +14 -3
- package/dist/mod.d.cts +59 -6
- package/dist/mod.d.cts.map +1 -1
- package/dist/mod.d.ts +59 -6
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +14 -3
- package/dist/mod.js.map +1 -1
- package/package.json +2 -2
package/dist/mod.cjs
CHANGED
|
@@ -112,7 +112,7 @@ function getErrorProperty(properties, propertyNames) {
|
|
|
112
112
|
* await configure({
|
|
113
113
|
* sinks: {
|
|
114
114
|
* sentry: getSentrySink({
|
|
115
|
-
*
|
|
115
|
+
* breadcrumbs: true,
|
|
116
116
|
* }),
|
|
117
117
|
* },
|
|
118
118
|
* loggers: [
|
|
@@ -185,7 +185,7 @@ function getSentrySink(optionsOrClient) {
|
|
|
185
185
|
if ("parentSpanId" in spanCtx) attributes.parent_span_id = spanCtx.parentSpanId;
|
|
186
186
|
}
|
|
187
187
|
const client = sentry.getClient();
|
|
188
|
-
if (client) {
|
|
188
|
+
if (client && shouldSendToLogs(transformed, options.logs)) {
|
|
189
189
|
const { enableLogs, _experiments } = client.getOptions();
|
|
190
190
|
const loggingEnabled = enableLogs ?? _experiments?.enableLogs;
|
|
191
191
|
const sentryLogger = sentry.logger;
|
|
@@ -212,7 +212,7 @@ function getSentrySink(optionsOrClient) {
|
|
|
212
212
|
level: eventLevel,
|
|
213
213
|
extra: attributes
|
|
214
214
|
});
|
|
215
|
-
else if (options
|
|
215
|
+
else if (shouldAddBreadcrumb(transformed, options)) {
|
|
216
216
|
const isolationScope = sentry.getIsolationScope();
|
|
217
217
|
isolationScope?.addBreadcrumb({
|
|
218
218
|
category: transformed.category.join("."),
|
|
@@ -231,6 +231,17 @@ function getSentrySink(optionsOrClient) {
|
|
|
231
231
|
}
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
+
function shouldSendToLogs(record, options) {
|
|
235
|
+
return options?.level == null || (0, __logtape_logtape.compareLogLevel)(record.level, options.level) >= 0;
|
|
236
|
+
}
|
|
237
|
+
function shouldAddBreadcrumb(record, options) {
|
|
238
|
+
const breadcrumbOptions = options.breadcrumbs;
|
|
239
|
+
if (breadcrumbOptions === false) return false;
|
|
240
|
+
if (breadcrumbOptions === true) return true;
|
|
241
|
+
if (breadcrumbOptions == null) return options.enableBreadcrumbs === true;
|
|
242
|
+
if (breadcrumbOptions.level != null && (0, __logtape_logtape.compareLogLevel)(record.level, breadcrumbOptions.level) < 0) return false;
|
|
243
|
+
return breadcrumbOptions.maxLevel == null || (0, __logtape_logtape.compareLogLevel)(record.level, breadcrumbOptions.maxLevel) <= 0;
|
|
244
|
+
}
|
|
234
245
|
|
|
235
246
|
//#endregion
|
|
236
247
|
exports.getSentrySink = getSentrySink;
|
package/dist/mod.d.cts
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
|
-
import { LogRecord, Sink } from "@logtape/logtape";
|
|
1
|
+
import { LogLevel, LogRecord, Sink } from "@logtape/logtape";
|
|
2
2
|
import { LogSeverityLevel, ParameterizedString, SeverityLevel } from "@sentry/core";
|
|
3
3
|
|
|
4
4
|
//#region src/mod.d.ts
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Options for records sent through Sentry's Logs API.
|
|
8
|
+
*
|
|
9
|
+
* @since 2.3.0
|
|
10
|
+
*/
|
|
11
|
+
interface SentryLogsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Minimum level for records sent through Sentry's Logs API.
|
|
14
|
+
*
|
|
15
|
+
* @default `"trace"`
|
|
16
|
+
*/
|
|
17
|
+
level?: LogLevel;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options for records added as Sentry breadcrumbs.
|
|
21
|
+
*
|
|
22
|
+
* @since 2.3.0
|
|
23
|
+
*/
|
|
24
|
+
interface SentryBreadcrumbOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Minimum level for records added as breadcrumbs.
|
|
27
|
+
*
|
|
28
|
+
* @default `"trace"`
|
|
29
|
+
*/
|
|
30
|
+
level?: LogLevel;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum level for records added as breadcrumbs.
|
|
33
|
+
*/
|
|
34
|
+
maxLevel?: LogLevel;
|
|
35
|
+
}
|
|
6
36
|
/**
|
|
7
37
|
* A Sentry client instance type (used for v1.1.x backward compatibility).
|
|
8
38
|
*
|
|
@@ -103,14 +133,37 @@ interface SentrySinkOptions {
|
|
|
103
133
|
/**
|
|
104
134
|
* Enable automatic breadcrumb creation for log events.
|
|
105
135
|
*
|
|
106
|
-
* When enabled,
|
|
107
|
-
* providing a complete context trail when errors occur. Breadcrumbs
|
|
108
|
-
* lightweight and only appear in error reports for debugging.
|
|
136
|
+
* When enabled, non-error logs become breadcrumbs in Sentry's isolation
|
|
137
|
+
* scope, providing a complete context trail when errors occur. Breadcrumbs
|
|
138
|
+
* are lightweight and only appear in error reports for debugging.
|
|
109
139
|
*
|
|
110
140
|
* @default false
|
|
141
|
+
* @deprecated Use `breadcrumbs` instead.
|
|
111
142
|
* @since 1.3.0
|
|
112
143
|
*/
|
|
113
144
|
enableBreadcrumbs?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Enables and configures automatic breadcrumb creation for log events.
|
|
147
|
+
*
|
|
148
|
+
* Set this to `true` to use the default breadcrumb behavior, or pass an
|
|
149
|
+
* options object to control which non-error log levels become breadcrumbs.
|
|
150
|
+
*
|
|
151
|
+
* When this option is set, it takes precedence over the deprecated
|
|
152
|
+
* `enableBreadcrumbs` option.
|
|
153
|
+
*
|
|
154
|
+
* @default false
|
|
155
|
+
* @since 2.3.0
|
|
156
|
+
*/
|
|
157
|
+
breadcrumbs?: boolean | SentryBreadcrumbOptions;
|
|
158
|
+
/**
|
|
159
|
+
* Configures records sent through Sentry's Logs API.
|
|
160
|
+
*
|
|
161
|
+
* The Sentry SDK must still have structured logging enabled with
|
|
162
|
+
* `enableLogs: true` or `_experiments.enableLogs: true`.
|
|
163
|
+
*
|
|
164
|
+
* @since 2.3.0
|
|
165
|
+
*/
|
|
166
|
+
logs?: SentryLogsOptions;
|
|
114
167
|
/**
|
|
115
168
|
* Property names to inspect for an `Error` instance when deciding whether
|
|
116
169
|
* error-level records should be sent through Sentry's `captureException()`.
|
|
@@ -190,7 +243,7 @@ interface SentrySinkOptions {
|
|
|
190
243
|
* await configure({
|
|
191
244
|
* sinks: {
|
|
192
245
|
* sentry: getSentrySink({
|
|
193
|
-
*
|
|
246
|
+
* breadcrumbs: true,
|
|
194
247
|
* }),
|
|
195
248
|
* },
|
|
196
249
|
* loggers: [
|
|
@@ -223,5 +276,5 @@ interface SentrySinkOptions {
|
|
|
223
276
|
declare function getSentrySink(optionsOrClient?: SentrySinkOptions | SentryInstance): Sink;
|
|
224
277
|
//# sourceMappingURL=mod.d.ts.map
|
|
225
278
|
//#endregion
|
|
226
|
-
export { SentryInstance, SentryNamespace, SentrySinkOptions, getSentrySink };
|
|
279
|
+
export { SentryBreadcrumbOptions, SentryInstance, SentryLogsOptions, SentryNamespace, SentrySinkOptions, getSentrySink };
|
|
227
280
|
//# sourceMappingURL=mod.d.cts.map
|
package/dist/mod.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"mod.d.cts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;AAmFA;AAcA;;AAMU,UApBO,iBAAA,CAoBP;EAAQ;AAKG;AA4BrB;AAiBA;;EAAgC,KAKnB,CAAA,EArEH,QAqEG;;;;;;;AAuDT,UApHa,uBAAA,CAoHb;EAAM;AADQ;AAelB;;;EAmB0B,KA2BA,CAAA,EA1KhB,QA0KgB;EAAuB;;;EA+BF,QAAA,CAAA,EApMlC,QAoMkC;AA4F/C;;;;;AAEO;;;;;;;;;UAtQU,cAAA;qDAGI;;;;;;;;;;;;UAcJ,eAAA;;;;0BAKJ,sCACQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA0CN;;;YAGD;;;;;;WAQH,QACP,OACE,4BAEW,iCACG;;;;;;UAUH,iBAAA;;;;;;;;;;;;;;;;;;;WAmBN;;;;;;;;;;;;;;;;;;;;;;;;;0BA2Be;;;;;;;;;SAUjB;;;;;;;;;;;;;;;;;;;wBAqBe,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4FtB,aAAA,mBACI,oBAAoB,iBACrC"}
|
package/dist/mod.d.ts
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
|
-
import { LogRecord, Sink } from "@logtape/logtape";
|
|
1
|
+
import { LogLevel, LogRecord, Sink } from "@logtape/logtape";
|
|
2
2
|
import { LogSeverityLevel, ParameterizedString, SeverityLevel } from "@sentry/core";
|
|
3
3
|
|
|
4
4
|
//#region src/mod.d.ts
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Options for records sent through Sentry's Logs API.
|
|
8
|
+
*
|
|
9
|
+
* @since 2.3.0
|
|
10
|
+
*/
|
|
11
|
+
interface SentryLogsOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Minimum level for records sent through Sentry's Logs API.
|
|
14
|
+
*
|
|
15
|
+
* @default `"trace"`
|
|
16
|
+
*/
|
|
17
|
+
level?: LogLevel;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Options for records added as Sentry breadcrumbs.
|
|
21
|
+
*
|
|
22
|
+
* @since 2.3.0
|
|
23
|
+
*/
|
|
24
|
+
interface SentryBreadcrumbOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Minimum level for records added as breadcrumbs.
|
|
27
|
+
*
|
|
28
|
+
* @default `"trace"`
|
|
29
|
+
*/
|
|
30
|
+
level?: LogLevel;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum level for records added as breadcrumbs.
|
|
33
|
+
*/
|
|
34
|
+
maxLevel?: LogLevel;
|
|
35
|
+
}
|
|
6
36
|
/**
|
|
7
37
|
* A Sentry client instance type (used for v1.1.x backward compatibility).
|
|
8
38
|
*
|
|
@@ -103,14 +133,37 @@ interface SentrySinkOptions {
|
|
|
103
133
|
/**
|
|
104
134
|
* Enable automatic breadcrumb creation for log events.
|
|
105
135
|
*
|
|
106
|
-
* When enabled,
|
|
107
|
-
* providing a complete context trail when errors occur. Breadcrumbs
|
|
108
|
-
* lightweight and only appear in error reports for debugging.
|
|
136
|
+
* When enabled, non-error logs become breadcrumbs in Sentry's isolation
|
|
137
|
+
* scope, providing a complete context trail when errors occur. Breadcrumbs
|
|
138
|
+
* are lightweight and only appear in error reports for debugging.
|
|
109
139
|
*
|
|
110
140
|
* @default false
|
|
141
|
+
* @deprecated Use `breadcrumbs` instead.
|
|
111
142
|
* @since 1.3.0
|
|
112
143
|
*/
|
|
113
144
|
enableBreadcrumbs?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Enables and configures automatic breadcrumb creation for log events.
|
|
147
|
+
*
|
|
148
|
+
* Set this to `true` to use the default breadcrumb behavior, or pass an
|
|
149
|
+
* options object to control which non-error log levels become breadcrumbs.
|
|
150
|
+
*
|
|
151
|
+
* When this option is set, it takes precedence over the deprecated
|
|
152
|
+
* `enableBreadcrumbs` option.
|
|
153
|
+
*
|
|
154
|
+
* @default false
|
|
155
|
+
* @since 2.3.0
|
|
156
|
+
*/
|
|
157
|
+
breadcrumbs?: boolean | SentryBreadcrumbOptions;
|
|
158
|
+
/**
|
|
159
|
+
* Configures records sent through Sentry's Logs API.
|
|
160
|
+
*
|
|
161
|
+
* The Sentry SDK must still have structured logging enabled with
|
|
162
|
+
* `enableLogs: true` or `_experiments.enableLogs: true`.
|
|
163
|
+
*
|
|
164
|
+
* @since 2.3.0
|
|
165
|
+
*/
|
|
166
|
+
logs?: SentryLogsOptions;
|
|
114
167
|
/**
|
|
115
168
|
* Property names to inspect for an `Error` instance when deciding whether
|
|
116
169
|
* error-level records should be sent through Sentry's `captureException()`.
|
|
@@ -190,7 +243,7 @@ interface SentrySinkOptions {
|
|
|
190
243
|
* await configure({
|
|
191
244
|
* sinks: {
|
|
192
245
|
* sentry: getSentrySink({
|
|
193
|
-
*
|
|
246
|
+
* breadcrumbs: true,
|
|
194
247
|
* }),
|
|
195
248
|
* },
|
|
196
249
|
* loggers: [
|
|
@@ -223,5 +276,5 @@ interface SentrySinkOptions {
|
|
|
223
276
|
declare function getSentrySink(optionsOrClient?: SentrySinkOptions | SentryInstance): Sink;
|
|
224
277
|
//# sourceMappingURL=mod.d.ts.map
|
|
225
278
|
//#endregion
|
|
226
|
-
export { SentryInstance, SentryNamespace, SentrySinkOptions, getSentrySink };
|
|
279
|
+
export { SentryBreadcrumbOptions, SentryInstance, SentryLogsOptions, SentryNamespace, SentrySinkOptions, getSentrySink };
|
|
227
280
|
//# sourceMappingURL=mod.d.ts.map
|
package/dist/mod.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"mod.d.ts","names":[],"sources":["../src/mod.ts"],"sourcesContent":[],"mappings":";;;;;;;AAmFA;AAcA;;AAMU,UApBO,iBAAA,CAoBP;EAAQ;AAKG;AA4BrB;AAiBA;;EAAgC,KAKnB,CAAA,EArEH,QAqEG;;;;;;;AAuDT,UApHa,uBAAA,CAoHb;EAAM;AADQ;AAelB;;;EAmB0B,KA2BA,CAAA,EA1KhB,QA0KgB;EAAuB;;;EA+BF,QAAA,CAAA,EApMlC,QAoMkC;AA4F/C;;;;;AAEO;;;;;;;;;UAtQU,cAAA;qDAGI;;;;;;;;;;;;UAcJ,eAAA;;;;0BAKJ,sCACQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA0CN;;;YAGD;;;;;;WAQH,QACP,OACE,4BAEW,iCACG;;;;;;UAUH,iBAAA;;;;;;;;;;;;;;;;;;;WAmBN;;;;;;;;;;;;;;;;;;;;;;;;;0BA2Be;;;;;;;;;SAUjB;;;;;;;;;;;;;;;;;;;wBAqBe,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4FtB,aAAA,mBACI,oBAAoB,iBACrC"}
|
package/dist/mod.js
CHANGED
|
@@ -111,7 +111,7 @@ function getErrorProperty(properties, propertyNames) {
|
|
|
111
111
|
* await configure({
|
|
112
112
|
* sinks: {
|
|
113
113
|
* sentry: getSentrySink({
|
|
114
|
-
*
|
|
114
|
+
* breadcrumbs: true,
|
|
115
115
|
* }),
|
|
116
116
|
* },
|
|
117
117
|
* loggers: [
|
|
@@ -184,7 +184,7 @@ function getSentrySink(optionsOrClient) {
|
|
|
184
184
|
if ("parentSpanId" in spanCtx) attributes.parent_span_id = spanCtx.parentSpanId;
|
|
185
185
|
}
|
|
186
186
|
const client = sentry.getClient();
|
|
187
|
-
if (client) {
|
|
187
|
+
if (client && shouldSendToLogs(transformed, options.logs)) {
|
|
188
188
|
const { enableLogs, _experiments } = client.getOptions();
|
|
189
189
|
const loggingEnabled = enableLogs ?? _experiments?.enableLogs;
|
|
190
190
|
const sentryLogger = sentry.logger;
|
|
@@ -211,7 +211,7 @@ function getSentrySink(optionsOrClient) {
|
|
|
211
211
|
level: eventLevel,
|
|
212
212
|
extra: attributes
|
|
213
213
|
});
|
|
214
|
-
else if (options
|
|
214
|
+
else if (shouldAddBreadcrumb(transformed, options)) {
|
|
215
215
|
const isolationScope = sentry.getIsolationScope();
|
|
216
216
|
isolationScope?.addBreadcrumb({
|
|
217
217
|
category: transformed.category.join("."),
|
|
@@ -230,6 +230,17 @@ function getSentrySink(optionsOrClient) {
|
|
|
230
230
|
}
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
|
+
function shouldSendToLogs(record, options) {
|
|
234
|
+
return options?.level == null || compareLogLevel(record.level, options.level) >= 0;
|
|
235
|
+
}
|
|
236
|
+
function shouldAddBreadcrumb(record, options) {
|
|
237
|
+
const breadcrumbOptions = options.breadcrumbs;
|
|
238
|
+
if (breadcrumbOptions === false) return false;
|
|
239
|
+
if (breadcrumbOptions === true) return true;
|
|
240
|
+
if (breadcrumbOptions == null) return options.enableBreadcrumbs === true;
|
|
241
|
+
if (breadcrumbOptions.level != null && compareLogLevel(record.level, breadcrumbOptions.level) < 0) return false;
|
|
242
|
+
return breadcrumbOptions.maxLevel == null || compareLogLevel(record.level, breadcrumbOptions.maxLevel) <= 0;
|
|
243
|
+
}
|
|
233
244
|
|
|
234
245
|
//#endregion
|
|
235
246
|
export { getSentrySink };
|
package/dist/mod.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.js","names":["record: LogRecord","tplValues: string[]","level: LogLevel","properties: Readonly<Record<string, unknown>>","propertyNames: readonly string[]","optionsOrClient?: SentrySinkOptions | SentryInstance","legacyClient: SentryInstance | undefined","options: SentrySinkOptions","msg: ParameterizedString","ctx?: unknown","exception: unknown","hint?: unknown"],"sources":["../src/mod.ts"],"sourcesContent":["import {\n compareLogLevel,\n getLogger,\n type LogLevel,\n type LogRecord,\n type Sink,\n} from \"@logtape/logtape\";\nimport type {\n LogSeverityLevel,\n ParameterizedString,\n SeverityLevel,\n} from \"@sentry/core\";\n// Import namespace to safely check for public logger API (added in v9.41.0)\nimport * as SentryCore from \"@sentry/core\";\n// Cross-runtime inspect: Deno.inspect / util.inspect (handles circular\n// references); falls back to JSON.stringify in browsers. Resolved via the\n// `#util` import map per runtime.\nimport { inspect } from \"#util\";\n\n/**\n * Converts a LogTape {@link LogRecord} into a Sentry {@link ParameterizedString}.\n *\n * This preserves the template structure for better message grouping in Sentry,\n * allowing similar messages with different values to be grouped together.\n *\n * @param record The log record to convert.\n * @returns A parameterized string with template and values.\n */\nfunction getParameterizedString(record: LogRecord): ParameterizedString {\n let result = \"\";\n let tplString = \"\";\n const tplValues: string[] = [];\n for (let i = 0; i < record.message.length; i++) {\n if (i % 2 === 0) {\n result += record.message[i];\n tplString += String(record.message[i]).replaceAll(\"%\", \"%%\");\n } else {\n const value = inspect(record.message[i]);\n result += value;\n tplString += `%s`;\n tplValues.push(value);\n }\n }\n const paramStr = new String(result) as ParameterizedString;\n paramStr.__sentry_template_string__ = tplString;\n paramStr.__sentry_template_values__ = tplValues;\n return paramStr;\n}\n\n// Level normalization helpers\n\nfunction mapLevelForEvents(level: LogLevel): SeverityLevel {\n switch (level) {\n case \"trace\":\n return \"debug\";\n default:\n return level as SeverityLevel; // debug | info | error | fatal\n }\n}\n\nfunction mapLevelForLogs(level: LogLevel): LogSeverityLevel {\n switch (level) {\n case \"trace\":\n return \"debug\";\n case \"warning\":\n return \"warn\";\n case \"debug\":\n case \"info\":\n case \"error\":\n case \"fatal\":\n return level;\n default:\n return \"info\"; // fallback\n }\n}\n\nconst defaultErrorPropertyNames = [\"error\", \"err\"] as const;\n\nfunction getErrorProperty(\n properties: Readonly<Record<string, unknown>>,\n propertyNames: readonly string[],\n): readonly [property: string, error: Error] | undefined {\n for (const property of propertyNames) {\n if (properties[property] instanceof Error) {\n return [property, properties[property]];\n }\n }\n return undefined;\n}\n\n/**\n * A Sentry client instance type (used for v1.1.x backward compatibility).\n *\n * Client instances only support `captureMessage` and `captureException`.\n * For scope operations (breadcrumbs, user context, traces), the sink always\n * uses global functions from `@sentry/core`.\n *\n * @deprecated This is only used for backward compatibility with v1.1.x.\n * New code should use `getSentrySink()` without parameters, which automatically\n * uses Sentry's global functions.\n *\n * @since 1.3.0\n */\nexport interface SentryInstance {\n captureMessage: (\n message: string,\n captureContext?: SeverityLevel | unknown,\n ) => string;\n captureException: (exception: unknown, hint?: unknown) => string;\n}\n\n/**\n * A Sentry SDK namespace object.\n *\n * Pass the namespace imported by your application when *@logtape/sentry* should\n * use the same Sentry module instance that initialized your app, for example\n * `import * as Sentry from \"@sentry/node\"`.\n *\n * @since 2.2.0\n */\nexport interface SentryNamespace {\n /**\n * Captures a message event and sends it to Sentry.\n */\n captureMessage(\n message: ParameterizedString,\n captureContext?: SeverityLevel | unknown,\n ): string;\n\n /**\n * Captures an exception event and sends it to Sentry.\n */\n captureException(exception: unknown, hint?: unknown): string;\n\n /**\n * Gets the currently active span, if any.\n */\n getActiveSpan():\n | {\n spanContext: () => {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n };\n }\n | undefined;\n\n /**\n * Gets the currently active Sentry client, if any.\n */\n getClient():\n | {\n getOptions: () => {\n enableLogs?: boolean;\n _experiments?: {\n enableLogs?: boolean;\n };\n };\n }\n | undefined;\n\n /**\n * Gets the current isolation scope.\n */\n getIsolationScope():\n | {\n addBreadcrumb: (breadcrumb: {\n category: string;\n level: SeverityLevel;\n message: string;\n timestamp: number;\n data: Record<string, unknown>;\n }) => void;\n }\n | undefined;\n\n /**\n * Sentry's structured logging API, available in Sentry SDK 9.41.0+.\n */\n logger?: Partial<\n Record<\n LogSeverityLevel,\n (\n message: ParameterizedString,\n attributes: Record<string, unknown>,\n ) => void\n >\n >;\n}\n\n/**\n * Options for configuring the Sentry sink.\n * @since 1.3.0\n */\nexport interface SentrySinkOptions {\n /**\n * Sentry SDK namespace to use for capture, scope, span, and structured log\n * APIs.\n *\n * This is useful when your application initializes Sentry through a framework\n * SDK such as `@sentry/nextjs` or `@sentry/react-native`, and\n * *@logtape/sentry* resolves a different `@sentry/core` module instance.\n *\n * @example\n * ```typescript\n * import * as Sentry from \"@sentry/node\";\n *\n * getSentrySink({ sentry: Sentry });\n * ```\n *\n * @default `@sentry/core`\n * @since 2.2.0\n */\n sentry?: SentryNamespace;\n\n /**\n * Enable automatic breadcrumb creation for log events.\n *\n * When enabled, all logs become breadcrumbs in Sentry's isolation scope,\n * providing a complete context trail when errors occur. Breadcrumbs are\n * lightweight and only appear in error reports for debugging.\n *\n * @default false\n * @since 1.3.0\n */\n enableBreadcrumbs?: boolean;\n\n /**\n * Property names to inspect for an `Error` instance when deciding whether\n * error-level records should be sent through Sentry's `captureException()`.\n *\n * Names are checked in order, and the first property containing an `Error`\n * instance is used as the captured exception. Set this to a custom list when\n * your application or logger stores the primary exception under another name.\n *\n * @default `[\"error\", \"err\"]`\n * @since 2.3.0\n */\n errorPropertyNames?: readonly string[];\n\n /**\n * Optional hook to transform or filter records before sending to Sentry.\n * Return `null` to drop the record.\n *\n * @since 1.3.0\n */\n beforeSend?: (record: LogRecord) => LogRecord | null;\n}\n\n/**\n * Gets a LogTape sink that sends logs to Sentry.\n *\n * This sink uses Sentry's global capture functions from `@sentry/core` by\n * default, following Sentry v8+ best practices. Simply call `Sentry.init()`\n * before creating the sink, and it will automatically use your initialized\n * client when both packages resolve the same Sentry module instance.\n *\n * @param optionsOrClient Optional configuration. Can be:\n * - Omitted: Uses global Sentry functions (recommended)\n * - Object with options: Configure sink behavior\n * - Object with `sentry`: Use an application-provided Sentry SDK namespace\n * - Sentry client instance: Backward compatibility (deprecated)\n * @returns A LogTape sink that sends logs to Sentry.\n *\n * @example Recommended usage - no parameters\n * ```typescript\n * import { configure } from \"@logtape/logtape\";\n * import { getSentrySink } from \"@logtape/sentry\";\n * import * as Sentry from \"@sentry/node\";\n *\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink(), // That's it!\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"error\" },\n * ],\n * });\n * ```\n *\n * @example With an application-provided Sentry namespace\n * ```typescript\n * import { configure } from \"@logtape/logtape\";\n * import { getSentrySink } from \"@logtape/sentry\";\n * import * as Sentry from \"@sentry/nextjs\";\n *\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink({ sentry: Sentry }),\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"error\" },\n * ],\n * });\n * ```\n *\n * @example With options\n * ```typescript\n * import * as Sentry from \"@sentry/node\";\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink({\n * enableBreadcrumbs: true,\n * }),\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"info\" },\n * ],\n * });\n * ```\n *\n * @example Edge functions - must flush before termination\n * ```typescript\n * // Cloudflare Workers\n * export default {\n * async fetch(request, env, ctx) {\n * logger.error(\"Something happened\");\n * ctx.waitUntil(Sentry.flush(2000)); // Don't block response\n * return new Response(\"OK\");\n * }\n * };\n * ```\n *\n * @example Legacy usage (v1.1.x - deprecated)\n * ```typescript\n * import { getClient } from \"@sentry/node\";\n * const client = getClient();\n * getSentrySink(client); // Still works but shows deprecation warning\n * ```\n *\n * @since 1.0.0\n */\nexport function getSentrySink(\n optionsOrClient?: SentrySinkOptions | SentryInstance,\n): Sink {\n let legacyClient: SentryInstance | undefined;\n let options: SentrySinkOptions = {};\n\n // Detect which API pattern is being used\n if (optionsOrClient == null) {\n // Pattern: getSentrySink() - no params (RECOMMENDED)\n // Use global functions\n } else if (\n typeof optionsOrClient === \"object\" &&\n \"captureMessage\" in optionsOrClient &&\n typeof optionsOrClient.captureMessage === \"function\"\n ) {\n // Pattern: getSentrySink(client) - DEPRECATED (v1.1.x backward compatibility)\n getLogger([\"logtape\", \"meta\", \"sentry\"]).warn(\n \"Passing a client directly is deprecated. \" +\n \"Use getSentrySink({ sentry: Sentry }) instead.\",\n );\n legacyClient = optionsOrClient as SentryInstance;\n } else if (typeof optionsOrClient === \"object\") {\n // Pattern: getSentrySink({ options }) - options object\n options = optionsOrClient as SentrySinkOptions;\n } else {\n throw new Error(\n `[@logtape/sentry] Invalid parameter (type: ${typeof optionsOrClient}).\\n\\n` +\n \"Expected one of:\\n\" +\n \" getSentrySink() // Recommended\\n\" +\n \" getSentrySink({ options }) // With options\\n\" +\n \" getSentrySink({ sentry }) // With a Sentry SDK namespace\\n\" +\n \" getSentrySink(client) // Deprecated (v1.1.x compat)\\n\",\n );\n }\n\n const sentry = options.sentry ?? SentryCore;\n\n // Choose which Sentry functions to use:\n // - For capture functions: use client if provided (v1.1.x compat),\n // otherwise the configured SDK namespace.\n // - For scope operations: use the configured SDK namespace because clients\n // don't expose current scope/span APIs.\n const captureMessage = legacyClient\n ? (msg: ParameterizedString, ctx?: unknown) =>\n legacyClient.captureMessage(String(msg), ctx)\n : sentry.captureMessage;\n const captureException = legacyClient\n ? (exception: unknown, hint?: unknown) =>\n legacyClient.captureException(exception, hint)\n : sentry.captureException;\n\n return (record: LogRecord) => {\n try {\n // Skip meta logger records to prevent infinite recursion\n const { category } = record;\n if (\n category[0] === \"logtape\" && category[1] === \"meta\" &&\n category[2] === \"sentry\"\n ) {\n return;\n }\n\n // Optional transformation/filtering\n const transformed = options.beforeSend\n ? options.beforeSend(record)\n : record;\n if (transformed == null) return;\n\n // Parameterized message for structured logging and events\n const paramMessage = getParameterizedString(transformed);\n const message = paramMessage.toString();\n\n // Level mapping\n const eventLevel = mapLevelForEvents(transformed.level);\n\n // Enriched structured attributes\n const attributes = {\n ...transformed.properties,\n \"sentry.origin\": \"auto.logging.logtape\",\n category: transformed.category.join(\".\"),\n timestamp: transformed.timestamp,\n } as Record<string, unknown>;\n\n // After enriched attributes\n const activeSpan = sentry.getActiveSpan();\n if (activeSpan) {\n const spanCtx = activeSpan.spanContext();\n attributes.trace_id = spanCtx.traceId;\n attributes.span_id = spanCtx.spanId;\n if (\"parentSpanId\" in spanCtx) {\n attributes.parent_span_id = spanCtx.parentSpanId; // Optional\n }\n }\n\n // Send structured log if Sentry logging is enabled (v9.41.0+)\n // Uses public logger API when available (SDK 9.41.0+)\n const client = sentry.getClient();\n if (client) {\n const { enableLogs, _experiments } = client.getOptions();\n const loggingEnabled = enableLogs ?? _experiments?.enableLogs;\n\n const sentryLogger = sentry.logger as SentryNamespace[\"logger\"];\n if (loggingEnabled && sentryLogger != null) {\n const logLevel = mapLevelForLogs(transformed.level);\n const logFn = sentryLogger[logLevel];\n if (typeof logFn === \"function\") {\n logFn(paramMessage, attributes);\n }\n }\n }\n\n // Capture as Sentry event (Issue) based on level and error presence\n // Use compareLogLevel() to handle future severity level additions\n const isErrorLevel = compareLogLevel(transformed.level, \"error\") >= 0;\n const errorProperty = getErrorProperty(\n transformed.properties,\n options.errorPropertyNames ?? defaultErrorPropertyNames,\n );\n\n if (isErrorLevel && errorProperty != null) {\n // Error instance at error/fatal level -> captureException for stack trace\n const [property, error] = errorProperty;\n const rest = { ...attributes };\n delete rest[property];\n captureException(error, {\n level: eventLevel,\n extra: { message, ...rest },\n });\n } else if (isErrorLevel) {\n // Error/fatal level without Error instance -> captureMessage as Issue\n captureMessage(paramMessage, {\n level: eventLevel,\n extra: attributes,\n });\n } else if (options.enableBreadcrumbs) {\n // Non-error levels -> breadcrumbs only (if enabled)\n const isolationScope = sentry.getIsolationScope();\n isolationScope?.addBreadcrumb({\n category: transformed.category.join(\".\"),\n level: eventLevel,\n message,\n timestamp: transformed.timestamp / 1000,\n data: attributes,\n });\n }\n } catch (error) {\n getLogger([\"logtape\", \"meta\", \"sentry\"]).error(\n \"Failed to send log events to Sentry\",\n { error },\n );\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mod.js","names":["record: LogRecord","tplValues: string[]","level: LogLevel","properties: Readonly<Record<string, unknown>>","propertyNames: readonly string[]","optionsOrClient?: SentrySinkOptions | SentryInstance","legacyClient: SentryInstance | undefined","options: SentrySinkOptions","msg: ParameterizedString","ctx?: unknown","exception: unknown","hint?: unknown","options: SentryLogsOptions | undefined"],"sources":["../src/mod.ts"],"sourcesContent":["import {\n compareLogLevel,\n getLogger,\n type LogLevel,\n type LogRecord,\n type Sink,\n} from \"@logtape/logtape\";\nimport type {\n LogSeverityLevel,\n ParameterizedString,\n SeverityLevel,\n} from \"@sentry/core\";\n// Import namespace to safely check for public logger API (added in v9.41.0)\nimport * as SentryCore from \"@sentry/core\";\n// Cross-runtime inspect: Deno.inspect / util.inspect (handles circular\n// references); falls back to JSON.stringify in browsers. Resolved via the\n// `#util` import map per runtime.\nimport { inspect } from \"#util\";\n\n/**\n * Converts a LogTape {@link LogRecord} into a Sentry {@link ParameterizedString}.\n *\n * This preserves the template structure for better message grouping in Sentry,\n * allowing similar messages with different values to be grouped together.\n *\n * @param record The log record to convert.\n * @returns A parameterized string with template and values.\n */\nfunction getParameterizedString(record: LogRecord): ParameterizedString {\n let result = \"\";\n let tplString = \"\";\n const tplValues: string[] = [];\n for (let i = 0; i < record.message.length; i++) {\n if (i % 2 === 0) {\n result += record.message[i];\n tplString += String(record.message[i]).replaceAll(\"%\", \"%%\");\n } else {\n const value = inspect(record.message[i]);\n result += value;\n tplString += `%s`;\n tplValues.push(value);\n }\n }\n const paramStr = new String(result) as ParameterizedString;\n paramStr.__sentry_template_string__ = tplString;\n paramStr.__sentry_template_values__ = tplValues;\n return paramStr;\n}\n\n// Level normalization helpers\n\nfunction mapLevelForEvents(level: LogLevel): SeverityLevel {\n switch (level) {\n case \"trace\":\n return \"debug\";\n default:\n return level as SeverityLevel; // debug | info | error | fatal\n }\n}\n\nfunction mapLevelForLogs(level: LogLevel): LogSeverityLevel {\n switch (level) {\n case \"trace\":\n return \"debug\";\n case \"warning\":\n return \"warn\";\n case \"debug\":\n case \"info\":\n case \"error\":\n case \"fatal\":\n return level;\n default:\n return \"info\"; // fallback\n }\n}\n\nconst defaultErrorPropertyNames = [\"error\", \"err\"] as const;\n\n/**\n * Options for records sent through Sentry's Logs API.\n *\n * @since 2.3.0\n */\nexport interface SentryLogsOptions {\n /**\n * Minimum level for records sent through Sentry's Logs API.\n *\n * @default `\"trace\"`\n */\n level?: LogLevel;\n}\n\n/**\n * Options for records added as Sentry breadcrumbs.\n *\n * @since 2.3.0\n */\nexport interface SentryBreadcrumbOptions {\n /**\n * Minimum level for records added as breadcrumbs.\n *\n * @default `\"trace\"`\n */\n level?: LogLevel;\n\n /**\n * Maximum level for records added as breadcrumbs.\n */\n maxLevel?: LogLevel;\n}\n\nfunction getErrorProperty(\n properties: Readonly<Record<string, unknown>>,\n propertyNames: readonly string[],\n): readonly [property: string, error: Error] | undefined {\n for (const property of propertyNames) {\n if (properties[property] instanceof Error) {\n return [property, properties[property]];\n }\n }\n return undefined;\n}\n\n/**\n * A Sentry client instance type (used for v1.1.x backward compatibility).\n *\n * Client instances only support `captureMessage` and `captureException`.\n * For scope operations (breadcrumbs, user context, traces), the sink always\n * uses global functions from `@sentry/core`.\n *\n * @deprecated This is only used for backward compatibility with v1.1.x.\n * New code should use `getSentrySink()` without parameters, which automatically\n * uses Sentry's global functions.\n *\n * @since 1.3.0\n */\nexport interface SentryInstance {\n captureMessage: (\n message: string,\n captureContext?: SeverityLevel | unknown,\n ) => string;\n captureException: (exception: unknown, hint?: unknown) => string;\n}\n\n/**\n * A Sentry SDK namespace object.\n *\n * Pass the namespace imported by your application when *@logtape/sentry* should\n * use the same Sentry module instance that initialized your app, for example\n * `import * as Sentry from \"@sentry/node\"`.\n *\n * @since 2.2.0\n */\nexport interface SentryNamespace {\n /**\n * Captures a message event and sends it to Sentry.\n */\n captureMessage(\n message: ParameterizedString,\n captureContext?: SeverityLevel | unknown,\n ): string;\n\n /**\n * Captures an exception event and sends it to Sentry.\n */\n captureException(exception: unknown, hint?: unknown): string;\n\n /**\n * Gets the currently active span, if any.\n */\n getActiveSpan():\n | {\n spanContext: () => {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n };\n }\n | undefined;\n\n /**\n * Gets the currently active Sentry client, if any.\n */\n getClient():\n | {\n getOptions: () => {\n enableLogs?: boolean;\n _experiments?: {\n enableLogs?: boolean;\n };\n };\n }\n | undefined;\n\n /**\n * Gets the current isolation scope.\n */\n getIsolationScope():\n | {\n addBreadcrumb: (breadcrumb: {\n category: string;\n level: SeverityLevel;\n message: string;\n timestamp: number;\n data: Record<string, unknown>;\n }) => void;\n }\n | undefined;\n\n /**\n * Sentry's structured logging API, available in Sentry SDK 9.41.0+.\n */\n logger?: Partial<\n Record<\n LogSeverityLevel,\n (\n message: ParameterizedString,\n attributes: Record<string, unknown>,\n ) => void\n >\n >;\n}\n\n/**\n * Options for configuring the Sentry sink.\n * @since 1.3.0\n */\nexport interface SentrySinkOptions {\n /**\n * Sentry SDK namespace to use for capture, scope, span, and structured log\n * APIs.\n *\n * This is useful when your application initializes Sentry through a framework\n * SDK such as `@sentry/nextjs` or `@sentry/react-native`, and\n * *@logtape/sentry* resolves a different `@sentry/core` module instance.\n *\n * @example\n * ```typescript\n * import * as Sentry from \"@sentry/node\";\n *\n * getSentrySink({ sentry: Sentry });\n * ```\n *\n * @default `@sentry/core`\n * @since 2.2.0\n */\n sentry?: SentryNamespace;\n\n /**\n * Enable automatic breadcrumb creation for log events.\n *\n * When enabled, non-error logs become breadcrumbs in Sentry's isolation\n * scope, providing a complete context trail when errors occur. Breadcrumbs\n * are lightweight and only appear in error reports for debugging.\n *\n * @default false\n * @deprecated Use `breadcrumbs` instead.\n * @since 1.3.0\n */\n enableBreadcrumbs?: boolean;\n\n /**\n * Enables and configures automatic breadcrumb creation for log events.\n *\n * Set this to `true` to use the default breadcrumb behavior, or pass an\n * options object to control which non-error log levels become breadcrumbs.\n *\n * When this option is set, it takes precedence over the deprecated\n * `enableBreadcrumbs` option.\n *\n * @default false\n * @since 2.3.0\n */\n breadcrumbs?: boolean | SentryBreadcrumbOptions;\n\n /**\n * Configures records sent through Sentry's Logs API.\n *\n * The Sentry SDK must still have structured logging enabled with\n * `enableLogs: true` or `_experiments.enableLogs: true`.\n *\n * @since 2.3.0\n */\n logs?: SentryLogsOptions;\n\n /**\n * Property names to inspect for an `Error` instance when deciding whether\n * error-level records should be sent through Sentry's `captureException()`.\n *\n * Names are checked in order, and the first property containing an `Error`\n * instance is used as the captured exception. Set this to a custom list when\n * your application or logger stores the primary exception under another name.\n *\n * @default `[\"error\", \"err\"]`\n * @since 2.3.0\n */\n errorPropertyNames?: readonly string[];\n\n /**\n * Optional hook to transform or filter records before sending to Sentry.\n * Return `null` to drop the record.\n *\n * @since 1.3.0\n */\n beforeSend?: (record: LogRecord) => LogRecord | null;\n}\n\n/**\n * Gets a LogTape sink that sends logs to Sentry.\n *\n * This sink uses Sentry's global capture functions from `@sentry/core` by\n * default, following Sentry v8+ best practices. Simply call `Sentry.init()`\n * before creating the sink, and it will automatically use your initialized\n * client when both packages resolve the same Sentry module instance.\n *\n * @param optionsOrClient Optional configuration. Can be:\n * - Omitted: Uses global Sentry functions (recommended)\n * - Object with options: Configure sink behavior\n * - Object with `sentry`: Use an application-provided Sentry SDK namespace\n * - Sentry client instance: Backward compatibility (deprecated)\n * @returns A LogTape sink that sends logs to Sentry.\n *\n * @example Recommended usage - no parameters\n * ```typescript\n * import { configure } from \"@logtape/logtape\";\n * import { getSentrySink } from \"@logtape/sentry\";\n * import * as Sentry from \"@sentry/node\";\n *\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink(), // That's it!\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"error\" },\n * ],\n * });\n * ```\n *\n * @example With an application-provided Sentry namespace\n * ```typescript\n * import { configure } from \"@logtape/logtape\";\n * import { getSentrySink } from \"@logtape/sentry\";\n * import * as Sentry from \"@sentry/nextjs\";\n *\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink({ sentry: Sentry }),\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"error\" },\n * ],\n * });\n * ```\n *\n * @example With options\n * ```typescript\n * import * as Sentry from \"@sentry/node\";\n * Sentry.init({ dsn: process.env.SENTRY_DSN });\n *\n * await configure({\n * sinks: {\n * sentry: getSentrySink({\n * breadcrumbs: true,\n * }),\n * },\n * loggers: [\n * { category: [], sinks: [\"sentry\"], lowestLevel: \"info\" },\n * ],\n * });\n * ```\n *\n * @example Edge functions - must flush before termination\n * ```typescript\n * // Cloudflare Workers\n * export default {\n * async fetch(request, env, ctx) {\n * logger.error(\"Something happened\");\n * ctx.waitUntil(Sentry.flush(2000)); // Don't block response\n * return new Response(\"OK\");\n * }\n * };\n * ```\n *\n * @example Legacy usage (v1.1.x - deprecated)\n * ```typescript\n * import { getClient } from \"@sentry/node\";\n * const client = getClient();\n * getSentrySink(client); // Still works but shows deprecation warning\n * ```\n *\n * @since 1.0.0\n */\nexport function getSentrySink(\n optionsOrClient?: SentrySinkOptions | SentryInstance,\n): Sink {\n let legacyClient: SentryInstance | undefined;\n let options: SentrySinkOptions = {};\n\n // Detect which API pattern is being used\n if (optionsOrClient == null) {\n // Pattern: getSentrySink() - no params (RECOMMENDED)\n // Use global functions\n } else if (\n typeof optionsOrClient === \"object\" &&\n \"captureMessage\" in optionsOrClient &&\n typeof optionsOrClient.captureMessage === \"function\"\n ) {\n // Pattern: getSentrySink(client) - DEPRECATED (v1.1.x backward compatibility)\n getLogger([\"logtape\", \"meta\", \"sentry\"]).warn(\n \"Passing a client directly is deprecated. \" +\n \"Use getSentrySink({ sentry: Sentry }) instead.\",\n );\n legacyClient = optionsOrClient as SentryInstance;\n } else if (typeof optionsOrClient === \"object\") {\n // Pattern: getSentrySink({ options }) - options object\n options = optionsOrClient as SentrySinkOptions;\n } else {\n throw new Error(\n `[@logtape/sentry] Invalid parameter (type: ${typeof optionsOrClient}).\\n\\n` +\n \"Expected one of:\\n\" +\n \" getSentrySink() // Recommended\\n\" +\n \" getSentrySink({ options }) // With options\\n\" +\n \" getSentrySink({ sentry }) // With a Sentry SDK namespace\\n\" +\n \" getSentrySink(client) // Deprecated (v1.1.x compat)\\n\",\n );\n }\n\n const sentry = options.sentry ?? SentryCore;\n\n // Choose which Sentry functions to use:\n // - For capture functions: use client if provided (v1.1.x compat),\n // otherwise the configured SDK namespace.\n // - For scope operations: use the configured SDK namespace because clients\n // don't expose current scope/span APIs.\n const captureMessage = legacyClient\n ? (msg: ParameterizedString, ctx?: unknown) =>\n legacyClient.captureMessage(String(msg), ctx)\n : sentry.captureMessage;\n const captureException = legacyClient\n ? (exception: unknown, hint?: unknown) =>\n legacyClient.captureException(exception, hint)\n : sentry.captureException;\n\n return (record: LogRecord) => {\n try {\n // Skip meta logger records to prevent infinite recursion\n const { category } = record;\n if (\n category[0] === \"logtape\" && category[1] === \"meta\" &&\n category[2] === \"sentry\"\n ) {\n return;\n }\n\n // Optional transformation/filtering\n const transformed = options.beforeSend\n ? options.beforeSend(record)\n : record;\n if (transformed == null) return;\n\n // Parameterized message for structured logging and events\n const paramMessage = getParameterizedString(transformed);\n const message = paramMessage.toString();\n\n // Level mapping\n const eventLevel = mapLevelForEvents(transformed.level);\n\n // Enriched structured attributes\n const attributes = {\n ...transformed.properties,\n \"sentry.origin\": \"auto.logging.logtape\",\n category: transformed.category.join(\".\"),\n timestamp: transformed.timestamp,\n } as Record<string, unknown>;\n\n // After enriched attributes\n const activeSpan = sentry.getActiveSpan();\n if (activeSpan) {\n const spanCtx = activeSpan.spanContext();\n attributes.trace_id = spanCtx.traceId;\n attributes.span_id = spanCtx.spanId;\n if (\"parentSpanId\" in spanCtx) {\n attributes.parent_span_id = spanCtx.parentSpanId; // Optional\n }\n }\n\n // Send structured log if Sentry logging is enabled (v9.41.0+)\n // Uses public logger API when available (SDK 9.41.0+)\n const client = sentry.getClient();\n if (client && shouldSendToLogs(transformed, options.logs)) {\n const { enableLogs, _experiments } = client.getOptions();\n const loggingEnabled = enableLogs ?? _experiments?.enableLogs;\n\n const sentryLogger = sentry.logger as SentryNamespace[\"logger\"];\n if (loggingEnabled && sentryLogger != null) {\n const logLevel = mapLevelForLogs(transformed.level);\n const logFn = sentryLogger[logLevel];\n if (typeof logFn === \"function\") {\n logFn(paramMessage, attributes);\n }\n }\n }\n\n // Capture as Sentry event (Issue) based on level and error presence\n // Use compareLogLevel() to handle future severity level additions\n const isErrorLevel = compareLogLevel(transformed.level, \"error\") >= 0;\n const errorProperty = getErrorProperty(\n transformed.properties,\n options.errorPropertyNames ?? defaultErrorPropertyNames,\n );\n\n if (isErrorLevel && errorProperty != null) {\n // Error instance at error/fatal level -> captureException for stack trace\n const [property, error] = errorProperty;\n const rest = { ...attributes };\n delete rest[property];\n captureException(error, {\n level: eventLevel,\n extra: { message, ...rest },\n });\n } else if (isErrorLevel) {\n // Error/fatal level without Error instance -> captureMessage as Issue\n captureMessage(paramMessage, {\n level: eventLevel,\n extra: attributes,\n });\n } else if (shouldAddBreadcrumb(transformed, options)) {\n // Non-error levels -> breadcrumbs only (if enabled)\n const isolationScope = sentry.getIsolationScope();\n isolationScope?.addBreadcrumb({\n category: transformed.category.join(\".\"),\n level: eventLevel,\n message,\n timestamp: transformed.timestamp / 1000,\n data: attributes,\n });\n }\n } catch (error) {\n getLogger([\"logtape\", \"meta\", \"sentry\"]).error(\n \"Failed to send log events to Sentry\",\n { error },\n );\n }\n };\n}\n\nfunction shouldSendToLogs(\n record: LogRecord,\n options: SentryLogsOptions | undefined,\n): boolean {\n return options?.level == null ||\n compareLogLevel(record.level, options.level) >= 0;\n}\n\nfunction shouldAddBreadcrumb(\n record: LogRecord,\n options: SentrySinkOptions,\n): boolean {\n const breadcrumbOptions = options.breadcrumbs;\n if (breadcrumbOptions === false) return false;\n if (breadcrumbOptions === true) return true;\n if (breadcrumbOptions == null) return options.enableBreadcrumbs === true;\n\n if (\n breadcrumbOptions.level != null &&\n compareLogLevel(record.level, breadcrumbOptions.level) < 0\n ) {\n return false;\n }\n\n return breadcrumbOptions.maxLevel == null ||\n compareLogLevel(record.level, breadcrumbOptions.maxLevel) <= 0;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logtape/sentry",
|
|
3
|
-
"version": "2.3.0-dev.
|
|
3
|
+
"version": "2.3.0-dev.846+d8ef44ba",
|
|
4
4
|
"description": "LogTape Sentry sink",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"LogTape",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
],
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@sentry/core": ">=8.0.0",
|
|
72
|
-
"@logtape/logtape": "^2.3.0-dev.
|
|
72
|
+
"@logtape/logtape": "^2.3.0-dev.846+d8ef44ba"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@sentry/core": "^9.41.0",
|