Skip to content

Commit 0a034af

Browse files
committed
feat(sdk-metrics,sdk-logs,sdk-trace-base): ConsoleMetricExporter,
ConsoleLogRecordExporter, and ConsoleSpanExporter fallback to console.log when console.dir is unavailable
1 parent 4667145 commit 0a034af

File tree

6 files changed

+153
-10
lines changed

6 files changed

+153
-10
lines changed

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

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

26+
function log(payload: unknown): void {
27+
/* eslint-disable-next-line no-console */
28+
if (typeof console?.dir === 'function') {
29+
/* eslint-disable-next-line no-console */
30+
console.dir(payload, { depth: null });
31+
return;
32+
}
33+
34+
let serialized: string;
35+
try {
36+
serialized = JSON.stringify(payload, null, 2);
37+
} catch {
38+
serialized = String(payload);
39+
}
40+
41+
/* eslint-disable-next-line no-console */
42+
if (typeof console?.log === 'function') {
43+
/* eslint-disable-next-line no-console */
44+
console.log(serialized);
45+
}
46+
}
47+
2648
/**
2749
* This is implementation of {@link LogRecordExporter} that prints LogRecords to the
2850
* console. This class can be used for diagnostic purposes.
@@ -82,7 +104,7 @@ export class ConsoleLogRecordExporter implements LogRecordExporter {
82104
done?: (result: ExportResult) => void
83105
): void {
84106
for (const logRecord of logRecords) {
85-
console.dir(this._exportInfo(logRecord), { depth: null });
107+
log(this._exportInfo(logRecord));
86108
}
87109
done?.({ code: ExportResultCode.SUCCESS });
88110
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,5 +105,27 @@ describe('ConsoleLogRecordExporter', () => {
105105
const options = consoleArgs[1];
106106
assert.strictEqual(options?.depth, null);
107107
});
108+
109+
it('falls back to console.log when console.dir is unavailable', () => {
110+
const previousConsoleDir = console.dir;
111+
console.dir = undefined as unknown as typeof console.dir;
112+
113+
const consoleExporter = new ConsoleLogRecordExporter();
114+
const spyConsoleLog = sinon.spy(console, 'log');
115+
const provider = new LoggerProvider({
116+
processors: [new SimpleLogRecordProcessor(consoleExporter)],
117+
});
118+
119+
provider.getLogger('fallback').emit({ body: 'test fallback' });
120+
121+
assert.ok(spyConsoleLog.called);
122+
const loggedPayload = spyConsoleLog.args[0][0];
123+
assert.ok(loggedPayload.includes('\n "body"'));
124+
const parsedPayload = JSON.parse(loggedPayload);
125+
assert.strictEqual(parsedPayload.body, 'test fallback');
126+
127+
spyConsoleLog.restore();
128+
console.dir = previousConsoleDir;
129+
});
108130
});
109131
});

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

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

25+
function log(payload: unknown): void {
26+
/* eslint-disable-next-line no-console */
27+
if (typeof console?.dir === 'function') {
28+
/* eslint-disable-next-line no-console */
29+
console.dir(payload, { depth: null });
30+
return;
31+
}
32+
33+
let serialized: string;
34+
try {
35+
serialized = JSON.stringify(payload, null, 2);
36+
} catch {
37+
serialized = String(payload);
38+
}
39+
40+
/* eslint-disable-next-line no-console */
41+
if (typeof console?.log === 'function') {
42+
/* eslint-disable-next-line no-console */
43+
console.log(serialized);
44+
}
45+
}
46+
2547
/**
2648
* This is implementation of {@link SpanExporter} that prints spans to the
2749
* console. This class can be used for diagnostic purposes.
@@ -93,7 +115,7 @@ export class ConsoleSpanExporter implements SpanExporter {
93115
done?: (result: ExportResult) => void
94116
): void {
95117
for (const span of spans) {
96-
console.dir(this._exportInfo(span), { depth: null });
118+
log(this._exportInfo(span));
97119
}
98120
if (done) {
99121
return done({ code: ExportResultCode.SUCCESS });

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,30 @@ describe('ConsoleSpanExporter', () => {
126126
assert.strictEqual(options?.depth, null);
127127
});
128128

129+
it('falls back to console.log when console.dir is unavailable', () => {
130+
const previousConsoleDir = console.dir;
131+
console.dir = undefined as unknown as typeof console.dir;
132+
133+
consoleExporter = new ConsoleSpanExporter();
134+
const basicTracerProvider = new BasicTracerProvider({
135+
sampler: new AlwaysOnSampler(),
136+
spanProcessors: [new SimpleSpanProcessor(consoleExporter)],
137+
});
138+
139+
const spyConsoleLog = sinon.spy(console, 'log');
140+
const tracer = basicTracerProvider.getTracer('fallback');
141+
const span = tracer.startSpan('fallback-span');
142+
span.end();
143+
144+
assert.ok(spyConsoleLog.called);
145+
const loggedPayload = spyConsoleLog.args[0][0];
146+
assert.ok(loggedPayload.includes('\n "traceId"'));
147+
const parsedPayload = JSON.parse(loggedPayload);
148+
assert.strictEqual(parsedPayload.name, 'fallback-span');
149+
150+
spyConsoleLog.restore();
151+
console.dir = previousConsoleDir;
152+
});
129153
});
130154

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

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

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

29+
function log(payload: unknown): void {
30+
/* eslint-disable-next-line no-console */
31+
if (typeof console?.dir === 'function') {
32+
/* eslint-disable-next-line no-console */
33+
console.dir(payload, { depth: null });
34+
return;
35+
}
36+
37+
let serialized: string;
38+
try {
39+
serialized = JSON.stringify(payload, null, 2);
40+
} catch {
41+
serialized = String(payload);
42+
}
43+
44+
/* eslint-disable-next-line no-console */
45+
if (typeof console?.log === 'function') {
46+
/* eslint-disable-next-line no-console */
47+
console.log(serialized);
48+
}
49+
}
50+
2951
/**
3052
* This is an implementation of {@link PushMetricExporter} that prints metrics to the
3153
* console. This class can be used for diagnostic purposes.
@@ -77,14 +99,11 @@ export class ConsoleMetricExporter implements PushMetricExporter {
7799
): void {
78100
for (const scopeMetrics of metrics.scopeMetrics) {
79101
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-
);
102+
log({
103+
descriptor: metric.descriptor,
104+
dataPointType: metric.dataPointType,
105+
dataPoints: metric.dataPoints,
106+
});
88107
}
89108
}
90109

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,40 @@ describe('ConsoleMetricExporter', () => {
156156
console.dir = previousConsoleDir;
157157
await metricReader.shutdown();
158158
});
159+
160+
it('falls back to console.log when console.dir is unavailable', async () => {
161+
const previousConsoleDir = console.dir;
162+
console.dir = undefined as unknown as typeof console.dir;
163+
164+
const exporter = new ConsoleMetricExporter();
165+
const metricReader = new PeriodicExportingMetricReader({
166+
exporter,
167+
exportIntervalMillis: 100,
168+
exportTimeoutMillis: 100,
169+
});
170+
const meterProvider = new MeterProvider({
171+
resource: testResource,
172+
readers: [metricReader],
173+
});
174+
const meter = meterProvider.getMeter('fallback', '1.0.0');
175+
const counter = meter.createCounter('counter_total');
176+
counter.add(1);
177+
178+
const spyConsoleLog = sinon.spy(console, 'log');
179+
const spyExport = sinon.spy(exporter, 'export');
180+
181+
await waitForNumberOfExports(spyExport, 1);
182+
183+
assert.ok(spyConsoleLog.called);
184+
const loggedPayload = spyConsoleLog.args[0][0];
185+
assert.ok(loggedPayload.includes('\n "descriptor"'));
186+
const parsedPayload = JSON.parse(loggedPayload);
187+
assert.strictEqual(parsedPayload.descriptor.name, 'counter_total');
188+
189+
spyConsoleLog.restore();
190+
console.dir = previousConsoleDir;
191+
await metricReader.shutdown();
192+
});
159193
});
160194

161195
describe('constructor', () => {

0 commit comments

Comments
 (0)