Skip to content

Commit 9a12bd9

Browse files
committed
fix(sdk-logs,sdk-trace-base,sdk-metrics): #5800
ConsoleSpanExporter, ConsoleMetricExporter, and ConsoleLogRecordExporter use console.log instead of console.dir
1 parent a05e581 commit 9a12bd9

File tree

6 files changed

+139
-37
lines changed

6 files changed

+139
-37
lines changed

experimental/packages/sdk-logs/src/export/ConsoleLogRecordExporter.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ import {
2323
import type { ReadableLogRecord } from './ReadableLogRecord';
2424
import type { LogRecordExporter } from './LogRecordExporter';
2525

26+
function log(payload: unknown): void {
27+
let serialized: string;
28+
try {
29+
serialized = JSON.stringify(
30+
payload,
31+
(_key, value) => (value === undefined ? null : value),
32+
2
33+
);
34+
} catch {
35+
serialized = String(payload);
36+
}
37+
38+
/* eslint-disable-next-line no-console */
39+
console.log(serialized);
40+
}
41+
2642
/**
2743
* This is implementation of {@link LogRecordExporter} that prints LogRecords to the
2844
* console. This class can be used for diagnostic purposes.
@@ -82,7 +98,7 @@ export class ConsoleLogRecordExporter implements LogRecordExporter {
8298
done?: (result: ExportResult) => void
8399
): void {
84100
for (const logRecord of logRecords) {
85-
console.dir(this._exportInfo(logRecord), { depth: 3 });
101+
log(this._exportInfo(logRecord));
86102
}
87103
done?.({ code: ExportResultCode.SUCCESS });
88104
}

experimental/packages/sdk-logs/test/common/export/ConsoleLogRecordExporter.test.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,8 @@ import {
2626

2727
/* eslint-disable no-console */
2828
describe('ConsoleLogRecordExporter', () => {
29-
let previousConsoleDir: typeof console.dir;
30-
31-
beforeEach(() => {
32-
previousConsoleDir = console.dir;
33-
console.dir = () => {};
34-
});
35-
3629
afterEach(() => {
37-
console.dir = previousConsoleDir;
30+
sinon.restore();
3831
});
3932

4033
describe('export', () => {
@@ -43,7 +36,7 @@ describe('ConsoleLogRecordExporter', () => {
4336
const instrumentationScopeName = '@opentelemetry/sdk-logs/test';
4437
const instrumentationScopeVersion = '1.2.3';
4538
const consoleExporter = new ConsoleLogRecordExporter();
46-
const spyConsole = sinon.spy(console, 'dir');
39+
const stubConsole = sinon.stub(console, 'log');
4740
const spyExport = sinon.spy(consoleExporter, 'export');
4841
const provider = new LoggerProvider({
4942
processors: [new SimpleLogRecordProcessor(consoleExporter)],
@@ -59,8 +52,8 @@ describe('ConsoleLogRecordExporter', () => {
5952

6053
const logRecords = spyExport.args[0];
6154
const firstLogRecord = logRecords[0][0];
62-
const consoleArgs = spyConsole.args[0];
63-
const consoleLogRecord = consoleArgs[0];
55+
const loggedPayload = stubConsole.args[0][0];
56+
const consoleLogRecord = JSON.parse(loggedPayload);
6457
const keys = Object.keys(consoleLogRecord).sort().join(',');
6558

6659
const expectedKeys = [
@@ -89,7 +82,30 @@ describe('ConsoleLogRecordExporter', () => {
8982
);
9083

9184
assert.ok(spyExport.calledOnce);
85+
assert.ok(stubConsole.calledOnce);
9286
});
9387
});
88+
89+
it('should serialize log record using String() when JSON fails', () => {
90+
const consoleExporter = new ConsoleLogRecordExporter();
91+
const stubConsole = sinon.stub(console, 'log');
92+
const originalStringify = JSON.stringify;
93+
let shouldThrow = true;
94+
sinon.stub(JSON, 'stringify').callsFake((...args) => {
95+
if (shouldThrow) {
96+
shouldThrow = false;
97+
throw new Error('boom');
98+
}
99+
return originalStringify(...args);
100+
});
101+
const provider = new LoggerProvider({
102+
processors: [new SimpleLogRecordProcessor(consoleExporter)],
103+
});
104+
105+
provider.getLogger('test').emit({ body: 'trigger fallback' });
106+
107+
assert.ok(stubConsole.calledOnce);
108+
assert.strictEqual(stubConsole.args[0][0], '[object Object]');
109+
});
94110
});
95111
});

packages/opentelemetry-sdk-trace-base/src/export/ConsoleSpanExporter.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,22 @@ import {
2222
hrTimeToMicroseconds,
2323
} from '@opentelemetry/core';
2424

25+
function log(payload: unknown): void {
26+
let serialized: string;
27+
try {
28+
serialized = JSON.stringify(
29+
payload,
30+
(_key, value) => (value === undefined ? null : value),
31+
2
32+
);
33+
} catch {
34+
serialized = String(payload);
35+
}
36+
37+
/* eslint-disable-next-line no-console */
38+
console.log(serialized);
39+
}
40+
2541
/**
2642
* This is implementation of {@link SpanExporter} that prints spans to the
2743
* console. This class can be used for diagnostic purposes.
@@ -93,7 +109,7 @@ export class ConsoleSpanExporter implements SpanExporter {
93109
done?: (result: ExportResult) => void
94110
): void {
95111
for (const span of spans) {
96-
console.dir(this._exportInfo(span), { depth: 3 });
112+
log(this._exportInfo(span));
97113
}
98114
if (done) {
99115
return done({ code: ExportResultCode.SUCCESS });

packages/opentelemetry-sdk-trace-base/test/common/export/ConsoleSpanExporter.test.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,13 @@ import {
2828
/* eslint-disable no-console */
2929
describe('ConsoleSpanExporter', () => {
3030
let consoleExporter: ConsoleSpanExporter;
31-
let previousConsoleDir: any;
3231

3332
beforeEach(() => {
34-
previousConsoleDir = console.dir;
35-
console.dir = () => {};
3633
consoleExporter = new ConsoleSpanExporter();
3734
});
3835

3936
afterEach(() => {
40-
console.dir = previousConsoleDir;
37+
sinon.restore();
4138
});
4239

4340
describe('.export()', () => {
@@ -49,7 +46,7 @@ describe('ConsoleSpanExporter', () => {
4946
spanProcessors: [new SimpleSpanProcessor(consoleExporter)],
5047
});
5148

52-
const spyConsole = sinon.spy(console, 'dir');
49+
const stubConsole = sinon.stub(console, 'log');
5350
const spyExport = sinon.spy(consoleExporter, 'export');
5451

5552
const instrumentationScopeName = '@opentelemetry/sdk-trace-base/test';
@@ -73,8 +70,8 @@ describe('ConsoleSpanExporter', () => {
7370
const spans = spyExport.args[0];
7471
const firstSpan = spans[0][0];
7572
const firstEvent = firstSpan.events[0];
76-
const consoleArgs = spyConsole.args[0];
77-
const consoleSpan = consoleArgs[0];
73+
const loggedPayload = stubConsole.args[0][0];
74+
const consoleSpan = JSON.parse(loggedPayload);
7875
const keys = Object.keys(consoleSpan).sort().join(',');
7976

8077
const expectedKeys = [
@@ -106,8 +103,34 @@ describe('ConsoleSpanExporter', () => {
106103
);
107104

108105
assert.ok(spyExport.calledOnce);
106+
assert.ok(stubConsole.calledOnce);
109107
});
110108
});
109+
110+
it('should serialize span information using String() when JSON fails', () => {
111+
const basicTracerProvider = new BasicTracerProvider({
112+
sampler: new AlwaysOnSampler(),
113+
spanProcessors: [new SimpleSpanProcessor(consoleExporter)],
114+
});
115+
116+
const stubConsole = sinon.stub(console, 'log');
117+
const originalStringify = JSON.stringify;
118+
let shouldThrow = true;
119+
sinon.stub(JSON, 'stringify').callsFake((...args) => {
120+
if (shouldThrow) {
121+
shouldThrow = false;
122+
throw new Error('boom');
123+
}
124+
return originalStringify(...args);
125+
});
126+
127+
const tracer = basicTracerProvider.getTracer('fallback-string');
128+
const span = tracer.startSpan('fallback-span');
129+
span.end();
130+
131+
assert.ok(stubConsole.calledOnce);
132+
assert.strictEqual(stubConsole.args[0][0], '[object Object]');
133+
});
111134
});
112135

113136
describe('force flush', () => {

packages/sdk-metrics/src/export/ConsoleMetricExporter.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ interface ConsoleMetricExporterOptions {
2626
temporalitySelector?: AggregationTemporalitySelector;
2727
}
2828

29+
function log(payload: unknown): void {
30+
let serialized: string;
31+
try {
32+
serialized = JSON.stringify(
33+
payload,
34+
(_key, value) => (value === undefined ? null : value),
35+
2
36+
);
37+
} catch {
38+
serialized = String(payload);
39+
}
40+
41+
/* eslint-disable-next-line no-console */
42+
console.log(serialized);
43+
}
44+
2945
/**
3046
* This is an implementation of {@link PushMetricExporter} that prints metrics to the
3147
* console. This class can be used for diagnostic purposes.
@@ -77,14 +93,11 @@ export class ConsoleMetricExporter implements PushMetricExporter {
7793
): void {
7894
for (const scopeMetrics of metrics.scopeMetrics) {
7995
for (const metric of scopeMetrics.metrics) {
80-
console.dir(
81-
{
82-
descriptor: metric.descriptor,
83-
dataPointType: metric.dataPointType,
84-
dataPoints: metric.dataPoints,
85-
},
86-
{ depth: null }
87-
);
96+
log({
97+
descriptor: metric.descriptor,
98+
dataPointType: metric.dataPointType,
99+
dataPoints: metric.dataPoints,
100+
});
88101
}
89102
}
90103

packages/sdk-metrics/test/export/ConsoleMetricExporter.test.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,12 @@ async function waitForNumberOfExports(
4747
/* eslint-disable no-console */
4848
describe('ConsoleMetricExporter', () => {
4949
describe('export', () => {
50-
let previousConsoleDir: any;
5150
let exporter: ConsoleMetricExporter;
5251
let meterProvider: MeterProvider;
5352
let metricReader: PeriodicExportingMetricReader;
5453
let meter: metrics.Meter;
5554

5655
beforeEach(() => {
57-
previousConsoleDir = console.dir;
58-
console.dir = () => {};
59-
6056
exporter = new ConsoleMetricExporter();
6157
metricReader = new PeriodicExportingMetricReader({
6258
exporter: exporter,
@@ -71,9 +67,8 @@ describe('ConsoleMetricExporter', () => {
7167
});
7268

7369
afterEach(async () => {
74-
console.dir = previousConsoleDir;
75-
7670
await metricReader.shutdown();
71+
sinon.restore();
7772
});
7873

7974
it('should export information about metric', async () => {
@@ -91,14 +86,14 @@ describe('ConsoleMetricExporter', () => {
9186
histogram.record(100);
9287
histogram.record(1000);
9388

94-
const spyConsole = sinon.spy(console, 'dir');
89+
const stubConsole = sinon.stub(console, 'log');
9590
const spyExport = sinon.spy(exporter, 'export');
9691

9792
await waitForNumberOfExports(spyExport, 1);
9893
const resourceMetrics = spyExport.args[0];
9994
const firstResourceMetric = resourceMetrics[0];
100-
const consoleArgs = spyConsole.args[0];
101-
const consoleMetric = consoleArgs[0];
95+
const loggedPayload = stubConsole.args[0][0];
96+
const consoleMetric = JSON.parse(loggedPayload);
10297
const keys = Object.keys(consoleMetric).sort().join(',');
10398

10499
const expectedKeys = ['dataPointType', 'dataPoints', 'descriptor'].join(
@@ -124,6 +119,29 @@ describe('ConsoleMetricExporter', () => {
124119
);
125120

126121
assert.ok(spyExport.calledOnce);
122+
assert.ok(stubConsole.called);
123+
});
124+
125+
it('should serialize metric information using String() when JSON fails', async () => {
126+
const stubConsole = sinon.stub(console, 'log');
127+
const originalStringify = JSON.stringify;
128+
let shouldThrow = true;
129+
sinon.stub(JSON, 'stringify').callsFake((...args) => {
130+
if (shouldThrow) {
131+
shouldThrow = false;
132+
throw new Error('boom');
133+
}
134+
return originalStringify(...args);
135+
});
136+
137+
const counter = meter.createCounter('fallback_counter');
138+
counter.add(1);
139+
140+
const spyExport = sinon.spy(exporter, 'export');
141+
await waitForNumberOfExports(spyExport, 1);
142+
143+
assert.ok(stubConsole.calledOnce);
144+
assert.strictEqual(stubConsole.args[0][0], '[object Object]');
127145
});
128146
});
129147

0 commit comments

Comments
 (0)