Skip to content

Commit

Permalink
chore: Sync logging with jest
Browse files Browse the repository at this point in the history
Jest would overwrite pino logs when logging via a worker thread. This
changes logging so that we only use pino-pretty if running in jest, and
we only use it as a stream in the main loop.

See https://github.com/pinojs/pino-pretty?tab=readme-ov-file#usage-with-jest
  • Loading branch information
spalladino committed Dec 6, 2024
1 parent 1b0dfb7 commit bc07ed7
Showing 1 changed file with 36 additions and 30 deletions.
66 changes: 36 additions & 30 deletions yarn-project/foundation/src/log/pino-logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createColors } from 'colorette';
import isNode from 'detect-node';
import { type LoggerOptions, pino } from 'pino';
import { pino } from 'pino';
import pretty from 'pino-pretty';
import { inspect } from 'util';

import { compactArray } from '../collection/array.js';
Expand Down Expand Up @@ -67,24 +68,25 @@ function isLevelEnabled(logger: pino.Logger<'verbose', boolean>, level: LogLevel
const defaultLogLevel = process.env.NODE_ENV === 'test' ? 'silent' : 'info';
const [logLevel, logFilters] = parseEnv(process.env.LOG_LEVEL, defaultLogLevel);

// Transport options for pretty logging to stdout via pino-pretty.
// Transport options for pretty logging to stderr via pino-pretty.
const useColor = true;
const { bold, reset } = createColors({ useColor });
const prettyTransport: LoggerOptions['transport'] = {
const pinoPrettyOpts = {
destination: 2,
sync: true,
colorize: useColor,
ignore: 'module,pid,hostname,trace_id,span_id,trace_flags',
messageFormat: `${bold('{module}')} ${reset('{msg}')}`,
customLevels: 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10',
customColors: 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray',
};
const prettyTransport: pino.TransportSingleOptions = {
target: 'pino-pretty',
options: {
destination: 2,
sync: true,
colorize: useColor,
ignore: 'module,pid,hostname,trace_id,span_id,trace_flags',
messageFormat: `${bold('{module}')} ${reset('{msg}')}`,
customLevels: 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10',
customColors: 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray',
},
options: pinoPrettyOpts,
};

// Transport for vanilla stdio logging as JSON.
const stdioTransport: LoggerOptions['transport'] = {
const stdioTransport: pino.TransportSingleOptions = {
target: 'pino/file',
options: { destination: 2 },
};
Expand All @@ -103,27 +105,31 @@ const levels = {
// would mean that all child loggers created before the telemetry-client is initialized would not have
// this transport configured. Note that the target is defined as the export in the telemetry-client,
// since pino will load this transport separately on a worker thread, to minimize disruption to the main loop.

const otelTransport: LoggerOptions['transport'] = {
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
const otelTransport: pino.TransportSingleOptions = {
target: '@aztec/telemetry-client/otel-pino-stream',
options: { levels, messageKey: 'msg' },
};

// In nodejs, create a new pino instance with an stdout transport (either vanilla or json), and optionally
// an OTLP transport if the OTLP endpoint is provided. Note that transports are initialized in a worker thread.
// On the browser, we just log to the console.
const otlpEndpoint = process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT;
const logger = isNode
? pino(
pinoOpts,
pino.transport({
targets: compactArray([
['1', 'true', 'TRUE'].includes(process.env.LOG_JSON ?? '') ? stdioTransport : prettyTransport,
process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ? otelTransport : undefined,
]),
}),
)
: pino({ ...pinoOpts, browser: { asObject: false } });
function makeLogger() {
if (!isNode) {
// We are on the browser
return pino({ ...pinoOpts, browser: { asObject: false } });
} else if (process.env.JEST_WORKER_ID) {
// We are on jest, we need sync logging
return pino(pinoOpts, pretty({ ...pinoPrettyOpts }));
} else {
// Regular nodejs with transports on worker thread, using pino-pretty for console logging if LOG_JSON
// is not set, and an optional OTLP transport if the OTLP endpoint is provided.
const targets: pino.TransportSingleOptions[] = compactArray([
['1', 'true', 'TRUE'].includes(process.env.LOG_JSON ?? '') ? stdioTransport : prettyTransport,
otlpEndpoint ? otelTransport : undefined,
]);
return pino(pinoOpts, pino.transport({ targets }));
}
}

const logger = makeLogger();

// Log the logger configuration.
logger.verbose(
Expand Down

0 comments on commit bc07ed7

Please sign in to comment.