azify-logger 1.0.44 → 1.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/register.js CHANGED
@@ -1,7 +1,15 @@
1
1
  if (process.env.AZIFY_LOGGER_DISABLE === '1') {
2
2
  module.exports = {}
3
3
  } else {
4
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
5
+ try { process.stderr.write('[azify-logger] REGISTER_START register.js loading\n') } catch (_) {}
6
+ }
4
7
  try {
8
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
9
+ try { process.stderr.write('[azify-logger] REGISTER step1 top try\n') } catch (_) {}
10
+ }
11
+ const ModuleForRequire = require('module')
12
+ const nodeRequireOriginal = ModuleForRequire.prototype.require
5
13
  const bunyan = require('bunyan')
6
14
  const createBunyanStream = require('./streams/bunyan')
7
15
  const { createHttpLoggerTransport } = require('./streams/httpQueue')
@@ -13,6 +21,10 @@ try {
13
21
 
14
22
  const { shouldSample, markSource, HTTP_CLIENT_MODE } = require('./sampling')
15
23
 
24
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
25
+ try { process.stderr.write('[azify-logger] REGISTER step1b after requires\n') } catch (_) {}
26
+ try { process.stderr.write('[azify-logger] REGISTER step2-early (right after step1b)\n') } catch (_) {}
27
+ }
16
28
  const serviceName = process.env.APP_NAME
17
29
  const environment = process.env.NODE_ENV
18
30
  const loggerUrlFromEnv = process.env.AZIFY_LOGGER_URL
@@ -25,11 +37,64 @@ try {
25
37
  }
26
38
 
27
39
  const loggerUrlString = loggerEndpoint.toString()
28
- const transport = createHttpLoggerTransport(loggerUrlString)
40
+ let transport = null
41
+ const MAX_PENDING_OUTBOUND = 2000
42
+
43
+ function sendRequestResponseDirectHttp(payload) {
44
+ try {
45
+ const body = typeof payload === 'string' ? payload : JSON.stringify(payload)
46
+ const isHttps = loggerEndpoint.protocol === 'https:'
47
+ const lib = isHttps ? require('https') : require('http')
48
+ const path = (loggerEndpoint.pathname && loggerEndpoint.pathname !== '/') ? loggerEndpoint.pathname : '/log'
49
+ const port = loggerEndpoint.port ? parseInt(loggerEndpoint.port, 10) : (isHttps ? 443 : 80)
50
+ const opts = {
51
+ hostname: loggerEndpoint.hostname,
52
+ port: Number.isFinite(port) ? port : (isHttps ? 443 : 80),
53
+ path,
54
+ method: 'POST',
55
+ headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body, 'utf8') }
56
+ }
57
+ const debug = process.env.AZIFY_LOGGER_DEBUG === '1'
58
+ const req = lib.request(opts, function (res) {
59
+ res.resume()
60
+ if (res.statusCode < 200 || res.statusCode >= 300) {
61
+ try {
62
+ process.stderr.write(`[azify-logger] REQUEST/RESPONSE log POST failed: server responded ${res.statusCode} (check log server at ${loggerUrlString})\n`)
63
+ } catch (_) {}
64
+ if (debug) try { process.stderr.write(`[azify-logger] direct HTTP log server responded ${res.statusCode}\n`) } catch (_) {}
65
+ }
66
+ })
67
+ req.on('error', function (err) {
68
+ try {
69
+ process.stderr.write(`[azify-logger] REQUEST/RESPONSE log POST failed: ${err && err.message} (is log server running at ${loggerUrlString}?)\n`)
70
+ } catch (_) {}
71
+ if (debug) try { process.stderr.write(`[azify-logger] direct HTTP to log server failed: ${err && err.message}\n`) } catch (_) {}
72
+ })
73
+ req.setTimeout(5000, function () { req.destroy() })
74
+ req.write(body)
75
+ req.end()
76
+ } catch (e) {
77
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write(`[azify-logger] sendRequestResponseDirectHttp error: ${e && e.message}\n`) } catch (_) {}
78
+ }
79
+ }
80
+ const pendingOutboundLogs = []
81
+ const flushTransport = () => transport && typeof transport.flush === 'function' ? transport.flush().catch(() => {}) : Promise.resolve()
82
+ function setTransport(t) {
83
+ transport = t
84
+ if (t && pendingOutboundLogs.length > 0) {
85
+ const toFlush = pendingOutboundLogs.splice(0, pendingOutboundLogs.length)
86
+ toFlush.forEach(function (item) {
87
+ try {
88
+ if (typeof transport.enqueue === 'function') transport.enqueue(item.payload, item.headers || { 'content-type': 'application/json' })
89
+ } catch (_) {}
90
+ })
91
+ }
92
+ if (t) { process.once('beforeExit', flushTransport); process.once('exit', flushTransport) }
93
+ }
29
94
 
30
- const flushTransport = () => transport.flush().catch(() => {})
31
- process.once('beforeExit', flushTransport)
32
- process.once('exit', flushTransport)
95
+ try {
96
+ setTransport(createHttpLoggerTransport(loggerUrlString, {}))
97
+ } catch (_) {}
33
98
 
34
99
  const normalizedLoggerOrigin = `${loggerEndpoint.protocol}//${loggerEndpoint.host}`
35
100
  const normalizedLoggerPath = normalizePath(loggerEndpoint.pathname || '/')
@@ -78,20 +143,38 @@ try {
78
143
  return sanitized
79
144
  }
80
145
 
146
+ const debug = process.env.AZIFY_LOGGER_DEBUG === '1'
147
+ const httpVerbose = process.env.AZIFY_LOGGER_HTTP_VERBOSE === '1'
81
148
  function sendOutboundLog(level, message, meta) {
82
149
  try {
150
+ const msgStr = String(message)
151
+ const isReqRes = msgStr.includes('[REQUEST]') || msgStr.includes('[RESPONSE]')
152
+ if (isReqRes && meta && meta.url && isLoggerApiCall({ url: meta.url })) return
153
+ if (isReqRes && meta && (meta.url === 'unknown' || !String(meta.url || '').trim() || String(meta.url).toLowerCase() === 'unknown')) return
83
154
  const source = meta && meta.__source
155
+ if (debug && source === 'pino-stdout') {
156
+ if (debug) try { process.stderr.write('[azify-logger] SENDOUTBOUND_ENTER level=' + level + ' source=' + source + ' msg=' + msgStr.slice(0, 60) + '\n') } catch (_) {}
157
+ }
84
158
  if (!shouldSample(level, source)) {
159
+ if (debug && (isReqRes || msgStr.includes('failed after'))) {
160
+ if (debug) process.stderr.write('[azify-logger] SENDOUTBOUND_SKIP shouldSample level=' + level + ' source=' + source + '\n')
161
+ }
85
162
  return
86
163
  }
164
+ if (debug && source === 'pino-stdout') {
165
+ if (debug) try { process.stderr.write('[azify-logger] SENDOUTBOUND_ENQUEUE level=' + level + ' msg=' + msgStr.slice(0, 50) + '\n') } catch (_) {}
166
+ }
87
167
 
168
+ const forceHttpClientSource = source === 'http-client' || isReqRes
169
+ const effectiveServiceName = serviceName || process.env.APP_NAME || process.env.SERVICE_NAME || (meta && meta.service && meta.service.name) || 'unknown-app'
88
170
  const payload = {
89
171
  level,
90
172
  message,
91
173
  meta: {
92
174
  ...meta,
175
+ ...(forceHttpClientSource && { source: 'http-client' }),
93
176
  service: {
94
- name: serviceName,
177
+ name: effectiveServiceName,
95
178
  version: (meta && meta.service && meta.service.version) || '1.0.0'
96
179
  },
97
180
  environment,
@@ -99,15 +182,156 @@ try {
99
182
  hostname: os.hostname()
100
183
  }
101
184
  }
185
+ const headers = { 'content-type': 'application/json' }
186
+
187
+ if (isReqRes && loggerUrlString) {
188
+ if (debug) try { process.stderr.write(`[AZIFY] REQUEST/RESPONSE -> direct HTTP source=${source || 'n/a'} ${msgStr.slice(0, 72)}\n`) } catch (_) {}
189
+ setImmediate(function () { sendRequestResponseDirectHttp(payload) })
190
+ return
191
+ }
102
192
 
103
193
  if (transport && typeof transport.enqueue === 'function') {
104
- transport.enqueue(payload, {
105
- 'content-type': 'application/json'
106
- })
194
+ transport.enqueue(payload, headers)
195
+ } else {
196
+ if (pendingOutboundLogs.length >= MAX_PENDING_OUTBOUND) pendingOutboundLogs.shift()
197
+ pendingOutboundLogs.push({ payload, headers })
198
+ if (isReqRes && debug) {
199
+ try { process.stderr.write(`[AZIFY] REQUEST/RESPONSE PENDING (no transport yet) source=${source || 'n/a'} ${msgStr.slice(0, 60)}\n`) } catch (_) {}
200
+ }
107
201
  }
108
202
  } catch (err) {
203
+ if (debug) process.stderr.write(`[azify-logger] sendOutboundLog error: ${err && err.message}\n`)
109
204
  }
110
205
  }
206
+ if (typeof global !== 'undefined') global.__AZIFY_SEND_OUTBOUND_LOG = sendOutboundLog
207
+ try {
208
+ const early = require('./register-http-client-early')
209
+ if (typeof early.flushEarlyLogQueue === 'function') early.flushEarlyLogQueue()
210
+ if (typeof early.patchUndiciInCache === 'function') early.patchUndiciInCache()
211
+ } catch (_) {}
212
+
213
+ const MAX_LINE_BUFFER_BYTES = 2 * 1024 * 1024
214
+ function makePinoCaptureHandler(streamName) {
215
+ let inCapture = false
216
+ let lineBuffer = ''
217
+ function trySendLine(line) {
218
+ const trimmed = line.trim()
219
+ if (debug && trimmed.startsWith('{') && (trimmed.includes('"msg":') || trimmed.includes('"message":'))) {
220
+ if (debug) try { process.stderr.write('[azify-logger] TRYSENDLINE_IN stream=' + streamName + ' len=' + trimmed.length + ' preview=' + trimmed.slice(0, 80) + '...\n') } catch (_) {}
221
+ }
222
+ if (!trimmed.startsWith('{') || (!trimmed.includes('"msg":') && !trimmed.includes('"message":'))) {
223
+ if (debug && trimmed.length > 0 && trimmed.length < 200) {
224
+ if (debug) try { process.stderr.write('[azify-logger] TRYSENDLINE_SKIP no json/msg stream=' + streamName + ' preview=' + trimmed.slice(0, 60) + '\n') } catch (_) {}
225
+ }
226
+ return
227
+ }
228
+ try {
229
+ const record = JSON.parse(trimmed)
230
+ const msg = record.msg || record.message || ''
231
+ if (!msg || typeof record.level !== 'number') {
232
+ if (debug) try { process.stderr.write(`[azify-logger] TRYSENDLINE_SKIP no level/msg stream=${streamName}\n`) } catch (_) {}
233
+ return
234
+ }
235
+ inCapture = true
236
+ const levelMap = { 60: 'fatal', 50: 'error', 40: 'warn', 30: 'info', 20: 'debug', 10: 'trace' }
237
+ const level = levelMap[record.level] || 'info'
238
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
239
+ let ctx = getRequestContext()
240
+ if (!ctx || !ctx.traceId) ctx = getLastJobContext()
241
+ let traceId = (ctx && ctx.traceId) ? toTraceIdHex(String(ctx.traceId)) : null
242
+ let spanId = ctx && ctx.spanId != null ? String(ctx.spanId).slice(0, 16) : null
243
+ if (!traceId && typeof msg === 'string' && /traceId/i.test(msg)) {
244
+ const m = msg.match(/"traceId"\s*:\s*"([^"]+)"/)
245
+ if (m) traceId = toTraceIdHex(m[1])
246
+ }
247
+ if (!traceId) {
248
+ try {
249
+ const { startRequestContext } = require('./store')
250
+ const c = startRequestContext({})
251
+ traceId = c.traceId
252
+ spanId = c.spanId
253
+ } catch (_) {
254
+ traceId = require('crypto').randomUUID ? require('crypto').randomUUID().replace(/-/g, '') : String(Date.now())
255
+ }
256
+ }
257
+ const meta = { traceId, spanId, parentSpanId: ctx && ctx.parentSpanId, requestId: ctx && ctx.requestId, __source: 'pino-stdout' }
258
+ if (debug) try { process.stderr.write(`[azify-logger] TRYSENDLINE_SEND level=${level} msg=${String(msg).slice(0, 50)}\n`) } catch (_) {}
259
+ sendOutboundLog(level, msg, meta)
260
+ if (debug) process.stderr.write(`[azify-logger] TRYSENDLINE_DONE ${streamName} sent: ${String(msg).slice(0, 60)}\n`)
261
+ } catch (e) {
262
+ if (debug) try { process.stderr.write(`[azify-logger] TRYSENDLINE_ERR stream=${streamName} ${e && e.message}\n`) } catch (_) {}
263
+ } finally {
264
+ inCapture = false
265
+ }
266
+ }
267
+ return function handleChunk(str) {
268
+ if (typeof str !== 'string' || !str || inCapture) return
269
+ if (debug && str.trim().startsWith('{') && (str.includes('"level":') || str.includes('"msg":'))) {
270
+ if (debug) try { process.stderr.write('[azify-logger] CAPTURE_CHUNK stream=' + streamName + ' len=' + str.length + ' preview=' + str.slice(0, 70).replace(/\n/g, ' ') + '...\n') } catch (_) {}
271
+ }
272
+ lineBuffer += str
273
+ if (lineBuffer.length > MAX_LINE_BUFFER_BYTES) {
274
+ const lastNewline = lineBuffer.lastIndexOf('\n')
275
+ if (lastNewline > 0) {
276
+ const keep = lineBuffer.slice(lastNewline + 1)
277
+ lineBuffer = lineBuffer.slice(0, lastNewline + 1)
278
+ const lines = lineBuffer.split('\n')
279
+ lineBuffer = lines.pop() || ''
280
+ for (let i = 0; i < lines.length; i++) trySendLine(lines[i])
281
+ lineBuffer = keep
282
+ } else {
283
+ lineBuffer = lineBuffer.slice(-1024)
284
+ }
285
+ }
286
+ const lines = lineBuffer.split('\n')
287
+ lineBuffer = lines.pop() || ''
288
+ for (let i = 0; i < lines.length; i++) trySendLine(lines[i])
289
+ if (lineBuffer.trim().startsWith('{') && lineBuffer.trim().endsWith('}')) {
290
+ trySendLine(lineBuffer)
291
+ lineBuffer = ''
292
+ }
293
+ }
294
+ }
295
+ const stdoutCapture = makePinoCaptureHandler('stdout')
296
+ const stderrCapture = makePinoCaptureHandler('stderr')
297
+
298
+ function patchStreamAtWrite(stream, streamName, capture) {
299
+ if (!stream || stream.__azifyLoggerWritePatched) return
300
+ const origWrite = stream.write
301
+ if (typeof origWrite !== 'function') return
302
+ stream.write = function(chunk, encoding, callback) {
303
+ const str = (typeof chunk === 'string' ? chunk : (chunk && chunk.toString ? chunk.toString() : ''))
304
+ capture(str)
305
+ return origWrite.apply(this, arguments)
306
+ }
307
+ stream.__azifyLoggerWritePatched = true
308
+ }
309
+
310
+ function patchStreamAt_Writer(stream, streamName, capture) {
311
+ if (!stream || !stream._write || stream.__azifyLogger_WriterPatched) return
312
+ const orig_Writer = stream._write.bind(stream)
313
+ stream._write = function(chunk, encoding, callback) {
314
+ const str = (typeof chunk === 'string' ? chunk : (chunk && chunk.toString ? chunk.toString() : ''))
315
+ capture(str)
316
+ return orig_Writer(chunk, encoding, callback)
317
+ }
318
+ stream.__azifyLogger_WriterPatched = true
319
+ }
320
+
321
+ try {
322
+ if (!global.__AZIFY_LOGGER_STDOUT_PATCHED) {
323
+ global.__AZIFY_LOGGER_STDOUT_PATCHED = true
324
+ patchStreamAtWrite(process.stdout, 'stdout', stdoutCapture)
325
+ patchStreamAt_Writer(process.stdout, 'stdout', stdoutCapture)
326
+ }
327
+ } catch (_) {}
328
+ try {
329
+ if (!global.__AZIFY_LOGGER_STDERR_PATCHED) {
330
+ global.__AZIFY_LOGGER_STDERR_PATCHED = true
331
+ patchStreamAtWrite(process.stderr, 'stderr', stderrCapture)
332
+ patchStreamAt_Writer(process.stderr, 'stderr', stderrCapture)
333
+ }
334
+ } catch (_) {}
111
335
 
