Skip to content

Commit c39f95a

Browse files
eskirkcodecapitano
andauthored
feat: add custom error serializer to console instrumentation (#902)
Co-authored-by: marco.schaefer <[email protected]>
1 parent faee062 commit c39f95a

File tree

5 files changed

+51
-2
lines changed

5 files changed

+51
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Next
44

5+
- Feature (`@grafana/faro-web-sdk`): Add `errorSerializer` to the `consoleInstrumentation` property of the core
6+
configuration object to provide custom ways to serialize data captured by the `ConsoleInstrumentation` (#902)
57
- Feature (`@grafana/faro-web-sdk`): Provide APIs to send `service.name` override instructions to the
68
receiver (#893)
79
- Improvement (`@grafana/faro-web-sdk`): Send an event for `service.name` overrides (#903)

packages/core/src/config/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ export interface Config<P = APIEvent> {
179179
* By default, Faro sends an error for console.error calls. If you want to send a log instead, set this to true.
180180
*/
181181
consoleErrorAsLog?: boolean;
182+
183+
/**
184+
* Custom function to serialize the contents of error messages
185+
*/
186+
errorSerializer?: LogArgsSerializer;
182187
};
183188
}
184189

packages/web-sdk/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,4 @@ export {
168168
} from './instrumentations/session';
169169

170170
export { getIgnoreUrls } from './utils/url';
171+
export { stringifyExternalJson } from './utils/json';

packages/web-sdk/src/instrumentations/console/instrumentation.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { ExceptionEvent, LogEvent } from '@grafana/faro-core';
33
import { mockConfig, MockTransport } from '@grafana/faro-core/src/testUtils';
44

55
import { makeCoreConfig } from '../../config';
6+
import { stringifyExternalJson } from '../../utils';
67

78
import { ConsoleInstrumentation } from './instrumentation';
89

@@ -81,6 +82,37 @@ describe('ConsoleInstrumentation', () => {
8182
);
8283
});
8384

85+
it('Handles objects with circular references with custom serializer', () => {
86+
const mockTransport = new MockTransport();
87+
88+
initializeFaro(
89+
makeCoreConfig(
90+
mockConfig({
91+
transports: [mockTransport],
92+
instrumentations: [
93+
new ConsoleInstrumentation({
94+
errorSerializer: (args: any[]) => {
95+
return args.map((arg) => (typeof arg === 'string' ? arg : stringifyExternalJson(arg))).join(' ');
96+
},
97+
}),
98+
],
99+
unpatchedConsole: {
100+
error: jest.fn(),
101+
} as unknown as Console,
102+
})
103+
)!
104+
);
105+
106+
const objWithCircularRef = { foo: 'bar', baz: 'bam' };
107+
(objWithCircularRef as any).circular = objWithCircularRef;
108+
109+
console.error('with circular refs object', objWithCircularRef);
110+
111+
expect((mockTransport.items[0] as TransportItem<ExceptionEvent>)?.payload.value).toBe(
112+
`console.error: with circular refs object {\"foo\":\"bar\",\"baz\":\"bam\",\"circular\":null}`
113+
);
114+
});
115+
84116
it('sends a faro log for console.error calls if configured', () => {
85117
const mockTransport = new MockTransport();
86118

packages/web-sdk/src/instrumentations/console/instrumentation.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { allLogLevels, BaseInstrumentation, defaultLogArgsSerializer, LogLevel, VERSION } from '@grafana/faro-core';
1+
import {
2+
allLogLevels,
3+
BaseInstrumentation,
4+
defaultLogArgsSerializer,
5+
LogArgsSerializer,
6+
LogLevel,
7+
VERSION,
8+
} from '@grafana/faro-core';
29

310
import type { ConsoleInstrumentationOptions } from './types';
411

@@ -7,6 +14,7 @@ export class ConsoleInstrumentation extends BaseInstrumentation {
714
readonly version = VERSION;
815

916
static defaultDisabledLevels: LogLevel[] = [LogLevel.DEBUG, LogLevel.TRACE, LogLevel.LOG];
17+
private errorSerializer: LogArgsSerializer = defaultLogArgsSerializer;
1018

1119
constructor(private options: ConsoleInstrumentationOptions = {}) {
1220
super();
@@ -15,6 +23,7 @@ export class ConsoleInstrumentation extends BaseInstrumentation {
1523
initialize() {
1624
this.logDebug('Initializing\n', this.options);
1725
this.options = { ...this.options, ...this.config.consoleInstrumentation };
26+
this.errorSerializer = this.options.errorSerializer ?? defaultLogArgsSerializer;
1827

1928
allLogLevels
2029
.filter(
@@ -25,7 +34,7 @@ export class ConsoleInstrumentation extends BaseInstrumentation {
2534
console[level] = (...args) => {
2635
try {
2736
if (level === LogLevel.ERROR && !this.options?.consoleErrorAsLog) {
28-
this.api.pushError(new Error('console.error: ' + defaultLogArgsSerializer(args)));
37+
this.api.pushError(new Error('console.error: ' + this.errorSerializer(args)));
2938
} else {
3039
this.api.pushLog(args, { level });
3140
}

0 commit comments

Comments
 (0)