Skip to content

Commit 3545c68

Browse files
authored
feat(utils): Add parameterize function (#9145)
Add `parameterize` fuction to utils package that takes params from a format string and attaches them as extra properties to a string. These are picked up during event processing and a `LogEntry` item is added to the event.
1 parent fffa932 commit 3545c68

File tree

20 files changed

+196
-23
lines changed

20 files changed

+196
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { parameterize } from '@sentry/utils';
2+
3+
const x = 'first';
4+
const y = 'second';
5+
6+
Sentry.captureMessage(parameterize`This is a log statement with ${x} and ${y} params`);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';
6+
7+
sentryTest('should capture a parameterized representation of the message', async ({ getLocalTestPath, page }) => {
8+
const bundle = process.env.PW_BUNDLE;
9+
10+
if (bundle && bundle.startsWith('bundle_')) {
11+
sentryTest.skip();
12+
}
13+
14+
const url = await getLocalTestPath({ testDir: __dirname });
15+
16+
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
17+
18+
expect(eventData.logentry).toStrictEqual({
19+
message: 'This is a log statement with %s and %s params',
20+
params: ['first', 'second'],
21+
});
22+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as Sentry from '@sentry/node';
2+
import { parameterize } from '@sentry/utils';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
});
8+
9+
const x = 'first';
10+
const y = 'second';
11+
12+
Sentry.captureMessage(parameterize`This is a log statement with ${x} and ${y} params`);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { TestEnv, assertSentryEvent } from '../../../../utils';
2+
3+
test('should capture a parameterized representation of the message', async () => {
4+
const env = await TestEnv.init(__dirname);
5+
const event = await env.getEnvelopeRequest();
6+
7+
assertSentryEvent(event[2], {
8+
logentry: {
9+
message: 'This is a log statement with %s and %s params',
10+
params: ['first', 'second'],
11+
},
12+
});
13+
});

packages/browser/src/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
Event,
88
EventHint,
99
Options,
10+
ParameterizedString,
1011
Severity,
1112
SeverityLevel,
1213
UserFeedback,
@@ -84,7 +85,7 @@ export class BrowserClient extends BaseClient<BrowserClientOptions> {
8485
* @inheritDoc
8586
*/
8687
public eventFromMessage(
87-
message: string,
88+
message: ParameterizedString,
8889
// eslint-disable-next-line deprecation/deprecation
8990
level: Severity | SeverityLevel = 'info',
9091
hint?: EventHint,

packages/browser/src/eventbuilder.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { getClient } from '@sentry/core';
2-
import type { Event, EventHint, Exception, Severity, SeverityLevel, StackFrame, StackParser } from '@sentry/types';
2+
import type {
3+
Event,
4+
EventHint,
5+
Exception,
6+
ParameterizedString,
7+
Severity,
8+
SeverityLevel,
9+
StackFrame,
10+
StackParser,
11+
} from '@sentry/types';
312
import {
413
addExceptionMechanism,
514
addExceptionTypeValue,
@@ -9,6 +18,7 @@ import {
918
isError,
1019
isErrorEvent,
1120
isEvent,
21+
isParameterizedString,
1222
isPlainObject,
1323
normalizeToSize,
1424
resolvedSyncPromise,
@@ -167,7 +177,7 @@ export function eventFromException(
167177
*/
168178
export function eventFromMessage(
169179
stackParser: StackParser,
170-
message: string,
180+
message: ParameterizedString,
171181
// eslint-disable-next-line deprecation/deprecation
172182
level: Severity | SeverityLevel = 'info',
173183
hint?: EventHint,
@@ -264,23 +274,32 @@ export function eventFromUnknownInput(
264274
*/
265275
export function eventFromString(
266276
stackParser: StackParser,
267-
input: string,
277+
message: ParameterizedString,
268278
syntheticException?: Error,
269279
attachStacktrace?: boolean,
270280
): Event {
271-
const event: Event = {
272-
message: input,
273-
};
281+
const event: Event = {};
274282

275283
if (attachStacktrace && syntheticException) {
276284
const frames = parseStackFrames(stackParser, syntheticException);
277285
if (frames.length) {
278286
event.exception = {
279-
values: [{ value: input, stacktrace: { frames } }],
287+
values: [{ value: message, stacktrace: { frames } }],
280288
};
281289
}
282290
}
283291

292+
if (isParameterizedString(message)) {
293+
const { __sentry_template_string__, __sentry_template_values__ } = message;
294+
295+
event.logentry = {
296+
message: __sentry_template_string__,
297+
params: __sentry_template_values__,
298+
};
299+
return event;
300+
}
301+
302+
event.message = message;
284303
return event;
285304
}
286305

packages/core/src/baseclient.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import type {
1919
MetricBucketItem,
2020
MetricsAggregator,
2121
Outcome,
22+
ParameterizedString,
2223
PropagationContext,
2324
SdkMetadata,
2425
Session,
@@ -36,6 +37,7 @@ import {
3637
addItemToEnvelope,
3738
checkOrSetAlreadyCaught,
3839
createAttachmentEnvelopeItem,
40+
isParameterizedString,
3941
isPlainObject,
4042
isPrimitive,
4143
isThenable,
@@ -182,16 +184,18 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
182184
* @inheritDoc
183185
*/
184186
public captureMessage(
185-
message: string,
187+
message: ParameterizedString,
186188
// eslint-disable-next-line deprecation/deprecation
187189
level?: Severity | SeverityLevel,
188190
hint?: EventHint,
189191
scope?: Scope,
190192
): string | undefined {
191193
let eventId: string | undefined = hint && hint.event_id;
192194

195+
const eventMessage = isParameterizedString(message) ? message : String(message);
196+
193197
const promisedEvent = isPrimitive(message)
194-
? this.eventFromMessage(String(message), level, hint)
198+
? this.eventFromMessage(eventMessage, level, hint)
195199
: this.eventFromException(message, hint);
196200

197201
this._process(
@@ -816,7 +820,7 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
816820
* @inheritDoc
817821
*/
818822
public abstract eventFromMessage(
819-
_message: string,
823+
_message: ParameterizedString,
820824
// eslint-disable-next-line deprecation/deprecation
821825
_level?: Severity | SeverityLevel,
822826
_hint?: EventHint,

packages/core/src/server-runtime-client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
Event,
77
EventHint,
88
MonitorConfig,
9+
ParameterizedString,
910
SerializedCheckIn,
1011
Severity,
1112
SeverityLevel,
@@ -63,7 +64,7 @@ export class ServerRuntimeClient<
6364
* @inheritDoc
6465
*/
6566
public eventFromMessage(
66-
message: string,
67+
message: ParameterizedString,
6768
// eslint-disable-next-line deprecation/deprecation
6869
level: Severity | SeverityLevel = 'info',
6970
hint?: EventHint,

packages/core/test/mocks/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
EventHint,
66
Integration,
77
Outcome,
8+
ParameterizedString,
89
Session,
910
Severity,
1011
SeverityLevel,
@@ -76,7 +77,7 @@ export class TestClient extends BaseClient<TestClientOptions> {
7677
}
7778

7879
public eventFromMessage(
79-
message: string,
80+
message: ParameterizedString,
8081
// eslint-disable-next-line deprecation/deprecation
8182
level: Severity | SeverityLevel = 'info',
8283
): PromiseLike<Event> {

packages/replay/test/utils/TestClient.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { BaseClient, createTransport, initAndBind } from '@sentry/core';
2-
import type { BrowserClientReplayOptions, ClientOptions, Event, SeverityLevel } from '@sentry/types';
2+
import type {
3+
BrowserClientReplayOptions,
4+
ClientOptions,
5+
Event,
6+
ParameterizedString,
7+
SeverityLevel,
8+
} from '@sentry/types';
39
import { resolvedSyncPromise } from '@sentry/utils';
410

511
export interface TestClientOptions extends ClientOptions, BrowserClientReplayOptions {}
@@ -24,7 +30,7 @@ export class TestClient extends BaseClient<TestClientOptions> {
2430
});
2531
}
2632

27-
public eventFromMessage(message: string, level: SeverityLevel = 'info'): PromiseLike<Event> {
33+
public eventFromMessage(message: ParameterizedString, level: SeverityLevel = 'info'): PromiseLike<Event> {
2834
return resolvedSyncPromise({ message, level });
2935
}
3036
}

packages/tracing-internal/test/utils/TestClient.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { BaseClient, createTransport, initAndBind } from '@sentry/core';
2-
import type { BrowserClientReplayOptions, ClientOptions, Event, SeverityLevel } from '@sentry/types';
2+
import type {
3+
BrowserClientReplayOptions,
4+
ClientOptions,
5+
Event,
6+
ParameterizedString,
7+
SeverityLevel,
8+
} from '@sentry/types';
39
import { resolvedSyncPromise } from '@sentry/utils';
410

511
export interface TestClientOptions extends ClientOptions, BrowserClientReplayOptions {}
@@ -24,7 +30,7 @@ export class TestClient extends BaseClient<TestClientOptions> {
2430
});
2531
}
2632

27-
public eventFromMessage(message: string, level: SeverityLevel = 'info'): PromiseLike<Event> {
33+
public eventFromMessage(message: ParameterizedString, level: SeverityLevel = 'info'): PromiseLike<Event> {
2834
return resolvedSyncPromise({ message, level });
2935
}
3036
}

packages/types/src/client.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { FeedbackEvent } from './feedback';
1010
import type { Integration, IntegrationClass } from './integration';
1111
import type { MetricBucketItem } from './metrics';
1212
import type { ClientOptions } from './options';
13+
import type { ParameterizedString } from './parameterize';
1314
import type { Scope } from './scope';
1415
import type { SdkMetadata } from './sdkmetadata';
1516
import type { Session, SessionAggregates } from './session';
@@ -159,7 +160,7 @@ export interface Client<O extends ClientOptions = ClientOptions> {
159160

160161
/** Creates an {@link Event} from primitive inputs to `captureMessage`. */
161162
eventFromMessage(
162-
message: string,
163+
message: ParameterizedString,
163164
// eslint-disable-next-line deprecation/deprecation
164165
level?: Severity | SeverityLevel,
165166
hint?: EventHint,

packages/types/src/event.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import type { User } from './user';
2020
export interface Event {
2121
event_id?: string;
2222
message?: string;
23+
logentry?: {
24+
message?: string;
25+
params?: string[];
26+
};
2327
timestamp?: number;
2428
start_timestamp?: number;
2529
// eslint-disable-next-line deprecation/deprecation

packages/types/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,4 @@ export type {
140140
export type { BrowserClientReplayOptions, BrowserClientProfilingOptions } from './browseroptions';
141141
export type { CheckIn, MonitorConfig, FinishedCheckIn, InProgressCheckIn, SerializedCheckIn } from './checkin';
142142
export type { MetricsAggregator, MetricBucketItem, MetricInstance } from './metrics';
143+
export type { ParameterizedString } from './parameterize';

packages/types/src/parameterize.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type ParameterizedString = string & {
2+
__sentry_template_string__?: string;
3+
__sentry_template_values__?: string[];
4+
};

packages/utils/src/eventbuilder.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import type {
66
Extras,
77
Hub,
88
Mechanism,
9+
ParameterizedString,
910
Severity,
1011
SeverityLevel,
1112
StackFrame,
1213
StackParser,
1314
} from '@sentry/types';
1415

15-
import { isError, isPlainObject } from './is';
16+
import { isError, isParameterizedString, isPlainObject } from './is';
1617
import { addExceptionMechanism, addExceptionTypeValue } from './misc';
1718
import { normalizeToSize } from './normalize';
1819
import { extractExceptionKeysForMessage } from './object';
@@ -127,7 +128,7 @@ export function eventFromUnknownInput(
127128
*/
128129
export function eventFromMessage(
129130
stackParser: StackParser,
130-
message: string,
131+
message: ParameterizedString,
131132
// eslint-disable-next-line deprecation/deprecation
132133
level: Severity | SeverityLevel = 'info',
133134
hint?: EventHint,
@@ -136,7 +137,6 @@ export function eventFromMessage(
136137
const event: Event = {
137138
event_id: hint && hint.event_id,
138139
level,
139-
message,
140140
};
141141

142142
if (attachStacktrace && hint && hint.syntheticException) {
@@ -153,5 +153,16 @@ export function eventFromMessage(
153153
}
154154
}
155155

156+
if (isParameterizedString(message)) {
157+
const { __sentry_template_string__, __sentry_template_values__ } = message;
158+
159+
event.logentry = {
160+
message: __sentry_template_string__,
161+
params: __sentry_template_values__,
162+
};
163+
return event;
164+
}
165+
166+
event.message = message;
156167
return event;
157168
}

packages/utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export * from './url';
3232
export * from './userIntegrations';
3333
export * from './cache';
3434
export * from './eventbuilder';
35+
export * from './parameterize';
3536
export * from './anr';
3637
export * from './lru';
3738
export * from './buildPolyfills';

0 commit comments

Comments
 (0)