112
336
  function normalizePath(path) {
113
337
  if (!path) {
@@ -162,12 +386,12 @@ try {
162
386
  const otelApi = require('@opentelemetry/api')
163
387
  const activeContext = otelApi.context.active()
164
388
  const span = otelApi.trace.getSpan(activeContext)
165
-
389
+
166
390
  if (!span) return null
167
-
391
+
168
392
  const spanContext = span.spanContext()
169
393
  if (!spanContext || !spanContext.traceId) return null
170
-
394
+
171
395
  return {
172
396
  traceId: spanContext.traceId,
173
397
  spanId: spanContext.spanId,
@@ -178,32 +402,181 @@ try {
178
402
  }
179
403
  }
180
404
 
405
+ const Module = require('module')
406
+ const originalRequireForEarly = Module.prototype.require
407
+ function patchUndiciExportsEarly(exports) {
408
+ if (!exports || typeof exports.request !== 'function' || exports.request.__azifyPatched) return exports
409
+ const { randomBytes } = require('crypto')
410
+ const { performance } = require('perf_hooks')
411
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
412
+ const origRequest = exports.request
413
+ exports.request = function(url, options, callback) {
414
+ const ctx = getRequestContext() || getLastJobContext()
415
+ const otelCtx = getOtelTraceContext()
416
+ const traceCtx = ctx || otelCtx
417
+ const method = (options?.method || 'GET').toUpperCase()
418
+ const urlString = typeof url === 'string' ? url : (url && url.toString && url.toString()) || 'unknown'
419
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
420
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
421
+ const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method, url: urlString }
422
+ markSource(requestMeta, 'http-client')
423
+ const startTime = performance.now()
424
+ if (HTTP_CLIENT_MODE !== 'off') {
425
+ sendOutboundLog('info', `[REQUEST] ${method} ${urlString}`, requestMeta)
426
+ }
427
+ const wrappedCb = callback ? function(err, data) {
428
+ const duration = Number((performance.now() - startTime).toFixed(2))
429
+ if (HTTP_CLIENT_MODE !== 'off') {
430
+ if (err) sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
431
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
432
+ }
433
+ return callback(err, data)
434
+ } : undefined
435
+ if (wrappedCb) return origRequest.call(this, url, options, wrappedCb)
436
+ const promise = origRequest.call(this, url, options)
437
+ if (promise && typeof promise.then === 'function') {
438
+ return promise.then(
439
+ (data) => {
440
+ const duration = Number((performance.now() - startTime).toFixed(2))
441
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
442
+ sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
443
+ }
444
+ return data
445
+ },
446
+ (err) => {
447
+ const duration = Number((performance.now() - startTime).toFixed(2))
448
+ if (HTTP_CLIENT_MODE !== 'off') {
449
+ sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
450
+ }
451
+ throw err
452
+ }
453
+ )
454
+ }
455
+ return promise
456
+ }
457
+ exports.request.__azifyPatched = true
458
+ if (exports.Dispatcher && exports.Dispatcher.prototype && typeof exports.Dispatcher.prototype.request === 'function' && !exports.Dispatcher.prototype.request.__azifyPatched) {
459
+ const proto = exports.Dispatcher.prototype
460
+ const origDispRequest = proto.request
461
+ proto.request = function(opts, callback) {
462
+ const ctx = getRequestContext() || getLastJobContext()
463
+ const otelCtx = getOtelTraceContext()
464
+ const traceCtx = ctx || otelCtx
465
+ const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
466
+ const origin = (opts && opts.origin) || ''
467
+ const path = (opts && opts.path) != null ? opts.path : '/'
468
+ const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
469
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
470
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
471
+ const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method: methodStr, url: urlString }
472
+ markSource(requestMeta, 'http-client')
473
+ const startTime = performance.now()
474
+ if (HTTP_CLIENT_MODE !== 'off') {
475
+ sendOutboundLog('info', `[REQUEST] ${methodStr} ${urlString}`, requestMeta)
476
+ }
477
+ const wrappedCb = typeof callback === 'function' ? function(err, data) {
478
+ const duration = Number((performance.now() - startTime).toFixed(2))
479
+ if (HTTP_CLIENT_MODE !== 'off') {
480
+ if (err) sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
481
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
482
+ }
483
+ return callback(err, data)
484
+ } : undefined
485
+ const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
486
+ if (ret && typeof ret.then === 'function' && !wrappedCb) {
487
+ return ret.then(
488
+ (data) => {
489
+ const duration = Number((performance.now() - startTime).toFixed(2))
490
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
491
+ sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
492
+ }
493
+ return data
494
+ },
495
+ (err) => {
496
+ const duration = Number((performance.now() - startTime).toFixed(2))
497
+ if (HTTP_CLIENT_MODE !== 'off') {
498
+ sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
499
+ }
500
+ throw err
501
+ }
502
+ )
503
+ }
504
+ return ret
505
+ }
506
+ proto.request.__azifyPatched = true
507
+ }
508
+ return exports
509
+ }
510
+ try {
511
+ const origLoad = Module._load
512
+ if (origLoad && origLoad.__azifyEarlyPatched) {
513
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[azify-logger] Module._load keeping early hook (not overwriting)\n') } catch (_) {}
514
+ } else if (typeof origLoad === 'function' && !Module._load.__azifyPatched) {
515
+ Module._load = function (request, parent, isMain) {
516
+ const result = origLoad.apply(this, arguments)
517
+ const pathStr = typeof request === 'string' ? request.replace(/\\/g, '/') : ''
518
+ const isUndici = pathStr === 'undici' || (pathStr.indexOf('undici') !== -1 && pathStr.indexOf('undici-package') === -1)
519
+ if (isUndici) {
520
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write(`[azify-logger] Module._load UNDICI path=${pathStr.slice(-90)}\n`) } catch (_) {}
521
+ try {
522
+ patchUndiciExportsEarly(result)
523
+ } catch (e) {
524
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[azify-logger] Module._load patchUndiciExportsEarly threw: ' + (e && e.message) + '\n') } catch (_) {}
525
+ }
526
+ }
527
+ return result
528
+ }
529
+ Module._load.__azifyPatched = true
530
+ if (debug) try { process.stderr.write('[azify-logger] Module._load hook installed (early)\n') } catch (_) {}
531
+ }
532
+ } catch (_) {}
533
+
534
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
535
+ try { process.stderr.write('[azify-logger] REGISTER step2 before first hook\n') } catch (_) {}
536
+ }
181
537
  try {
182
- const Module = require('module')
183
538
  const originalRequire = Module.prototype.require
184
539
  const { getRequestContext } = require('./store')
185
-
540
+
186
541
  Module.prototype.require = function(id) {
187
542
  const result = originalRequire.call(this, id)
543
+ const idStr = typeof id === 'string' ? id : ''
544
+ const idLooksLikeUndici = idStr === 'undici' || (idStr.length > 0 && idStr.replace(/\\/g, '/').indexOf('undici') !== -1 && idStr.indexOf('undici-package') === -1)
545
+ const looksLikeUndici = result && (typeof result.request === 'function' || typeof result.fetch === 'function') && (result.request && !result.request.__azifyPatched || result.fetch && !result.fetch.__azifyPatched)
546
+ if (process.env.AZIFY_LOGGER_DEBUG === '1' && (idLooksLikeUndici || looksLikeUndici)) {
547
+ try { process.stderr.write('[azify-logger] require undici-like id=' + idStr.slice(-70) + ' hasRequest=' + !!(result && result.request) + ' hasFetch=' + !!(result && result.fetch) + '\n') } catch (_) {}
548
+ }
549
+ if (debug && (idLooksLikeUndici || id === 'nestjs-pino')) {
550
+ try { process.stderr.write('[azify-logger] require hook id=' + id + '\n') } catch (_) {}
551
+ }
552
+ if ((idLooksLikeUndici || looksLikeUndici) && result) {
553
+ if (process.env.AZIFY_LOGGER_HTTP_DEBUG === '1' || process.env.AZIFY_LOGGER_DEBUG === '1') {
554
+ try { process.stderr.write('[AZIFY-HTTP] REGISTER_HOOK1 patch id=' + idStr.slice(-70) + '\n') } catch (_) {}
555
+ }
556
+ try {
557
+ const early = require('./register-http-client-early')
558
+ if (typeof early.patchUndiciExports === 'function') early.patchUndiciExports(result)
559
+ } catch (_) {}
560
+ }
188
561
  if (id === 'pino' || id.endsWith('/pino')) {
189
562
  if (result && typeof result === 'function') {
190
563
  const originalPino = result
191
-
564
+
192
565
  const patchedPino = function(options, stream) {
193
566
  if (new.target) {
194
567
  return originalPino.call(this, options, stream)
195
568
  }
196
-
569
+
197
570
  const logger = originalPino(options, stream)
198
571
  return applyLoggerPatches(logger, options)
199
572
  }
200
-
573
+
201
574
  function applyLoggerPatches(logger, pinoOptions) {
202
575
  if (logger.__azifyPatched) {
203
576
  return logger
204
577
  }
205
578
  logger.__azifyPatched = true
206
-
579
+
207
580
  const originalLog = logger.log
208
581
  const originalInfo = logger.info
209
582
  const originalError = logger.error
@@ -212,13 +585,13 @@ try {
212
585
  const originalFatal = logger.fatal
213
586
  const originalTrace = logger.trace
214
587
  const originalChild = logger.child
215
-
588
+
216
589
  const patchLogMethod = (original, methodName) => {
217
590
  return function(obj, msg, ...args) {
218
591
  const otelCtx = getOtelTraceContext()
219
592
  const ctx = getRequestContext()
220
- const traceCtx = otelCtx || ctx
221
-
593
+ const traceCtx = ctx || otelCtx
594
+
222
595
  if (traceCtx && traceCtx.traceId) {
223
596
  if (typeof obj === 'object' && obj !== null) {
224
597
  obj = {
@@ -229,20 +602,20 @@ try {
229
602
  requestId: traceCtx.requestId || ctx?.requestId
230
603
  }
231
604
  } else {
232
- obj = {
233
- traceId: traceCtx.traceId,
234
- spanId: traceCtx.spanId,
235
- parentSpanId: traceCtx.parentSpanId,
236
- requestId: traceCtx.requestId || ctx?.requestId,
237
- msg: obj
605
+ obj = {
606
+ traceId: traceCtx.traceId,
607
+ spanId: traceCtx.spanId,
608
+ parentSpanId: traceCtx.parentSpanId,
609
+ requestId: traceCtx.requestId || ctx?.requestId,
610
+ msg: obj
238
611
  }
239
612
  }
240
-
613
+
241
614
  try {
242
615
  const levelMap = { log: 'info', info: 'info', error: 'error', warn: 'warn', debug: 'debug', fatal: 'fatal', trace: 'trace' }
243
616
  const level = levelMap[methodName] || 'info'
244
617
  const message = (typeof obj === 'object' ? obj.message : obj) || msg || ''
245
-
618
+
246
619
  const meta = {
247
620
  ...obj,
248
621
  service: {
@@ -253,15 +626,15 @@ try {
253
626
  timestamp: new Date().toISOString(),
254
627
  hostname: require('os').hostname()
255
628
  }
256
-
629
+
257
630
  sendOutboundLog(level, message, meta)
258
631
  } catch (err) {}
259
632
  }
260
-
633
+
261
634
  return original.call(this, obj, msg, ...args)
262
635
  }
263
636
  }
264
-
637
+
265
638
  logger.log = patchLogMethod(originalLog, 'log')
266
639
  logger.info = patchLogMethod(originalInfo, 'info')
267
640
  logger.error = patchLogMethod(originalError, 'error')
@@ -269,97 +642,130 @@ try {
269
642
  logger.debug = patchLogMethod(originalDebug, 'debug')
270
643
  logger.fatal = patchLogMethod(originalFatal, 'fatal')
271
644
  logger.trace = patchLogMethod(originalTrace, 'trace')
272
-
645
+
273
646
  logger.child = function(bindings, options) {
274
647
  const childLogger = originalChild.call(this, bindings, options)
275
648
  return applyLoggerPatches(childLogger, pinoOptions)
276
649
  }
277
-
650
+
278
651
  return logger
279
652
  }
280
-
653
+
281
654
  Object.setPrototypeOf(patchedPino, originalPino)
282
655
  Object.assign(patchedPino, originalPino)
283
-
656
+
284
657
  return patchedPino
285
658
  }
286
659
  }
287
-
660
+
288
661
  return result
289
662
  }
290
-
663
+
291
664
  } catch (_) {}
292
665
 
293
- try {
294
- const { Logger } = require('@nestjs/common')
295
- if (Logger) {
296
- const originalLog = Logger.prototype.log
297
- const originalError = Logger.prototype.error
298
- const originalWarn = Logger.prototype.warn
299
- const originalDebug = Logger.prototype.debug
300
- const originalVerbose = Logger.prototype.verbose
301
-
302
- Logger.prototype.log = function(message, context) {
303
- const { getRequestContext } = require('./store')
304
- const ctx = getRequestContext()
305
- if (ctx && ctx.traceId) {
306
- return originalLog.call(this, message, context)
307
- }
308
- return originalLog.call(this, message, context)
666
+ function patchNestLogger(Logger) {
667
+ if (!Logger || !Logger.prototype || Logger.prototype.__azifyNestLoggerPatched) return
668
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
669
+ Logger.prototype.__azifyNestLoggerPatched = true
670
+ const originalLog = Logger.prototype.log
671
+ const originalError = Logger.prototype.error
672
+ const originalWarn = Logger.prototype.warn
673
+ const originalDebug = Logger.prototype.debug
674
+ const originalVerbose = Logger.prototype.verbose
675
+
676
+ function nestLoggerMeta(message) {
677
+ let ctx = getRequestContext()
678
+ if (!ctx || !ctx.traceId) {
679
+ const jobCtx = getLastJobContext()
680
+ if (jobCtx && jobCtx.traceId) ctx = jobCtx
309
681
  }
310
-
311
- Logger.prototype.error = function(message, trace, context) {
312
- const { getRequestContext } = require('./store')
313
- const ctx = getRequestContext()
314
- if (ctx && ctx.traceId) {
315
- return originalError.call(this, message, trace, context)
316
- }
317
- return originalError.call(this, message, trace, context)
318
- }
319
-
320
- Logger.prototype.warn = function(message, context) {
321
- const { getRequestContext } = require('./store')
322
- const ctx = getRequestContext()
323
- if (ctx && ctx.traceId) {
324
- return originalWarn.call(this, message, context)
325
- }
326
- return originalWarn.call(this, message, context)
327
- }
328
-
329
- Logger.prototype.debug = function(message, context) {
330
- const { getRequestContext } = require('./store')
331
- const ctx = getRequestContext()
332
- if (ctx && ctx.traceId) {
333
- return originalDebug.call(this, message, context)
682
+ if (!ctx || !ctx.traceId) {
683
+ try {
684
+ const { startRequestContext } = require('./store')
685
+ ctx = startRequestContext({})
686
+ } catch (_) {
687
+ ctx = { traceId: require('crypto').randomUUID ? require('crypto').randomUUID().replace(/-/g, '') : String(Date.now()), spanId: null, parentSpanId: null, requestId: null }
334
688
  }
335
- return originalDebug.call(this, message, context)
336
689
  }
337
-
338
- Logger.prototype.verbose = function(message, context) {
339
- const { getRequestContext } = require('./store')
340
- const ctx = getRequestContext()
341
- if (ctx && ctx.traceId) {
342
- return originalVerbose.call(this, message, context)
343
- }
344
- return originalVerbose.call(this, message, context)
690
+ const traceId = ctx.traceId ? toTraceIdHex(String(ctx.traceId)) : null
691
+ const msg = typeof message === 'string' ? message : (message && message.message) || (message && message.msg) || String(message)
692
+ return {
693
+ traceId,
694
+ spanId: ctx.spanId != null ? String(ctx.spanId).slice(0, 16) : null,
695
+ parentSpanId: ctx.parentSpanId ?? null,
696
+ requestId: ctx.requestId ?? null,
697
+ message: msg
345
698
  }
346
-
347
699
  }
700
+
701
+ function skipUndefinedMessage(msg) {
702
+ return msg == null || (typeof msg === 'string' && msg.trim() === '')
703
+ }
704
+
705
+ Logger.prototype.log = function(message, context) {
706
+ if (skipUndefinedMessage(message)) return
707
+ try {
708
+ const meta = nestLoggerMeta(message)
709
+ if (meta.traceId) sendOutboundLog('info', meta.message || message, meta)
710
+ } catch (_) {}
711
+ return originalLog.call(this, message, context)
712
+ }
713
+
714
+ Logger.prototype.error = function(message, trace, context) {
715
+ if (skipUndefinedMessage(message)) return
716
+ try {
717
+ const meta = nestLoggerMeta(message)
718
+ if (meta.traceId) sendOutboundLog('error', meta.message || message, meta)
719
+ } catch (_) {}
720
+ return originalError.call(this, message, trace, context)
721
+ }
722
+
723
+ Logger.prototype.warn = function(message, context) {
724
+ if (skipUndefinedMessage(message)) return
725
+ try {
726
+ const meta = nestLoggerMeta(message)
727
+ if (meta.traceId) sendOutboundLog('warn', meta.message || message, meta)
728
+ } catch (_) {}
729
+ return originalWarn.call(this, message, context)
730
+ }
731
+
732
+ Logger.prototype.debug = function(message, context) {
733
+ if (skipUndefinedMessage(message)) return
734
+ try {
735
+ const meta = nestLoggerMeta(message)
736
+ if (meta.traceId) sendOutboundLog('debug', meta.message || message, meta)
737
+ } catch (_) {}
738
+ return originalDebug.call(this, message, context)
739
+ }
740
+
741
+ Logger.prototype.verbose = function(message, context) {
742
+ if (skipUndefinedMessage(message)) return
743
+ try {
744
+ const meta = nestLoggerMeta(message)
745
+ if (meta.traceId) sendOutboundLog('debug', meta.message || message, meta)
746
+ } catch (_) {}
747
+ return originalVerbose.call(this, message, context)
748
+ }
749
+ }
750
+
751
+ try {
752
+ const { Logger } = require('@nestjs/common')
753
+ patchNestLogger(Logger)
348
754
  } catch (_) {}
349
755
 
350
756
  try {
351
757
  const originalConsoleLog = console.log
352
758
  const originalConsoleError = console.error
353
759
  const originalConsoleWarn = console.warn
354
-
760
+
355
761
  console.log = function(...args) {
356
762
  return originalConsoleLog.apply(this, args)
357
763
  }
358
-
764
+
359
765
  console.error = function(...args) {
360
766
  return originalConsoleError.apply(this, args)
361
767
  }
362
-
768
+
363
769
  console.warn = function(...args) {
364
770
  return originalConsoleWarn.apply(this, args)
365
771
  }
@@ -368,54 +774,142 @@ try {
368
774
  try {
369
775
  const Module = require('module')
370
776
  const originalRequire = Module.prototype.require
371
-
372
- Module.prototype.require = function(id) {
373
- const result = originalRequire.call(this, id)
374
-
375
- if (id === 'nestjs-pino' || id.endsWith('/nestjs-pino') || id.includes('nestjs-pino')) {
376
- if (result && result.Logger && result.Logger.prototype) {
377
- const proto = result.Logger.prototype
378
- const originalCall = proto.call
379
-
380
- proto.call = function(callMethod, message, context) {
381
- const { getRequestContext } = require('./store')
382
- const ctx = getRequestContext()
383
-
384
- if (ctx && ctx.traceId) {
385
- if (typeof message === 'object' && message !== null) {
386
- message = {
387
- ...message,
388
- traceId: ctx.traceId,
389
- spanId: ctx.spanId,
390
- parentSpanId: ctx.parentSpanId,
391
- requestId: ctx.requestId
392
- }
393
- }
394
-
777
+ function isAzifyLoggerInternal(id) {
778
+ if (typeof id !== 'string') return false
779
+ const n = id.replace(/\\/g, '/')
780
+ if (n === './store' || n === 'store' || n.endsWith('/store') || n.endsWith('/store.js')) return true
781
+ if (n === './middleware-express' || n === 'middleware-express' || n.includes('middleware-express')) return true
782
+ if (n === './streams/httpQueue' || n.includes('httpQueue')) return true
783
+ return false
784
+ }
785
+ function callerIsAzifyLogger() {
786
+ try {
787
+ const fn = (this && this.filename) ? this.filename : (this && this.id) ? this.id : ''
788
+ return typeof fn === 'string' && fn.replace(/\\/g, '/').includes('azify-logger')
789
+ } catch (_) { return false }
790
+ }
791
+
792
+ const bypassRequire = (typeof global.__AZIFY_NODE_REQUIRE === 'function') ? global.__AZIFY_NODE_REQUIRE : nodeRequireOriginal
793
+ const patchableIds = ['undici', 'pino', 'bull', 'bullmq', 'nestjs-pino', '@nestjs/common']
794
+ function applyPatchesToModule(result, id) {
795
+ const pathNorm = typeof id === 'string' ? id.replace(/\\/g, '/') : ''
796
+ const is = (name) => id === name || pathNorm.includes(name)
797
+ if (is('@nestjs/common')) {
798
+ try {
799
+ if (result && result.Logger) patchNestLogger(result.Logger)
800
+ } catch (_) {}
801
+ }
802
+ if (is('nestjs-pino')) {
803
+ function patchPinoLoggerClass(LoggerClass) {
804
+ if (!LoggerClass || !LoggerClass.prototype || LoggerClass.prototype.__azifyPinoLoggerPatched) return
805
+ if (debug) process.stderr.write('[azify-logger] patching nestjs-pino Logger\n')
806
+ LoggerClass.prototype.__azifyPinoLoggerPatched = true
807
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
808
+ const proto = LoggerClass.prototype
809
+
810
+ function pinoLoggerCtx(message) {
811
+ let ctx = getRequestContext()
812
+ const jobCtx = getLastJobContext()
813
+ if ((!ctx || !ctx.traceId) && jobCtx && jobCtx.traceId) ctx = jobCtx
814
+ const fromPayload = typeof message === 'object' && message !== null && (message.traceId || (message && message.data && message.data.traceId))
815
+ let traceId = fromPayload ? (message.traceId || (message.data && message.data.traceId)) : (ctx && ctx.traceId)
816
+ let spanId = fromPayload ? (message.spanId || (message.data && message.data.spanId)) : (ctx && ctx.spanId)
817
+ if (!traceId && typeof message === 'string' && /traceId/i.test(message)) {
818
+ const m = message.match(/"traceId"\s*:\s*"([^"]+)"/)
819
+ if (m) { traceId = m[1]; const s = message.match(/"spanId"\s*:\s*"([^"]+)"/); spanId = s ? s[1] : null }
820
+ }
821
+ let effectiveCtx = traceId ? { traceId: toTraceIdHex(String(traceId)), spanId: spanId != null ? String(spanId).slice(0, 16) : null, parentSpanId: (fromPayload && message && (message.parentSpanId != null || (message.data && message.data.parentSpanId != null)) ? (message.parentSpanId || (message.data && message.data.parentSpanId)) : ctx?.parentSpanId) ?? null, requestId: (fromPayload && message && (message.requestId != null || (message.data && message.data.requestId != null)) ? (message.requestId || (message.data && message.data.requestId)) : ctx?.requestId) ?? null } : ctx
822
+ if (!effectiveCtx || !effectiveCtx.traceId) {
395
823
  try {
396
- const levelMap = { log: 'info', info: 'info', error: 'error', warn: 'warn', debug: 'debug', fatal: 'fatal', trace: 'trace', verbose: 'debug' }
397
- const level = levelMap[callMethod] || 'info'
398
- const msg = (typeof message === 'object' ? message.message : message) || ''
399
-
400
- const meta = {
401
- ...(typeof message === 'object' ? message : {}),
402
- traceId: ctx.traceId,
403
- spanId: ctx.spanId,
404
- parentSpanId: ctx.parentSpanId,
405
- requestId: ctx.requestId
406
- }
407
-
408
- sendOutboundLog(level, msg, meta)
409
- } catch (err) {}
824
+ const { startRequestContext } = require('./store')
825
+ effectiveCtx = startRequestContext({})
826
+ } catch (_) {
827
+ effectiveCtx = { traceId: require('crypto').randomUUID ? require('crypto').randomUUID().replace(/-/g, '') : String(Date.now()), spanId: null, parentSpanId: null, requestId: null }
828
+ }
410
829
  }
411
-
412
- return originalCall.call(this, callMethod, message, context)
830
+ const msg = (typeof message === 'object' && message !== null ? message.message : message) || ''
831
+ return { effectiveCtx, msg }
413
832
  }
833
+
834
+ function pinoLoggerSend(level, message, meta) {
835
+ try {
836
+ const msg = (typeof message === 'string' ? message : (meta && meta.msg)) || (typeof message === 'object' && message !== null ? message.message : null) || ''
837
+ if (!msg && level !== 'debug' && level !== 'trace') return
838
+ const payload = { traceId: meta.effectiveCtx.traceId, spanId: meta.effectiveCtx.spanId, parentSpanId: meta.effectiveCtx.parentSpanId, requestId: meta.effectiveCtx.requestId, __source: 'pino-stdout', ...(typeof message === 'object' && message !== null ? message : {}) }
839
+ sendOutboundLog(level, msg, payload)
840
+ if (debug && (String(msg).includes('failed after') || String(msg).includes('[REQUEST]') || String(msg).includes('[RESPONSE]'))) {
841
+ process.stderr.write(`[azify-logger] nestjs-pino sent: ${String(msg).slice(0, 70)}\n`)
842
+ }
843
+ } catch (_) {}
844
+ }
845
+
846
+ const originalCall = proto.call
847
+ proto.call = function(callMethod, message, ...optionalParams) {
848
+ const { effectiveCtx, msg } = pinoLoggerCtx(message)
849
+ if (typeof message === 'object' && message !== null) {
850
+ message = { ...message, traceId: effectiveCtx.traceId, spanId: effectiveCtx.spanId, parentSpanId: effectiveCtx.parentSpanId, requestId: effectiveCtx.requestId }
851
+ }
852
+ pinoLoggerSend({ log: 'info', info: 'info', error: 'error', warn: 'warn', debug: 'debug', fatal: 'fatal', trace: 'trace', verbose: 'debug' }[callMethod] || 'info', message, { effectiveCtx, msg })
853
+ return originalCall.apply(this, [callMethod, message, ...optionalParams])
854
+ }
855
+
856
+ const origError = proto.error
857
+ const origLog = proto.log
858
+ const origWarn = proto.warn
859
+ const origDebug = proto.debug
860
+ const origVerbose = proto.verbose
861
+ function skipUndefinedMsg(msg) {
862
+ return msg == null || (typeof msg === 'string' && msg.trim() === '')
863
+ }
864
+ if (typeof origError === 'function') {
865
+ proto.error = function(message, trace, context) {
866
+ if (skipUndefinedMsg(message)) return
867
+ const meta = pinoLoggerCtx(message)
868
+ pinoLoggerSend('error', message, meta)
869
+ return origError.call(this, message, trace, context)
870
+ }
871
+ }
872
+ if (typeof origLog === 'function') {
873
+ proto.log = function(message, context) {
874
+ if (skipUndefinedMsg(message)) return
875
+ const meta = pinoLoggerCtx(message)
876
+ pinoLoggerSend('info', message, meta)
877
+ return origLog.call(this, message, context)
878
+ }
879
+ }
880
+ if (typeof origWarn === 'function') {
881
+ proto.warn = function(message, context) {
882
+ if (skipUndefinedMsg(message)) return
883
+ const meta = pinoLoggerCtx(message)
884
+ pinoLoggerSend('warn', message, meta)
885
+ return origWarn.call(this, message, context)
886
+ }
887
+ }
888
+ if (typeof origDebug === 'function') {
889
+ proto.debug = function(message, context) {
890
+ if (skipUndefinedMsg(message)) return
891
+ const meta = pinoLoggerCtx(message)
892
+ pinoLoggerSend('debug', message, meta)
893
+ return origDebug.call(this, message, context)
894
+ }
895
+ }
896
+ if (typeof origVerbose === 'function') {
897
+ proto.verbose = function(message, context) {
898
+ if (skipUndefinedMsg(message)) return
899
+ const meta = pinoLoggerCtx(message)
900
+ pinoLoggerSend('debug', message, meta)
901
+ return origVerbose.call(this, message, context)
902
+ }
903
+ }
904
+ }
905
+ if (result && result.Logger) patchPinoLoggerClass(result.Logger)
906
+ if (result && typeof result === 'function' && result.prototype && (result.prototype.call != null || result.prototype.error != null) && !result.prototype.__azifyPinoLoggerPatched) {
907
+ patchPinoLoggerClass(result)
414
908
  }
415
-
909
+
416
910
  if (result && result.LoggerModule && result.LoggerModule.forRoot) {
417
911
  const originalForRoot = result.LoggerModule.forRoot
418
-
912
+
419
913
  result.LoggerModule.forRoot = function(options) {
420
914
  if (process.env.AZIFY_LOGGER_URL) {
421
915
  try {
@@ -425,13 +919,13 @@ try {
425
919
  serviceName: process.env.APP_NAME || (options && options.pinoHttp && options.pinoHttp.name),
426
920
  environment: process.env.NODE_ENV
427
921
  })
428
-
922
+
429
923
  const pinoHttp = (options && options.pinoHttp) || {}
430
-
924
+
431
925
  if (pinoHttp.transport) {
432
926
  return originalForRoot.call(this, options)
433
927
  }
434
-
928
+
435
929
  const mergedOptions = {
436
930
  ...options,
437
931
  pinoHttp: {
@@ -439,176 +933,592 @@ try {
439
933
  stream: azifyStream,
440
934
  },
441
935
  }
442
-
936
+
443
937
  return originalForRoot.call(this, mergedOptions)
444
938
  } catch (err) {
445
939
  return originalForRoot.call(this, options)
446
940
  }
447
941
  }
448
-
942
+
449
943
  return originalForRoot.call(this, options)
450
944
  }
451
-
945
+
452
946
  Object.setPrototypeOf(result.LoggerModule.forRoot, originalForRoot)
453
947
  Object.assign(result.LoggerModule.forRoot, originalForRoot)
454
948
  }
455
949
  }
456
-
457
- return result
458
- }
459
-
460
- } catch (_) {}
461
950
 
462
- try {
463
- const undici = require('undici')
464
- if (undici && undici.request) {
465
- const originalRequest = undici.request
466
- const { randomBytes } = require('crypto')
467
- const { performance } = require('perf_hooks')
468
- const { shouldSample, markSource, HTTP_CLIENT_MODE } = require('./sampling')
469
-
470
- undici.request = function(url, options, callback) {
471
- const { getRequestContext } = require('./store')
472
- const ctx = getRequestContext()
473
- const otelCtx = getOtelTraceContext()
474
- const traceCtx = otelCtx || ctx
475
-
476
- const method = (options?.method || 'GET').toUpperCase()
477
- const urlString = typeof url === 'string' ? url : url?.toString() || 'unknown'
478
-
479
- const traceId = traceCtx?.traceId || randomUUID()
480
- const parentSpanId = traceCtx?.spanId || null
481
- const requestId = traceCtx?.requestId || ctx?.requestId || randomUUID()
482
- const spanId = randomBytes(8).toString('hex')
483
-
484
- const requestMeta = {
485
- traceId,
486
- spanId,
487
- parentSpanId,
488
- requestId,
489
- method,
490
- url: urlString
491
- }
492
-
493
- markSource(requestMeta, 'http-client')
494
-
495
- const startTime = performance.now()
496
-
497
- const shouldLogRequest = HTTP_CLIENT_MODE === 'all' || HTTP_CLIENT_MODE === 'errors' || !!traceId
498
- if (shouldLogRequest) {
499
- sendOutboundLog('info', `[REQUEST] ${method} ${urlString}`, requestMeta)
500
- }
501
-
502
- if (traceCtx && traceCtx.traceId) {
503
- const headers = (options && options.headers) || {}
504
- options = {
505
- ...options,
506
- headers: {
507
- ...headers,
508
- 'X-Trace-ID': traceCtx.traceId,
509
- 'X-Span-ID': spanId,
510
- 'X-Parent-Span-ID': traceCtx.parentSpanId || '',
511
- 'X-Request-ID': requestId
951
+ const skipBullPatch = process.env.AZIFY_LOGGER_NO_BULL_PATCH === '1'
952
+ if (!skipBullPatch && pathNorm.includes('explorer') && (pathNorm.includes('@nestjs/bull') || pathNorm.includes('nestjs/bull')) && !pathNorm.includes('bullmq')) {
953
+ try {
954
+ const Cls = (result && result.BullExplorer) || result
955
+ if (Cls && Cls.prototype && typeof Cls.prototype.handleProcessor === 'function' && !Cls.prototype.handleProcessor.__azifyPatched) {
956
+ const store = require('./store')
957
+ const toTraceIdHex = store.toTraceIdHex
958
+ const startRequestContext = store.startRequestContext
959
+ const setLastJobContext = store.setLastJobContext
960
+ const originalHandleProcessor = Cls.prototype.handleProcessor
961
+ Cls.prototype.handleProcessor = function(instance, key, queue, moduleRef, isRequestScoped, options) {
962
+ const self = this
963
+ const origArgs = [instance, key, queue, moduleRef, isRequestScoped, options]
964
+ const queueRef = queue
965
+ const wrapProcessor = (processor) => {
966
+ if (typeof processor !== 'function') return processor
967
+ return function(job) {
968
+ let ctx = null
969
+ try {
970
+ const data = job && job.data
971
+ const rawTraceId = data && data.traceId
972
+ const traceId = rawTraceId ? toTraceIdHex(String(rawTraceId)) : null
973
+ const spanId = data && data.spanId != null ? String(data.spanId).slice(0, 16) : null
974
+ if (traceId && spanId) ctx = { traceId, spanId, parentSpanId: data?.parentSpanId ?? null, requestId: data?.requestId ?? null }
975
+ else { let r = ''; try { r = (require('crypto').randomUUID && require('crypto').randomUUID()) || '' } catch (_) {}; ctx = startRequestContext({ requestId: r }) }
976
+ } catch (_) {}
977
+ if (!ctx) ctx = startRequestContext({})
978
+ setLastJobContext(ctx)
979
+ return processor(job)
980
+ }
981
+ }
982
+ const origProcess = queueRef.process
983
+ queueRef.process = function(...args) {
984
+ if (args.length > 0 && typeof args[args.length - 1] === 'function') {
985
+ args[args.length - 1] = wrapProcessor(args[args.length - 1])
986
+ }
987
+ return origProcess.apply(this, args)
988
+ }
989
+ try {
990
+ return originalHandleProcessor.apply(self, origArgs)
991
+ } finally {
992
+ queueRef.process = origProcess
993
+ }
512
994
  }
995
+ Cls.prototype.handleProcessor.__azifyPatched = true
513
996
  }
514
-
515
- try {
516
- const activeContext = otelContext.active()
517
- const span = trace.getSpan(activeContext)
518
- if (span) {
519
- const spanContext = span.spanContext()
520
- if (spanContext && spanContext.traceId && spanContext.spanId) {
997
+ } catch (_) {}
998
+ }
999
+
1000
+ if (!skipBullPatch && pathNorm.includes('bullmq') && pathNorm.includes('processor-decorator')) {
1001
+ try {
1002
+ const Cls = (result && result.ProcessorDecoratorService) || result
1003
+ if (Cls && Cls.prototype && typeof Cls.prototype.decorate === 'function' && !Cls.prototype.decorate.__azifyPatched) {
1004
+ const store = require('./store')
1005
+ const als = store.als
1006
+ const toTraceIdHex = store.toTraceIdHex
1007
+ const startRequestContext = store.startRequestContext
1008
+ const setLastJobContext = store.setLastJobContext
1009
+ const originalDecorate = Cls.prototype.decorate
1010
+ Cls.prototype.decorate = function(processor) {
1011
+ const decorated = originalDecorate.call(this, processor)
1012
+ if (typeof decorated !== 'function') return decorated
1013
+ return function(job, token, signal) {
1014
+ let ctx = null
521
1015
  try {
522
- const propagation = require('@opentelemetry/api').propagation
523
- if (propagation && propagation.inject) {
524
- propagation.inject(activeContext, options.headers)
1016
+ const data = job && job.data
1017
+ const rawTraceId = data && data.traceId
1018
+ const traceId = rawTraceId ? toTraceIdHex(String(rawTraceId)) : null
1019
+ const spanId = data && data.spanId != null ? String(data.spanId).slice(0, 16) : null
1020
+ if (traceId && spanId) ctx = { traceId, spanId, parentSpanId: data?.parentSpanId ?? null, requestId: data?.requestId ?? null }
1021
+ else { let r = ''; try { r = (require('crypto').randomUUID && require('crypto').randomUUID()) || '' } catch (_) {}; ctx = startRequestContext({ requestId: r }) }
1022
+ } catch (_) {}
1023
+ if (!ctx) ctx = startRequestContext({})
1024
+ setLastJobContext(ctx)
1025
+ return als.run(ctx, function() { return decorated(job, token, signal) })
1026
+ }
1027
+ }
1028
+ Cls.prototype.decorate.__azifyPatched = true
1029
+ }
1030
+ } catch (_) {}
1031
+ }
1032
+
1033
+ const isPino = id === 'pino' || (typeof id === 'string' && (id.endsWith('/pino') || (pathNorm.includes('/pino') && !pathNorm.includes('nestjs-pino'))))
1034
+ if (isPino) {
1035
+ if (result && typeof result === 'function') {
1036
+ if (debug) process.stderr.write(`[azify-logger] require hook patching pino id=${id}\n`)
1037
+ const { getRequestContext, getLastJobContext } = require('./store')
1038
+ const originalPino = result
1039
+ const patchedPino = function(options, stream) {
1040
+ if (new.target) {
1041
+ return originalPino.call(this, options, stream)
1042
+ }
1043
+ const logger = originalPino(options, stream)
1044
+ if (logger.__azifyPatched) return logger
1045
+ logger.__azifyPatched = true
1046
+ const originalLog = logger.log
1047
+ const originalInfo = logger.info
1048
+ const originalError = logger.error
1049
+ const originalWarn = logger.warn
1050
+ const originalDebug = logger.debug
1051
+ const originalFatal = logger.fatal
1052
+ const originalTrace = logger.trace
1053
+ const originalChild = logger.child
1054
+ const levelMap = { log: 'info', info: 'info', error: 'error', warn: 'warn', debug: 'debug', fatal: 'fatal', trace: 'trace' }
1055
+ const { toTraceIdHex } = require('./store')
1056
+ const patchLog = (original, methodName) => {
1057
+ return function(obj, msg, ...args) {
1058
+ let ctx = getRequestContext()
1059
+ if (!ctx || !ctx.traceId) {
1060
+ const jobCtx = getLastJobContext()
1061
+ if (jobCtx && jobCtx.traceId) ctx = jobCtx
1062
+ }
1063
+ const otelCtx = getOtelTraceContext()
1064
+ let traceId = ctx?.traceId
1065
+ let spanId = ctx?.spanId
1066
+ if (typeof obj === 'object' && obj !== null && (obj.traceId || obj.data?.traceId)) {
1067
+ traceId = obj.traceId || (obj.data && obj.data.traceId)
1068
+ spanId = obj.spanId != null ? obj.spanId : (obj.data && obj.data.spanId)
1069
+ }
1070
+ const msgStr = (typeof obj === 'object' ? obj?.message : obj) ?? msg ?? ''
1071
+ if (!traceId && typeof msgStr === 'string' && /traceId/i.test(msgStr)) {
1072
+ const m = msgStr.match(/"traceId"\s*:\s*"([^"]+)"/)
1073
+ if (m) { traceId = m[1]; const s = msgStr.match(/"spanId"\s*:\s*"([^"]+)"/); spanId = s ? s[1] : null }
1074
+ }
1075
+ let traceCtx = traceId ? { traceId: toTraceIdHex(String(traceId)), spanId: spanId != null ? String(spanId).slice(0, 16) : null, parentSpanId: ctx?.parentSpanId ?? null, requestId: ctx?.requestId ?? null } : (ctx || otelCtx)
1076
+ if (!traceCtx || !traceCtx.traceId) {
1077
+ try {
1078
+ const { startRequestContext } = require('./store')
1079
+ traceCtx = startRequestContext({})
1080
+ } catch (_) {
1081
+ traceCtx = { traceId: require('crypto').randomUUID ? require('crypto').randomUUID().replace(/-/g, '') : String(Date.now()), spanId: null, parentSpanId: null, requestId: null }
525
1082
  }
1083
+ }
1084
+ try {
1085
+ const out = (typeof obj === 'object' && obj !== null) ? { ...obj, traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId } : { traceId: traceCtx.traceId, spanId: traceCtx.spanId, parentSpanId: traceCtx.parentSpanId, requestId: traceCtx.requestId || ctx?.requestId, msg: obj }
1086
+ const message = (typeof out === 'object' ? out.message : out) || msg || ''
1087
+ const meta = { ...out, service: { name: serviceName, version: '1.0.0' }, environment, timestamp: new Date().toISOString(), hostname: require('os').hostname() }
1088
+ sendOutboundLog(levelMap[methodName] || 'info', message, meta)
526
1089
  } catch (_) {}
1090
+ return original.call(this, obj, msg, ...args)
527
1091
  }
528
1092
  }
529
- } catch (_) { }
1093
+ logger.log = patchLog(originalLog, 'log')
1094
+ logger.info = patchLog(originalInfo, 'info')
1095
+ logger.error = patchLog(originalError, 'error')
1096
+ logger.warn = patchLog(originalWarn, 'warn')
1097
+ logger.debug = patchLog(originalDebug, 'debug')
1098
+ logger.fatal = patchLog(originalFatal, 'fatal')
1099
+ logger.trace = patchLog(originalTrace, 'trace')
1100
+ logger.child = function(bindings, options) {
1101
+ const childLogger = originalChild.call(this, bindings, options)
1102
+ if (childLogger && !childLogger.__azifyPatched) {
1103
+ childLogger.__azifyPatched = true
1104
+ childLogger.log = patchLog(childLogger.log || (() => {}), 'log')
1105
+ childLogger.info = patchLog(childLogger.info || (() => {}), 'info')
1106
+ childLogger.error = patchLog(childLogger.error || (() => {}), 'error')
1107
+ childLogger.warn = patchLog(childLogger.warn || (() => {}), 'warn')
1108
+ childLogger.debug = patchLog(childLogger.debug || (() => {}), 'debug')
1109
+ childLogger.fatal = patchLog(childLogger.fatal || (() => {}), 'fatal')
1110
+ childLogger.trace = patchLog(childLogger.trace || (() => {}), 'trace')
1111
+ }
1112
+ return childLogger
1113
+ }
1114
+ return logger
1115
+ }
1116
+ Object.setPrototypeOf(patchedPino, originalPino)
1117
+ Object.assign(patchedPino, originalPino)
1118
+ return patchedPino
530
1119
  }
531
-
532
- const wrappedCallback = callback ? function(err, data) {
533
- const duration = Number((performance.now() - startTime).toFixed(2))
534
-
535
- if (err) {
536
- const shouldLogError = HTTP_CLIENT_MODE === 'all' || HTTP_CLIENT_MODE === 'errors'
537
- if (shouldLogError) {
538
- const errorMeta = {
539
- ...requestMeta,
540
- error: err.message,
541
- responseTimeMs: duration
1120
+ }
1121
+
1122
+ if (id === 'undici' || id.includes('undici')) {
1123
+ try {
1124
+ if (debug) try { process.stderr.write(`[azify-logger] applyPatchesToModule UNDICI id=${(typeof id === 'string' ? id.slice(-50) : id)} hasRequest=${!!(result && result.request)}\n`) } catch (_) {}
1125
+ const patchUndiciRequest = (target, label) => {
1126
+ if (!target || typeof target.request !== 'function' || target.request.__azifyPatched) return
1127
+ if (debug) process.stderr.write(`[azify-logger] patching undici ${label}\n`)
1128
+ const { randomBytes } = require('crypto')
1129
+ const { performance } = require('perf_hooks')
1130
+ const { markSource, HTTP_CLIENT_MODE } = require('./sampling')
1131
+ const { getRequestContext, toTraceIdHex } = require('./store')
1132
+ const originalRequest = target.request
1133
+ target.request = function(url, options, callback) {
1134
+ const { getLastJobContext } = require('./store')
1135
+ const ctx = getRequestContext() || getLastJobContext()
1136
+ const otelCtx = getOtelTraceContext()
1137
+ const traceCtx = ctx || otelCtx
1138
+ const method = (options?.method || 'GET').toUpperCase()
1139
+ const urlString = typeof url === 'string' ? url : (url && url.toString && url.toString()) || 'unknown'
1140
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1141
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1142
+ const parentSpanId = traceCtx?.spanId || null
1143
+ const requestId = traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1144
+ const spanId = randomBytes(8).toString('hex')
1145
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method, url: urlString }
1146
+ markSource(requestMeta, 'http-client')
1147
+ const startTime = performance.now()
1148
+ if (HTTP_CLIENT_MODE !== 'off') {
1149
+ sendOutboundLog('info', `[REQUEST] ${method} ${urlString}`, requestMeta)
1150
+ }
1151
+ if (traceCtx && traceCtx.traceId) {
1152
+ const headers = (options && options.headers) || {}
1153
+ options = { ...options, headers: { ...headers, 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': traceCtx.parentSpanId || '', 'X-Request-ID': requestId } }
1154
+ }
1155
+ const wrappedCb = callback ? function(err, data) {
1156
+ const duration = Number((performance.now() - startTime).toFixed(2))
1157
+ if (HTTP_CLIENT_MODE !== 'off') {
1158
+ if (err) sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err.message, responseTimeMs: duration })
1159
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1160
+ }
1161
+ return callback(err, data)
1162
+ } : undefined
1163
+ if (wrappedCb) return originalRequest.call(this, url, options, wrappedCb)
1164
+ const promise = originalRequest.call(this, url, options)
1165
+ if (promise && typeof promise.then === 'function') {
1166
+ return promise.then(
1167
+ (data) => {
1168
+ const duration = Number((performance.now() - startTime).toFixed(2))
1169
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1170
+ sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1171
+ }
1172
+ return data
1173
+ },
1174
+ (err) => {
1175
+ const duration = Number((performance.now() - startTime).toFixed(2))
1176
+ if (HTTP_CLIENT_MODE !== 'off') {
1177
+ sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1178
+ }
1179
+ throw err
1180
+ }
1181
+ )
542
1182
  }
543
- sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, errorMeta)
1183
+ return promise
544
1184
  }
545
- } else if (data) {
546
- const statusCode = data.statusCode
547
- const shouldLogResponse = HTTP_CLIENT_MODE === 'all' || (HTTP_CLIENT_MODE === 'errors' && statusCode >= 400)
548
-
549
- if (shouldLogResponse) {
550
- const responseMeta = {
551
- ...requestMeta,
552
- statusCode,
553
- responseTimeMs: duration
1185
+ target.request.__azifyPatched = true
1186
+ }
1187
+ if (result && result.request) patchUndiciRequest(result, 'request')
1188
+ if (result && result.Dispatcher && result.Dispatcher.prototype && typeof result.Dispatcher.prototype.request === 'function' && !result.Dispatcher.prototype.request.__azifyPatched) {
1189
+ if (debug) process.stderr.write('[azify-logger] patching undici Dispatcher.prototype.request\n')
1190
+ const { randomBytes } = require('crypto')
1191
+ const { performance } = require('perf_hooks')
1192
+ const { markSource, HTTP_CLIENT_MODE } = require('./sampling')
1193
+ const { getRequestContext, toTraceIdHex } = require('./store')
1194
+ const proto = result.Dispatcher.prototype
1195
+ const origDispRequest = proto.request
1196
+ proto.request = function(opts, callback) {
1197
+ const { getLastJobContext } = require('./store')
1198
+ const ctx = getRequestContext() || getLastJobContext()
1199
+ const otelCtx = getOtelTraceContext()
1200
+ const traceCtx = ctx || otelCtx
1201
+ const method = (opts && (opts.method || 'GET')) || 'GET'
1202
+ const methodStr = String(method).toUpperCase()
1203
+ const origin = (opts && opts.origin) || ''
1204
+ const path = (opts && opts.path) != null ? opts.path : '/'
1205
+ const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
1206
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1207
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1208
+ const parentSpanId = traceCtx?.spanId || null
1209
+ const requestId = traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1210
+ const spanId = randomBytes(8).toString('hex')
1211
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method: methodStr, url: urlString }
1212
+ markSource(requestMeta, 'http-client')
1213
+ const startTime = performance.now()
1214
+ if (HTTP_CLIENT_MODE !== 'off') {
1215
+ sendOutboundLog('info', `[REQUEST] ${methodStr} ${urlString}`, requestMeta)
554
1216
  }
555
- sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${statusCode} ${duration}ms`, responseMeta)
1217
+ if (traceCtx && traceCtx.traceId && opts && typeof opts === 'object') {
1218
+ const headers = { ...(opts.headers || {}), 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': traceCtx.parentSpanId || '', 'X-Request-ID': requestId }
1219
+ opts = { ...opts, headers }
1220
+ }
1221
+ const wrappedCb = typeof callback === 'function' ? function(err, data) {
1222
+ const duration = Number((performance.now() - startTime).toFixed(2))
1223
+ if (HTTP_CLIENT_MODE !== 'off') {
1224
+ if (err) sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
1225
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1226
+ }
1227
+ return callback(err, data)
1228
+ } : undefined
1229
+ const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
1230
+ if (ret && typeof ret.then === 'function' && !wrappedCb) {
1231
+ return ret.then(
1232
+ (data) => {
1233
+ const duration = Number((performance.now() - startTime).toFixed(2))
1234
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1235
+ sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1236
+ }
1237
+ return data
1238
+ },
1239
+ (err) => {
1240
+ const duration = Number((performance.now() - startTime).toFixed(2))
1241
+ if (HTTP_CLIENT_MODE !== 'off') {
1242
+ sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1243
+ }
1244
+ throw err
1245
+ }
1246
+ )
1247
+ }
1248
+ return ret
556
1249
  }
1250
+ proto.request.__azifyPatched = true
557
1251
  }
558
-
559
- return callback(err, data)
560
- } : undefined
561
-
562
- if (callback) {
563
- return originalRequest.call(this, url, options, wrappedCallback)
564
- }
565
-
566
- const result = originalRequest.call(this, url, options)
567
-
568
- if (result && typeof result.then === 'function') {
569
- return result.then(
570
- (data) => {
571
- const duration = Number((performance.now() - startTime).toFixed(2))
572
- const statusCode = data.statusCode
573
- const shouldLogResponse = HTTP_CLIENT_MODE === 'all' || (HTTP_CLIENT_MODE === 'errors' && statusCode >= 400)
574
-
575
- if (shouldLogResponse) {
576
- const responseMeta = {
577
- ...requestMeta,
578
- statusCode,
579
- responseTimeMs: duration
1252
+ try {
1253
+ const cache = require.cache
1254
+ if (cache && typeof cache === 'object') {
1255
+ for (const key of Object.keys(cache)) {
1256
+ if (typeof key !== 'string') continue
1257
+ const p = key.replace(/\\/g, '/')
1258
+ if (p.indexOf('node_modules') === -1 || p.indexOf('undici') === -1 || p.includes('undici-package')) continue
1259
+ const mod = cache[key]
1260
+ if (!mod || !mod.exports || mod.exports === result) continue
1261
+ const ex = mod.exports
1262
+ if (ex && ex.request) patchUndiciRequest(ex, 'request(cache)')
1263
+ if (ex && ex.Dispatcher && ex.Dispatcher.prototype && typeof ex.Dispatcher.prototype.request === 'function' && !ex.Dispatcher.prototype.request.__azifyPatched) {
1264
+ const proto = ex.Dispatcher.prototype
1265
+ const origDispRequest = proto.request
1266
+ proto.request = function(opts, callback) {
1267
+ const { getLastJobContext } = require('./store')
1268
+ const ctx = getRequestContext() || getLastJobContext()
1269
+ const otelCtx = getOtelTraceContext()
1270
+ const traceCtx = ctx || otelCtx
1271
+ const method = (opts && (opts.method || 'GET')) || 'GET'
1272
+ const methodStr = String(method).toUpperCase()
1273
+ const origin = (opts && opts.origin) || ''
1274
+ const path = (opts && opts.path) != null ? opts.path : '/'
1275
+ const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
1276
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1277
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1278
+ const parentSpanId = traceCtx?.spanId || null
1279
+ const requestId = traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1280
+ const spanId = randomBytes(8).toString('hex')
1281
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method: methodStr, url: urlString }
1282
+ markSource(requestMeta, 'http-client')
1283
+ const startTime = performance.now()
1284
+ if (HTTP_CLIENT_MODE !== 'off') {
1285
+ sendOutboundLog('info', `[REQUEST] ${methodStr} ${urlString}`, requestMeta)
1286
+ }
1287
+ if (traceCtx && traceCtx.traceId && opts && typeof opts === 'object') {
1288
+ const headers = { ...(opts.headers || {}), 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': traceCtx.parentSpanId || '', 'X-Request-ID': requestId }
1289
+ opts = { ...opts, headers }
1290
+ }
1291
+ const wrappedCb = typeof callback === 'function' ? function(err, data) {
1292
+ const duration = Number((performance.now() - startTime).toFixed(2))
1293
+ if (HTTP_CLIENT_MODE !== 'off') {
1294
+ if (err) sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
1295
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1296
+ }
1297
+ return callback(err, data)
1298
+ } : undefined
1299
+ const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
1300
+ if (ret && typeof ret.then === 'function' && !wrappedCb) {
1301
+ return ret.then(
1302
+ (data) => {
1303
+ const duration = Number((performance.now() - startTime).toFixed(2))
1304
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1305
+ sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1306
+ }
1307
+ return data
1308
+ },
1309
+ (err) => {
1310
+ const duration = Number((performance.now() - startTime).toFixed(2))
1311
+ if (HTTP_CLIENT_MODE !== 'off') {
1312
+ sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1313
+ }
1314
+ throw err
1315
+ }
1316
+ )
1317
+ }
1318
+ return ret
1319
+ }
1320
+ proto.request.__azifyPatched = true
580
1321
  }
581
- sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${statusCode} ${duration}ms`, responseMeta)
582
1322
  }
583
-
584
- return data
1323
+ }
1324
+ } catch (_) {}
1325
+ } catch (_) {}
1326
+ }
1327
+ }
1328
+ try {
1329
+ const origLoad = Module._load
1330
+ if (origLoad && origLoad.__azifyEarlyPatched) {
1331
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[azify-logger] Module._load (late) keeping early hook (not overwriting)\n') } catch (_) {}
1332
+ } else if (typeof origLoad === 'function' && !Module._load.__azifyPatched) {
1333
+ Module._load = function (request, parent, isMain) {
1334
+ const result = origLoad.apply(this, arguments)
1335
+ const pathStr = typeof request === 'string' ? request.replace(/\\/g, '/') : ''
1336
+ const isUndici = pathStr === 'undici' || (pathStr.indexOf('undici') !== -1 && pathStr.indexOf('undici-package') === -1)
1337
+ if (isUndici) {
1338
+ if (debug) try { process.stderr.write(`[azify-logger] Module._load UNDICI path=${pathStr.slice(-70)}\n`) } catch (_) {}
1339
+ try {
1340
+ applyPatchesToModule(result, pathStr)
1341
+ } catch (_) {}
1342
+ }
1343
+ return result
1344
+ }
1345
+ Module._load.__azifyPatched = true
1346
+ if (debug) try { process.stderr.write('[azify-logger] Module._load hook installed\n') } catch (_) {}
1347
+ }
1348
+ } catch (_) {}
1349
+ try {
1350
+ const Ritm = require('require-in-the-middle')
1351
+ const Hook = Ritm && (Ritm.Hook || Ritm)
1352
+ if (typeof Hook === 'function') {
1353
+ new Hook(['undici', 'nestjs-pino', 'pino'], function (exports, name) {
1354
+ const replaced = applyPatchesToModule(exports, name)
1355
+ return (replaced !== undefined && replaced !== null) ? replaced : exports
1356
+ })
1357
+ }
1358
+ } catch (_) {}
1359
+ Module.prototype.require = function(id) {
1360
+ const idStr = typeof id === 'string' ? id : ''
1361
+ const pathNorm = idStr.replace(/\\/g, '/')
1362
+ const isPinoOrNest = id === 'pino' || id === 'nestjs-pino' || pathNorm.includes('nestjs-pino') || pathNorm.includes('/pino')
1363
+ if (debug && isPinoOrNest) {
1364
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK_BEFORE id=${idStr.slice(-80)}\n`) } catch (_) {}
1365
+ }
1366
+ if (isAzifyLoggerInternal(id)) return bypassRequire.call(this, id)
1367
+ if (callerIsAzifyLogger.call(this)) {
1368
+ const isPatchable = patchableIds.some(m => id === m || (typeof id === 'string' && id.includes(m)))
1369
+ if (!isPatchable) return bypassRequire.call(this, id)
1370
+ }
1371
+ const result = originalRequire.call(this, id)
1372
+ const idLooksLikeUndici = idStr === 'undici' || (pathNorm.indexOf('undici') !== -1 && pathNorm.indexOf('undici-package') === -1)
1373
+ const undiciNeedsPatch = result && (typeof result.request === 'function' && !result.request.__azifyPatched || typeof result.fetch === 'function' && !result.fetch.__azifyPatched)
1374
+ if (idLooksLikeUndici || undiciNeedsPatch) {
1375
+ if (debug) try { process.stderr.write('[AZIFY] REGISTER require undici caller=' + (this && typeof this.filename === 'string' ? String(this.filename).replace(/\\/g, '/').slice(-85) : '') + ' patched=' + !!(result && result.request && result.request.__azifyPatched) + '\n') } catch (_) {}
1376
+ if (process.env.AZIFY_LOGGER_HTTP_DEBUG === '1' || process.env.AZIFY_LOGGER_DEBUG === '1') {
1377
+ try { process.stderr.write('[AZIFY-HTTP] REGISTER_HOOK2 patch id=' + idStr.slice(-70) + '\n') } catch (_) {}
1378
+ }
1379
+ try {
1380
+ const early = require('./register-http-client-early')
1381
+ if (typeof early.patchUndiciExports === 'function') early.patchUndiciExports(result)
1382
+ } catch (_) {}
1383
+ }
1384
+ if (debug && (idLooksLikeUndici || undiciNeedsPatch)) {
1385
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK undici id=${idStr.slice(-60)} hasRequest=${!!(result && result.request)}\n`) } catch (_) {}
1386
+ }
1387
+ if (debug && isPinoOrNest) {
1388
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK_AFTER id=${idStr.slice(-80)} hasResult=${!!result}\n`) } catch (_) {}
1389
+ }
1390
+ const replaced = applyPatchesToModule(result, id)
1391
+ return (replaced !== undefined && replaced !== null) ? replaced : result
1392
+ }
1393
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1394
+ try { process.stderr.write('[azify-logger] REGISTER step3 second hook set\n') } catch (_) {}
1395
+ }
1396
+ let walkRunCount = 0
1397
+ function walkCacheAndPatch() {
1398
+ try {
1399
+ walkRunCount++
1400
+ const cache = require.cache
1401
+ const keys = cache && typeof cache === 'object' ? Object.keys(cache) : []
1402
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1403
+ try { process.stderr.write(`[azify-logger] walkCache #${walkRunCount} keys=${keys.length}\n`) } catch (_) {}
1404
+ }
1405
+ if (cache && typeof cache === 'object') {
1406
+ if (debug && keys.length > 0) {
1407
+ const pinoRelated = keys.filter(k => typeof k === 'string' && (k.includes('pino') || k.includes('nestjs') || k.includes('undici')))
1408
+ if (walkRunCount <= 3 || pinoRelated.length > 0) {
1409
+ try {
1410
+ const preview = pinoRelated.length ? ' -> ' + pinoRelated.map(k => k.replace(/\\/g, '/').slice(-55)).join(' | ') : ''
1411
+ process.stderr.write(`[azify-logger] walkCache #${walkRunCount}: ${keys.length} keys, ${pinoRelated.length} pino/nest/undici${preview}\n`)
1412
+ } catch (_) {}
1413
+ }
1414
+ }
1415
+ for (const key of keys) {
1416
+ if (typeof key !== 'string') continue
1417
+ const pathNorm = key.replace(/\\/g, '/')
1418
+ if (!pathNorm.includes('undici') && !pathNorm.includes('nestjs-pino') && !pathNorm.includes('/pino') && !pathNorm.includes('bullmq') && !pathNorm.includes('/bull') && !pathNorm.includes('@nestjs/common')) continue
1419
+ const mod = cache[key]
1420
+ if (!mod || !mod.exports) continue
1421
+ if (debug && (pathNorm.includes('nestjs-pino') || pathNorm.includes('undici'))) {
1422
+ try { process.stderr.write(`[azify-logger] walkCache PATCHING key=${pathNorm.slice(-70)}\n`) } catch (_) {}
1423
+ }
1424
+ const replaced = applyPatchesToModule(mod.exports, key)
1425
+ if (replaced !== undefined && replaced !== null) mod.exports = replaced
1426
+ }
1427
+ }
1428
+ } catch (e) {
1429
+ if (debug) try { process.stderr.write(`[azify-logger] walkCache err: ${e && e.message}\n`) } catch (_) {}
1430
+ }
1431
+ }
1432
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1433
+ try { process.stderr.write('[azify-logger] about to call walkCacheAndPatch\n') } catch (_) {}
1434
+ }
1435
+ walkCacheAndPatch()
1436
+ try {
1437
+ setTransport(createHttpLoggerTransport(loggerUrlString))
1438
+ } catch (_) {}
1439
+ try {
1440
+ require('undici')
1441
+ if (debug) try { process.stderr.write('[azify-logger] eager require(undici) done\n') } catch (_) {}
1442
+ } catch (_) {}
1443
+ walkCacheAndPatch()
1444
+ try {
1445
+ if (typeof globalThis.fetch === 'function' && !globalThis.fetch.__azifyPatched) {
1446
+ const origFetch = globalThis.fetch
1447
+ globalThis.fetch = function(url, opts) {
1448
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
1449
+ const ctx = getRequestContext() || getLastJobContext()
1450
+ const otelCtx = getOtelTraceContext()
1451
+ const traceCtx = ctx || otelCtx
1452
+ const method = (opts && opts.method) ? String(opts.method).toUpperCase() : 'GET'
1453
+ const urlStr = typeof url === 'string' ? url : (url && url.url) ? url.url : (url && url.toString && url.toString()) || 'unknown'
1454
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1455
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1456
+ const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method, url: urlStr }
1457
+ markSource(requestMeta, 'http-client')
1458
+ const start = require('perf_hooks').performance.now()
1459
+ if (HTTP_CLIENT_MODE !== 'off') {
1460
+ sendOutboundLog('info', `[REQUEST] ${method} ${urlStr}`, requestMeta)
1461
+ }
1462
+ return origFetch.apply(this, arguments).then(
1463
+ (res) => {
1464
+ const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
1465
+ if (HTTP_CLIENT_MODE !== 'off') {
1466
+ sendOutboundLog('info', `[RESPONSE] ${method} ${urlStr} ${res.status} ${duration}ms`, { ...requestMeta, statusCode: res.status, responseTimeMs: duration })
1467
+ }
1468
+ return res
585
1469
  },
586
1470
  (err) => {
587
- const duration = Number((performance.now() - startTime).toFixed(2))
588
- const shouldLogError = HTTP_CLIENT_MODE === 'all' || HTTP_CLIENT_MODE === 'errors'
589
- if (shouldLogError) {
590
- const errorMeta = {
591
- ...requestMeta,
592
- error: err.message || String(err),
593
- responseTimeMs: duration
594
- }
595
- sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, errorMeta)
1471
+ const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
1472
+ if (HTTP_CLIENT_MODE !== 'off') {
1473
+ sendOutboundLog('error', `[RESPONSE] ${method} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
596
1474
  }
597
-
598
1475
  throw err
599
1476
  }
600
1477
  )
601
1478
  }
602
-
603
- return result
1479
+ globalThis.fetch.__azifyPatched = true
1480
+ if (debug) try { process.stderr.write('[azify-logger] patched globalThis.fetch\n') } catch (_) {}
604
1481
  }
1482
+ } catch (_) {}
1483
+ if (debug) try { process.stderr.write('[azify-logger] REGISTER_END Module.prototype.require hook installed + walkCacheAndPatch done\n') } catch (_) {}
1484
+ if (typeof setImmediate === 'function') {
1485
+ setImmediate(walkCacheAndPatch)
605
1486
  }
606
- } catch (_) {}
1487
+ ;[0, 50, 200, 500, 1000].forEach(function (delay) {
1488
+ if (typeof setTimeout === 'function') {
1489
+ setTimeout(walkCacheAndPatch, delay)
1490
+ }
1491
+ })
1492
+ } catch (e) {
1493
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1494
+ try {
1495
+ process.stderr.write('[azify-logger] REGISTER main catch hit\n')
1496
+ const msg = e && (e.message || (e.stack && e.stack.split('\n')[0]) || String(e))
1497
+ process.stderr.write('[azify-logger] REGISTER main catch: ' + msg + '\n')
1498
+ if (e && e.stack) process.stderr.write(e.stack + '\n')
1499
+ } catch (_) {}
1500
+ }
1501
+ }
607
1502
 
608
1503
  try {
1504
+ const ModuleForAxios = require('module')
1505
+ const origRequireAxios = ModuleForAxios.prototype.require
1506
+ let applyAxiosPatch = null
1507
+ if (typeof origRequireAxios === 'function' && !origRequireAxios.__azifyAxiosHookInstalled) {
1508
+ ModuleForAxios.prototype.require = function (id) {
1509
+ const result = origRequireAxios.apply(this, arguments)
1510
+ const idStr = typeof id === 'string' ? id.replace(/\\/g, '/') : ''
1511
+ const isAxios = idStr === 'axios' || idStr.endsWith('/axios') || (idStr.includes('axios') && idStr.indexOf('axios-') === -1)
1512
+ if (isAxios && result && typeof result === 'object' && !result.__azifyLoggerPatched && applyAxiosPatch) {
1513
+ try { applyAxiosPatch(result) } catch (_) {}
1514
+ }
1515
+ return result
1516
+ }
1517
+ ModuleForAxios.prototype.require.__azifyAxiosHookInstalled = true
1518
+ }
609
1519
  const axios = require('axios')
610
1520
  if (axios) {
611
- const { getRequestContext, runWithRequestContext } = require('./store')
1521
+ const { getRequestContext, getLastJobContext, runWithRequestContext } = require('./store')
612
1522
 
613
1523
  const buildUrl = (config) => {
614
1524
  const url = config.url || ''
@@ -648,7 +1558,7 @@ try {
648
1558
  return config
649
1559
  }
650
1560
 
651
- const ctx = getRequestContext()
1561
+ const ctx = getRequestContext() || getLastJobContext()
652
1562
  const traceId = (ctx?.traceId) || randomUUID()
653
1563
  const parentSpanId = (ctx?.spanId) || null
654
1564
  const requestId = (ctx?.requestId) || randomUUID()
@@ -686,6 +1596,7 @@ try {
686
1596
  !!traceId
687
1597
 
688
1598
  if (shouldLogRequest) {
1599
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[AZIFY-DBG] axios REQUEST ' + method + ' ' + String(url).slice(0, 80) + '\n') } catch (_) {}
689
1600
  sendOutboundLog('info', `[REQUEST] ${method} ${url}`, requestMeta)
690
1601
  }
691
1602
 
@@ -717,7 +1628,7 @@ try {
717
1628
 
718
1629
  if (shouldLogResponse || hasTraceHeaders) {
719
1630
  const finalUrl = (url && url !== 'unknown') ? url : (marker?.meta?.url || response.config?.url || response.request?.responseURL || url)
720
-
1631
+
721
1632
  if (finalUrl && finalUrl !== 'unknown') {
722
1633
  let meta
723
1634
  let duration = 0
@@ -735,7 +1646,7 @@ try {
735
1646
  }
736
1647
  }
737
1648
  }
738
-
1649
+
739
1650
  if (marker && marker.meta) {
740
1651
  duration = Number((performance.now() - marker.start).toFixed(2))
741
1652
  let responseBodyString = stringifyBody(response.data)
@@ -752,7 +1663,7 @@ try {
752
1663
  }
753
1664
  } else {
754
1665
  const requestHeaders = response.config?.headers || {}
755
- const ctx = getRequestContext()
1666
+ const ctx = getRequestContext() || getLastJobContext()
756
1667
  const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
757
1668
  const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
758
1669
  const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
@@ -761,7 +1672,7 @@ try {
761
1672
  if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
762
1673
  responseBodyString = responseBodyString.slice(0, 5000)
763
1674
  }
764
-
1675
+
765
1676
  meta = {
766
1677
  traceId,
767
1678
  spanId,
@@ -775,11 +1686,11 @@ try {
775
1686
  responseBody: responseBodyString
776
1687
  }
777
1688
  }
778
-
1689
+
779
1690
  markSource(meta, 'http-client')
780
1691
  const message = `[RESPONSE] ${meta.method} ${meta.url} ${response.status} ${meta.responseTimeMs}ms`
781
1692
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
782
-
1693
+
783
1694
  sendOutboundLog(level, message, meta)
784
1695
  }
785
1696
  }
@@ -807,7 +1718,7 @@ try {
807
1718
  if (shouldLogError && url && url !== 'unknown') {
808
1719
  let meta
809
1720
  let duration = 0
810
-
1721
+
811
1722
  if (marker && marker.meta) {
812
1723
  duration = Number((performance.now() - marker.start).toFixed(2))
813
1724
  meta = {
@@ -824,12 +1735,12 @@ try {
824
1735
  }
825
1736
  } else {
826
1737
  const requestHeaders = config?.headers || {}
827
- const ctx = getRequestContext()
1738
+ const ctx = getRequestContext() || getLastJobContext()
828
1739
  const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
829
1740
  const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
830
1741
  const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
831
1742
  const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
832
-
1743
+
833
1744
  meta = {
834
1745
  traceId,
835
1746
  spanId,
@@ -848,10 +1759,10 @@ try {
848
1759
  }
849
1760
  }
850
1761
  }
851
-
1762
+
852
1763
  markSource(meta, 'http-client')
853
1764
  const message = `[ERROR] ${meta.method} ${meta.url}`
854
-
1765
+
855
1766
  sendOutboundLog('error', message, meta)
856
1767
  }
857
1768
  } catch (err) {
@@ -864,13 +1775,27 @@ try {
864
1775
  return instance
865
1776
  }
866
1777
 
867
- patchInstance(axios)
868
-
869
- if (axios.create) {
870
- const originalCreate = axios.create
871
- axios.create = function(config) {
872
- const instance = originalCreate.call(this, config)
873
- return patchInstance(instance)
1778
+ applyAxiosPatch = function (ax) {
1779
+ if (!ax || ax.__azifyLoggerPatched) return
1780
+ patchInstance(ax)
1781
+ if (ax.create) {
1782
+ const originalCreate = ax.create
1783
+ ax.create = function (config) {
1784
+ const instance = originalCreate.call(this, config)
1785
+ patchInstance(instance)
1786
+ return instance
1787
+ }
1788
+ }
1789
+ }
1790
+ applyAxiosPatch(axios)
1791
+ for (const key of Object.keys(require.cache || {})) {
1792
+ if (typeof key !== 'string') continue
1793
+ const keyNorm = key.replace(/\\/g, '/')
1794
+ if (keyNorm === 'axios' || keyNorm.endsWith('/axios') || (keyNorm.includes('node_modules') && keyNorm.includes('/axios'))) {
1795
+ const mod = require.cache[key]
1796
+ if (mod && mod.exports && typeof mod.exports === 'object' && !mod.exports.__azifyLoggerPatched) {
1797
+ try { applyAxiosPatch(mod.exports) } catch (_) {}
1798
+ }
874
1799
  }
875
1800
  }
876
1801
  }
@@ -882,7 +1807,7 @@ try {
882
1807
  if (!g.__azifyLoggerFetchPatched) {
883
1808
  g.__azifyLoggerFetchPatched = true
884
1809
 
885
- const { getRequestContext, runWithRequestContext } = require('./store')
1810
+ const { getRequestContext, getLastJobContext, runWithRequestContext } = require('./store')
886
1811
 
887
1812
  const originalFetch = globalThis.fetch.bind(globalThis)
888
1813
 
@@ -911,11 +1836,11 @@ try {
911
1836
  if (HTTP_CLIENT_MODE === 'off') {
912
1837
  return originalFetch(input, init)
913
1838
  }
914
-
1839
+
915
1840
  let request
916
1841
  let method = 'UNKNOWN'
917
1842
  let url = String(input)
918
-
1843
+
919
1844
  try {
920
1845
  request = ensureRequest(input, init)
921
1846
  method = request.method.toUpperCase()
@@ -943,7 +1868,7 @@ try {
943
1868
  return originalFetch(request)
944
1869
  }
945
1870
 
946
- const ctx = getRequestContext()
1871
+ const ctx = getRequestContext() || getLastJobContext()
947
1872
  const traceId = (ctx?.traceId) || randomUUID()
948
1873
  const parentSpanId = (ctx?.spanId) || null
949
1874
  const requestId = (ctx?.requestId) || randomUUID()
@@ -1057,10 +1982,10 @@ try {
1057
1982
  }
1058
1983
  return {}
1059
1984
  }
1060
-
1985
+
1061
1986
  let responseBodyString = null
1062
1987
  let responseHeaders = {}
1063
-
1988
+
1064
1989
  try {
1065
1990
  responseHeaders = headersToObject(response.headers)
1066
1991
  const contentType = response.headers.get('content-type') || ''
@@ -1075,7 +2000,7 @@ try {
1075
2000
  return null
1076
2001
  }
1077
2002
  }
1078
-
2003
+
1079
2004
  if (contentType.includes('application/json') || contentType.includes('text/')) {
1080
2005
  try {
1081
2006
  const clonedResponse = response.clone()
@@ -1117,18 +2042,18 @@ try {
1117
2042
  }
1118
2043
  } catch (_) {
1119
2044
  }
1120
-
2045
+
1121
2046
  const responseMeta = {
1122
2047
  ...requestMeta,
1123
2048
  statusCode: response.status,
1124
2049
  responseTimeMs: duration,
1125
2050
  responseHeaders
1126
2051
  }
1127
-
2052
+
1128
2053
  if (responseBodyString !== null) {
1129
2054
  responseMeta.responseBody = responseBodyString
1130
2055
  }
1131
-
2056
+
1132
2057
  markSource(responseMeta, 'http-client')
1133
2058
  const message = `[RESPONSE] ${method} ${url} ${response.status} ${duration}ms`
1134
2059
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
@@ -1141,77 +2066,5 @@ try {
1141
2066
  }
1142
2067
  } catch (_) {}
1143
2068
 
1144
- try {
1145
- const Bull = require('bull')
1146
- const { runWithRequestContext } = require('./store')
1147
-
1148
- const originalAdd = Bull.prototype.add
1149
- Bull.prototype.add = function(name, data, opts) {
1150
- const ctx = getRequestContext()
1151
-
1152
- if (ctx && ctx.traceId) {
1153
- data = {
1154
- ...data,
1155
- traceId: ctx.traceId,
1156
- spanId: ctx.spanId,
1157
- parentSpanId: ctx.parentSpanId,
1158
- requestId: ctx.requestId,
1159
- }
1160
- }
1161
-
1162
- return originalAdd.call(this, name, data, opts)
1163
- }
1164
-
1165
- const originalProcess = Bull.prototype.process
1166
- Bull.prototype.process = function(name, concurrency, handler) {
1167
- let actualName, actualConcurrency, actualHandler
1168
-
1169
- if (typeof name === 'function') {
1170
- actualHandler = name
1171
- actualName = '__default__'
1172
- actualConcurrency = 1
1173
- } else if (typeof concurrency === 'function') {
1174
- actualHandler = concurrency
1175
- actualName = name
1176
- actualConcurrency = 1
1177
- } else {
1178
- actualName = name
1179
- actualConcurrency = concurrency
1180
- actualHandler = handler
1181
- }
1182
-
1183
- const wrappedHandler = function(job, done) {
1184
- const { traceId, spanId, parentSpanId, requestId, ...jobData } = job.data
1185
-
1186
- if (traceId && spanId) {
1187
- const ctx = {
1188
- traceId,
1189
- spanId,
1190
- parentSpanId,
1191
- requestId,
1192
- }
1193
-
1194
- return runWithRequestContext(ctx, () => {
1195
- return actualHandler.call(this, job, done)
1196
- })
1197
- } else {
1198
- const { startRequestContext } = require('./store')
1199
- const newCtx = startRequestContext({ requestId: require('uuid').v4() })
1200
-
1201
- return runWithRequestContext(newCtx, () => {
1202
- return actualHandler.call(this, job, done)
1203
- })
1204
- }
1205
- }
1206
-
1207
- if (typeof name === 'function') {
1208
- return originalProcess.call(this, wrappedHandler)
1209
- } else if (typeof concurrency === 'function') {
1210
- return originalProcess.call(this, actualName, wrappedHandler)
1211
- } else {
1212
- return originalProcess.call(this, actualName, actualConcurrency, wrappedHandler)
1213
- }
1214
- }
1215
- } catch (_) {}
1216
2069
  } catch (_) {}
1217
2070
  }