1
1
import { createColors } from 'colorette' ;
2
2
import isNode from 'detect-node' ;
3
- import { type LoggerOptions , pino } from 'pino' ;
3
+ import { pino , symbols } from 'pino' ;
4
+ import pretty from 'pino-pretty' ;
5
+ import { type Writable } from 'stream' ;
4
6
import { inspect } from 'util' ;
5
7
6
8
import { compactArray } from '../collection/array.js' ;
@@ -67,32 +69,35 @@ function isLevelEnabled(logger: pino.Logger<'verbose', boolean>, level: LogLevel
67
69
const defaultLogLevel = process . env . NODE_ENV === 'test' ? 'silent' : 'info' ;
68
70
const [ logLevel , logFilters ] = parseEnv ( process . env . LOG_LEVEL , defaultLogLevel ) ;
69
71
70
- // Transport options for pretty logging to stdout via pino-pretty.
72
+ // Transport options for pretty logging to stderr via pino-pretty.
71
73
const useColor = true ;
72
74
const { bold, reset } = createColors ( { useColor } ) ;
73
- const prettyTransport : LoggerOptions [ 'transport' ] = {
75
+ const pinoPrettyOpts = {
76
+ destination : 2 ,
77
+ sync : true ,
78
+ colorize : useColor ,
79
+ ignore : 'module,pid,hostname,trace_id,span_id,trace_flags' ,
80
+ messageFormat : `${ bold ( '{module}' ) } ${ reset ( '{msg}' ) } ` ,
81
+ customLevels : 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10' ,
82
+ customColors : 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray' ,
83
+ minimumLevel : 'trace' as const ,
84
+ } ;
85
+ const prettyTransport : pino . TransportSingleOptions = {
74
86
target : 'pino-pretty' ,
75
- options : {
76
- destination : 2 ,
77
- sync : true ,
78
- colorize : useColor ,
79
- ignore : 'module,pid,hostname,trace_id,span_id,trace_flags' ,
80
- messageFormat : `${ bold ( '{module}' ) } ${ reset ( '{msg}' ) } ` ,
81
- customLevels : 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10' ,
82
- customColors : 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray' ,
83
- } ,
87
+ options : pinoPrettyOpts ,
84
88
} ;
85
89
86
90
// Transport for vanilla stdio logging as JSON.
87
- const stdioTransport : LoggerOptions [ 'transport' ] = {
91
+ const stdioTransport : pino . TransportSingleOptions = {
88
92
target : 'pino/file' ,
89
93
options : { destination : 2 } ,
90
94
} ;
91
95
92
96
// Define custom logging levels for pino.
93
97
const customLevels = { verbose : 25 } ;
94
98
const pinoOpts = { customLevels, useOnlyCustomLevels : false , level : logLevel } ;
95
- const levels = {
99
+
100
+ export const levels = {
96
101
labels : { ...pino . levels . labels , ...Object . fromEntries ( Object . entries ( customLevels ) . map ( e => e . reverse ( ) ) ) } ,
97
102
values : { ...pino . levels . values , ...customLevels } ,
98
103
} ;
@@ -103,27 +108,32 @@ const levels = {
103
108
// would mean that all child loggers created before the telemetry-client is initialized would not have
104
109
// this transport configured. Note that the target is defined as the export in the telemetry-client,
105
110
// since pino will load this transport separately on a worker thread, to minimize disruption to the main loop.
106
-
107
- const otelTransport : LoggerOptions [ 'transport' ] = {
111
+ const otlpEndpoint = process . env . OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ;
112
+ const otelOpts = { levels } ;
113
+ const otelTransport : pino . TransportSingleOptions = {
108
114
target : '@aztec/telemetry-client/otel-pino-stream' ,
109
- options : { levels , messageKey : 'msg' } ,
115
+ options : otelOpts ,
110
116
} ;
111
117
112
- // In nodejs, create a new pino instance with an stdout transport (either vanilla or json), and optionally
113
- // an OTLP transport if the OTLP endpoint is provided. Note that transports are initialized in a worker thread.
114
- // On the browser, we just log to the console.
115
- const otlpEndpoint = process . env . OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ;
116
- const logger = isNode
117
- ? pino (
118
- pinoOpts ,
119
- pino . transport ( {
120
- targets : compactArray ( [
121
- [ '1' , 'true' , 'TRUE' ] . includes ( process . env . LOG_JSON ?? '' ) ? stdioTransport : prettyTransport ,
122
- process . env . OTEL_EXPORTER_OTLP_LOGS_ENDPOINT ? otelTransport : undefined ,
123
- ] ) ,
124
- } ) ,
125
- )
126
- : pino ( { ...pinoOpts , browser : { asObject : false } } ) ;
118
+ function makeLogger ( ) {
119
+ if ( ! isNode ) {
120
+ // We are on the browser
121
+ return pino ( { ...pinoOpts , browser : { asObject : false } } ) ;
122
+ } else if ( process . env . JEST_WORKER_ID ) {
123
+ // We are on jest, so we need sync logging. We stream to stderr with pretty.
124
+ return pino ( pinoOpts , pretty ( pinoPrettyOpts ) ) ;
125
+ } else {
126
+ // Regular nodejs with transports on worker thread, using pino-pretty for console logging if LOG_JSON
127
+ // is not set, and an optional OTLP transport if the OTLP endpoint is provided.
128
+ const targets : pino . TransportSingleOptions [ ] = compactArray ( [
129
+ [ '1' , 'true' , 'TRUE' ] . includes ( process . env . LOG_JSON ?? '' ) ? stdioTransport : prettyTransport ,
130
+ otlpEndpoint ? otelTransport : undefined ,
131
+ ] ) ;
132
+ return pino ( pinoOpts , pino . transport ( { targets } ) ) ;
133
+ }
134
+ }
135
+
136
+ const logger = makeLogger ( ) ;
127
137
128
138
// Log the logger configuration.
129
139
logger . verbose (
@@ -136,6 +146,27 @@ logger.verbose(
136
146
: `Browser console logger initialized with level ${ logLevel } ` ,
137
147
) ;
138
148
149
+ /**
150
+ * Registers an additional destination to the pino logger.
151
+ * Use only when working with destinations, not worker transports.
152
+ */
153
+ export function registerLoggingStream ( stream : Writable ) : void {
154
+ logger . verbose ( { module : 'logger' } , `Registering additional logging stream` ) ;
155
+ const original = ( logger as any ) [ symbols . streamSym ] ;
156
+ const destination = original
157
+ ? pino . multistream (
158
+ [
159
+ // Set streams to lowest logging level, and control actual logging from the parent logger
160
+ // otherwise streams default to info and refuse to log anything below that.
161
+ { level : 'trace' , stream : original } ,
162
+ { level : 'trace' , stream } ,
163
+ ] ,
164
+ { levels : levels . values } ,
165
+ )
166
+ : stream ;
167
+ ( logger as any ) [ symbols . streamSym ] = destination ;
168
+ }
169
+
139
170
/** Log function that accepts an exception object */
140
171
type ErrorLogFn = ( msg : string , err ?: Error | unknown , data ?: LogData ) => void ;
141
172
0 commit comments