azify-logger 1.0.44 → 1.0.46

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,605 @@ 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 existingHeaders = (options && options.headers && typeof options.headers === 'object') ? options.headers : {}
1141
+ const headerTraceId = existingHeaders['x-trace-id'] || existingHeaders['X-Trace-ID'] || null
1142
+ const headerSpanId = existingHeaders['x-span-id'] || existingHeaders['X-Span-ID'] || null
1143
+ const headerParentSpanId = existingHeaders['x-parent-span-id'] || existingHeaders['X-Parent-Span-ID'] || null
1144
+ const headerRequestId = existingHeaders['x-request-id'] || existingHeaders['X-Request-ID'] || null
1145
+ const rawTraceId = headerTraceId || traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1146
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1147
+ const parentSpanId = headerParentSpanId || traceCtx?.spanId || null
1148
+ const requestId = headerRequestId || traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1149
+ const spanId = headerSpanId || randomBytes(8).toString('hex')
1150
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method, url: urlString }
1151
+ markSource(requestMeta, 'http-client')
1152
+ const startTime = performance.now()
1153
+ if (HTTP_CLIENT_MODE !== 'off') {
1154
+ sendOutboundLog('info', `[REQUEST] ${method} ${urlString}`, requestMeta)
1155
+ }
1156
+ const headers = (options && options.headers && typeof options.headers === 'object') ? options.headers : {}
1157
+ options = { ...options, headers: { ...headers, 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': parentSpanId || '', 'X-Request-ID': requestId } }
1158
+ const wrappedCb = callback ? function(err, data) {
1159
+ const duration = Number((performance.now() - startTime).toFixed(2))
1160
+ if (HTTP_CLIENT_MODE !== 'off') {
1161
+ if (err) sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err.message, responseTimeMs: duration })
1162
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1163
+ }
1164
+ return callback(err, data)
1165
+ } : undefined
1166
+ if (wrappedCb) return originalRequest.call(this, url, options, wrappedCb)
1167
+ const promise = originalRequest.call(this, url, options)
1168
+ if (promise && typeof promise.then === 'function') {
1169
+ return promise.then(
1170
+ (data) => {
1171
+ const duration = Number((performance.now() - startTime).toFixed(2))
1172
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1173
+ sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1174
+ }
1175
+ return data
1176
+ },
1177
+ (err) => {
1178
+ const duration = Number((performance.now() - startTime).toFixed(2))
1179
+ if (HTTP_CLIENT_MODE !== 'off') {
1180
+ sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1181
+ }
1182
+ throw err
1183
+ }
1184
+ )
542
1185
  }
543
- sendOutboundLog('error', `[RESPONSE] ${method} ${urlString} ERROR ${duration}ms`, errorMeta)
1186
+ return promise
544
1187
  }
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
1188
+ target.request.__azifyPatched = true
1189
+ }
1190
+ if (result && result.request) patchUndiciRequest(result, 'request')
1191
+ if (result && result.Dispatcher && result.Dispatcher.prototype && typeof result.Dispatcher.prototype.request === 'function' && !result.Dispatcher.prototype.request.__azifyPatched) {
1192
+ if (debug) process.stderr.write('[azify-logger] patching undici Dispatcher.prototype.request\n')
1193
+ const { randomBytes } = require('crypto')
1194
+ const { performance } = require('perf_hooks')
1195
+ const { markSource, HTTP_CLIENT_MODE } = require('./sampling')
1196
+ const { getRequestContext, toTraceIdHex } = require('./store')
1197
+ const proto = result.Dispatcher.prototype
1198
+ const origDispRequest = proto.request
1199
+ proto.request = function(opts, callback) {
1200
+ const { getLastJobContext } = require('./store')
1201
+ const ctx = getRequestContext() || getLastJobContext()
1202
+ const otelCtx = getOtelTraceContext()
1203
+ const traceCtx = ctx || otelCtx
1204
+ const method = (opts && (opts.method || 'GET')) || 'GET'
1205
+ const methodStr = String(method).toUpperCase()
1206
+ const origin = (opts && opts.origin) || ''
1207
+ const path = (opts && opts.path) != null ? opts.path : '/'
1208
+ const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
1209
+ const existingHeaders = (opts && opts.headers && typeof opts.headers === 'object') ? opts.headers : {}
1210
+ const headerTraceId = existingHeaders['x-trace-id'] || existingHeaders['X-Trace-ID'] || null
1211
+ const headerSpanId = existingHeaders['x-span-id'] || existingHeaders['X-Span-ID'] || null
1212
+ const headerParentSpanId = existingHeaders['x-parent-span-id'] || existingHeaders['X-Parent-Span-ID'] || null
1213
+ const headerRequestId = existingHeaders['x-request-id'] || existingHeaders['X-Request-ID'] || null
1214
+ const rawTraceId = headerTraceId || traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1215
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1216
+ const parentSpanId = headerParentSpanId || traceCtx?.spanId || null
1217
+ const requestId = headerRequestId || traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1218
+ const spanId = headerSpanId || randomBytes(8).toString('hex')
1219
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method: methodStr, url: urlString }
1220
+ markSource(requestMeta, 'http-client')
1221
+ const startTime = performance.now()
1222
+ if (HTTP_CLIENT_MODE !== 'off') {
1223
+ sendOutboundLog('info', `[REQUEST] ${methodStr} ${urlString}`, requestMeta)
554
1224
  }
555
- sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${statusCode} ${duration}ms`, responseMeta)
1225
+ if (opts && typeof opts === 'object') {
1226
+ const headers = { ...(opts.headers || {}), 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': parentSpanId || '', 'X-Request-ID': requestId }
1227
+ opts = { ...opts, headers }
1228
+ }
1229
+ const wrappedCb = typeof callback === 'function' ? function(err, data) {
1230
+ const duration = Number((performance.now() - startTime).toFixed(2))
1231
+ if (HTTP_CLIENT_MODE !== 'off') {
1232
+ if (err) sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
1233
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1234
+ }
1235
+ return callback(err, data)
1236
+ } : undefined
1237
+ const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
1238
+ if (ret && typeof ret.then === 'function' && !wrappedCb) {
1239
+ return ret.then(
1240
+ (data) => {
1241
+ const duration = Number((performance.now() - startTime).toFixed(2))
1242
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1243
+ sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1244
+ }
1245
+ return data
1246
+ },
1247
+ (err) => {
1248
+ const duration = Number((performance.now() - startTime).toFixed(2))
1249
+ if (HTTP_CLIENT_MODE !== 'off') {
1250
+ sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1251
+ }
1252
+ throw err
1253
+ }
1254
+ )
1255
+ }
1256
+ return ret
556
1257
  }
1258
+ proto.request.__azifyPatched = true
557
1259
  }
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
1260
+ try {
1261
+ const cache = require.cache
1262
+ if (cache && typeof cache === 'object') {
1263
+ for (const key of Object.keys(cache)) {
1264
+ if (typeof key !== 'string') continue
1265
+ const p = key.replace(/\\/g, '/')
1266
+ if (p.indexOf('node_modules') === -1 || p.indexOf('undici') === -1 || p.includes('undici-package')) continue
1267
+ const mod = cache[key]
1268
+ if (!mod || !mod.exports || mod.exports === result) continue
1269
+ const ex = mod.exports
1270
+ if (ex && ex.request) patchUndiciRequest(ex, 'request(cache)')
1271
+ if (ex && ex.Dispatcher && ex.Dispatcher.prototype && typeof ex.Dispatcher.prototype.request === 'function' && !ex.Dispatcher.prototype.request.__azifyPatched) {
1272
+ const proto = ex.Dispatcher.prototype
1273
+ const origDispRequest = proto.request
1274
+ proto.request = function(opts, callback) {
1275
+ const { getLastJobContext } = require('./store')
1276
+ const ctx = getRequestContext() || getLastJobContext()
1277
+ const otelCtx = getOtelTraceContext()
1278
+ const traceCtx = ctx || otelCtx
1279
+ const method = (opts && (opts.method || 'GET')) || 'GET'
1280
+ const methodStr = String(method).toUpperCase()
1281
+ const origin = (opts && opts.origin) || ''
1282
+ const path = (opts && opts.path) != null ? opts.path : '/'
1283
+ const urlString = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
1284
+ const existingHeaders = (opts && opts.headers && typeof opts.headers === 'object') ? opts.headers : {}
1285
+ const headerTraceId = existingHeaders['x-trace-id'] || existingHeaders['X-Trace-ID'] || null
1286
+ const headerSpanId = existingHeaders['x-span-id'] || existingHeaders['X-Span-ID'] || null
1287
+ const headerParentSpanId = existingHeaders['x-parent-span-id'] || existingHeaders['X-Parent-Span-ID'] || null
1288
+ const headerRequestId = existingHeaders['x-request-id'] || existingHeaders['X-Request-ID'] || null
1289
+ const rawTraceId = headerTraceId || traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1290
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1291
+ const parentSpanId = headerParentSpanId || traceCtx?.spanId || null
1292
+ const requestId = headerRequestId || traceCtx?.requestId || ctx?.requestId || (require('crypto').randomUUID && require('crypto').randomUUID())
1293
+ const spanId = headerSpanId || randomBytes(8).toString('hex')
1294
+ const requestMeta = { traceId, spanId, parentSpanId, requestId, method: methodStr, url: urlString }
1295
+ markSource(requestMeta, 'http-client')
1296
+ const startTime = performance.now()
1297
+ if (HTTP_CLIENT_MODE !== 'off') {
1298
+ sendOutboundLog('info', `[REQUEST] ${methodStr} ${urlString}`, requestMeta)
1299
+ }
1300
+ if (opts && typeof opts === 'object') {
1301
+ const headers = { ...(opts.headers || {}), 'X-Trace-ID': traceId, 'X-Span-ID': spanId, 'X-Parent-Span-ID': parentSpanId || '', 'X-Request-ID': requestId }
1302
+ opts = { ...opts, headers }
1303
+ }
1304
+ const wrappedCb = typeof callback === 'function' ? function(err, data) {
1305
+ const duration = Number((performance.now() - startTime).toFixed(2))
1306
+ if (HTTP_CLIENT_MODE !== 'off') {
1307
+ if (err) sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
1308
+ else if (data) sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1309
+ }
1310
+ return callback(err, data)
1311
+ } : undefined
1312
+ const ret = wrappedCb ? origDispRequest.call(this, opts, wrappedCb) : origDispRequest.call(this, opts)
1313
+ if (ret && typeof ret.then === 'function' && !wrappedCb) {
1314
+ return ret.then(
1315
+ (data) => {
1316
+ const duration = Number((performance.now() - startTime).toFixed(2))
1317
+ if (HTTP_CLIENT_MODE !== 'off' && data) {
1318
+ sendOutboundLog('info', `[RESPONSE] ${methodStr} ${urlString} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
1319
+ }
1320
+ return data
1321
+ },
1322
+ (err) => {
1323
+ const duration = Number((performance.now() - startTime).toFixed(2))
1324
+ if (HTTP_CLIENT_MODE !== 'off') {
1325
+ sendOutboundLog('error', `[RESPONSE] ${methodStr} ${urlString} ERROR ${duration}ms`, { ...requestMeta, error: (err && err.message) || String(err), responseTimeMs: duration })
1326
+ }
1327
+ throw err
1328
+ }
1329
+ )
1330
+ }
1331
+ return ret
1332
+ }
1333
+ proto.request.__azifyPatched = true
580
1334
  }
581
- sendOutboundLog('info', `[RESPONSE] ${method} ${urlString} ${statusCode} ${duration}ms`, responseMeta)
582
1335
  }
583
-
584
- return data
1336
+ }
1337
+ } catch (_) {}
1338
+ } catch (_) {}
1339
+ }
1340
+ }
1341
+ try {
1342
+ const origLoad = Module._load
1343
+ if (origLoad && origLoad.__azifyEarlyPatched) {
1344
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[azify-logger] Module._load (late) keeping early hook (not overwriting)\n') } catch (_) {}
1345
+ } else if (typeof origLoad === 'function' && !Module._load.__azifyPatched) {
1346
+ Module._load = function (request, parent, isMain) {
1347
+ const result = origLoad.apply(this, arguments)
1348
+ const pathStr = typeof request === 'string' ? request.replace(/\\/g, '/') : ''
1349
+ const isUndici = pathStr === 'undici' || (pathStr.indexOf('undici') !== -1 && pathStr.indexOf('undici-package') === -1)
1350
+ if (isUndici) {
1351
+ if (debug) try { process.stderr.write(`[azify-logger] Module._load UNDICI path=${pathStr.slice(-70)}\n`) } catch (_) {}
1352
+ try {
1353
+ applyPatchesToModule(result, pathStr)
1354
+ } catch (_) {}
1355
+ }
1356
+ return result
1357
+ }
1358
+ Module._load.__azifyPatched = true
1359
+ if (debug) try { process.stderr.write('[azify-logger] Module._load hook installed\n') } catch (_) {}
1360
+ }
1361
+ } catch (_) {}
1362
+ try {
1363
+ const Ritm = require('require-in-the-middle')
1364
+ const Hook = Ritm && (Ritm.Hook || Ritm)
1365
+ if (typeof Hook === 'function') {
1366
+ new Hook(['undici', 'nestjs-pino', 'pino'], function (exports, name) {
1367
+ const replaced = applyPatchesToModule(exports, name)
1368
+ return (replaced !== undefined && replaced !== null) ? replaced : exports
1369
+ })
1370
+ }
1371
+ } catch (_) {}
1372
+ Module.prototype.require = function(id) {
1373
+ const idStr = typeof id === 'string' ? id : ''
1374
+ const pathNorm = idStr.replace(/\\/g, '/')
1375
+ const isPinoOrNest = id === 'pino' || id === 'nestjs-pino' || pathNorm.includes('nestjs-pino') || pathNorm.includes('/pino')
1376
+ if (debug && isPinoOrNest) {
1377
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK_BEFORE id=${idStr.slice(-80)}\n`) } catch (_) {}
1378
+ }
1379
+ if (isAzifyLoggerInternal(id)) return bypassRequire.call(this, id)
1380
+ if (callerIsAzifyLogger.call(this)) {
1381
+ const isPatchable = patchableIds.some(m => id === m || (typeof id === 'string' && id.includes(m)))
1382
+ if (!isPatchable) return bypassRequire.call(this, id)
1383
+ }
1384
+ const result = originalRequire.call(this, id)
1385
+ const idLooksLikeUndici = idStr === 'undici' || (pathNorm.indexOf('undici') !== -1 && pathNorm.indexOf('undici-package') === -1)
1386
+ const undiciNeedsPatch = result && (typeof result.request === 'function' && !result.request.__azifyPatched || typeof result.fetch === 'function' && !result.fetch.__azifyPatched)
1387
+ if (idLooksLikeUndici || undiciNeedsPatch) {
1388
+ 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 (_) {}
1389
+ if (process.env.AZIFY_LOGGER_HTTP_DEBUG === '1' || process.env.AZIFY_LOGGER_DEBUG === '1') {
1390
+ try { process.stderr.write('[AZIFY-HTTP] REGISTER_HOOK2 patch id=' + idStr.slice(-70) + '\n') } catch (_) {}
1391
+ }
1392
+ try {
1393
+ const early = require('./register-http-client-early')
1394
+ if (typeof early.patchUndiciExports === 'function') early.patchUndiciExports(result)
1395
+ } catch (_) {}
1396
+ }
1397
+ if (debug && (idLooksLikeUndici || undiciNeedsPatch)) {
1398
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK undici id=${idStr.slice(-60)} hasRequest=${!!(result && result.request)}\n`) } catch (_) {}
1399
+ }
1400
+ if (debug && isPinoOrNest) {
1401
+ try { process.stderr.write(`[azify-logger] REQUIRE_HOOK_AFTER id=${idStr.slice(-80)} hasResult=${!!result}\n`) } catch (_) {}
1402
+ }
1403
+ const replaced = applyPatchesToModule(result, id)
1404
+ return (replaced !== undefined && replaced !== null) ? replaced : result
1405
+ }
1406
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1407
+ try { process.stderr.write('[azify-logger] REGISTER step3 second hook set\n') } catch (_) {}
1408
+ }
1409
+ let walkRunCount = 0
1410
+ function walkCacheAndPatch() {
1411
+ try {
1412
+ walkRunCount++
1413
+ const cache = require.cache
1414
+ const keys = cache && typeof cache === 'object' ? Object.keys(cache) : []
1415
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1416
+ try { process.stderr.write(`[azify-logger] walkCache #${walkRunCount} keys=${keys.length}\n`) } catch (_) {}
1417
+ }
1418
+ if (cache && typeof cache === 'object') {
1419
+ if (debug && keys.length > 0) {
1420
+ const pinoRelated = keys.filter(k => typeof k === 'string' && (k.includes('pino') || k.includes('nestjs') || k.includes('undici')))
1421
+ if (walkRunCount <= 3 || pinoRelated.length > 0) {
1422
+ try {
1423
+ const preview = pinoRelated.length ? ' -> ' + pinoRelated.map(k => k.replace(/\\/g, '/').slice(-55)).join(' | ') : ''
1424
+ process.stderr.write(`[azify-logger] walkCache #${walkRunCount}: ${keys.length} keys, ${pinoRelated.length} pino/nest/undici${preview}\n`)
1425
+ } catch (_) {}
1426
+ }
1427
+ }
1428
+ for (const key of keys) {
1429
+ if (typeof key !== 'string') continue
1430
+ const pathNorm = key.replace(/\\/g, '/')
1431
+ if (!pathNorm.includes('undici') && !pathNorm.includes('nestjs-pino') && !pathNorm.includes('/pino') && !pathNorm.includes('bullmq') && !pathNorm.includes('/bull') && !pathNorm.includes('@nestjs/common')) continue
1432
+ const mod = cache[key]
1433
+ if (!mod || !mod.exports) continue
1434
+ if (debug && (pathNorm.includes('nestjs-pino') || pathNorm.includes('undici'))) {
1435
+ try { process.stderr.write(`[azify-logger] walkCache PATCHING key=${pathNorm.slice(-70)}\n`) } catch (_) {}
1436
+ }
1437
+ const replaced = applyPatchesToModule(mod.exports, key)
1438
+ if (replaced !== undefined && replaced !== null) mod.exports = replaced
1439
+ }
1440
+ }
1441
+ } catch (e) {
1442
+ if (debug) try { process.stderr.write(`[azify-logger] walkCache err: ${e && e.message}\n`) } catch (_) {}
1443
+ }
1444
+ }
1445
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1446
+ try { process.stderr.write('[azify-logger] about to call walkCacheAndPatch\n') } catch (_) {}
1447
+ }
1448
+ walkCacheAndPatch()
1449
+ try {
1450
+ setTransport(createHttpLoggerTransport(loggerUrlString))
1451
+ } catch (_) {}
1452
+ try {
1453
+ require('undici')
1454
+ if (debug) try { process.stderr.write('[azify-logger] eager require(undici) done\n') } catch (_) {}
1455
+ } catch (_) {}
1456
+ walkCacheAndPatch()
1457
+ try {
1458
+ if (typeof globalThis.fetch === 'function' && !globalThis.fetch.__azifyPatched) {
1459
+ const origFetch = globalThis.fetch
1460
+ globalThis.fetch = function(url, opts) {
1461
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('./store')
1462
+ const ctx = getRequestContext() || getLastJobContext()
1463
+ const otelCtx = getOtelTraceContext()
1464
+ const traceCtx = ctx || otelCtx
1465
+ const method = (opts && opts.method) ? String(opts.method).toUpperCase() : 'GET'
1466
+ const urlStr = typeof url === 'string' ? url : (url && url.url) ? url.url : (url && url.toString && url.toString()) || 'unknown'
1467
+ const rawTraceId = traceCtx?.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
1468
+ const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
1469
+ const requestMeta = { traceId, spanId: traceCtx?.spanId || null, parentSpanId: traceCtx?.parentSpanId || null, requestId: traceCtx?.requestId || null, method, url: urlStr }
1470
+ markSource(requestMeta, 'http-client')
1471
+ const start = require('perf_hooks').performance.now()
1472
+ if (HTTP_CLIENT_MODE !== 'off') {
1473
+ sendOutboundLog('info', `[REQUEST] ${method} ${urlStr}`, requestMeta)
1474
+ }
1475
+ return origFetch.apply(this, arguments).then(
1476
+ (res) => {
1477
+ const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
1478
+ if (HTTP_CLIENT_MODE !== 'off') {
1479
+ sendOutboundLog('info', `[RESPONSE] ${method} ${urlStr} ${res.status} ${duration}ms`, { ...requestMeta, statusCode: res.status, responseTimeMs: duration })
1480
+ }
1481
+ return res
585
1482
  },
586
1483
  (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)
1484
+ const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
1485
+ if (HTTP_CLIENT_MODE !== 'off') {
1486
+ sendOutboundLog('error', `[RESPONSE] ${method} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
596
1487
  }
597
-
598
1488
  throw err
599
1489
  }
600
1490
  )
601
1491
  }
602
-
603
- return result
1492
+ globalThis.fetch.__azifyPatched = true
1493
+ if (debug) try { process.stderr.write('[azify-logger] patched globalThis.fetch\n') } catch (_) {}
604
1494
  }
1495
+ } catch (_) {}
1496
+ if (debug) try { process.stderr.write('[azify-logger] REGISTER_END Module.prototype.require hook installed + walkCacheAndPatch done\n') } catch (_) {}
1497
+ if (typeof setImmediate === 'function') {
1498
+ setImmediate(walkCacheAndPatch)
605
1499
  }
606
- } catch (_) {}
1500
+ ;[0, 50, 200, 500, 1000].forEach(function (delay) {
1501
+ if (typeof setTimeout === 'function') {
1502
+ setTimeout(walkCacheAndPatch, delay)
1503
+ }
1504
+ })
1505
+ } catch (e) {
1506
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
1507
+ try {
1508
+ process.stderr.write('[azify-logger] REGISTER main catch hit\n')
1509
+ const msg = e && (e.message || (e.stack && e.stack.split('\n')[0]) || String(e))
1510
+ process.stderr.write('[azify-logger] REGISTER main catch: ' + msg + '\n')
1511
+ if (e && e.stack) process.stderr.write(e.stack + '\n')
1512
+ } catch (_) {}
1513
+ }
1514
+ }
607
1515
 
608
1516
  try {
1517
+ const ModuleForAxios = require('module')
1518
+ const origRequireAxios = ModuleForAxios.prototype.require
1519
+ let applyAxiosPatch = null
1520
+ if (typeof origRequireAxios === 'function' && !origRequireAxios.__azifyAxiosHookInstalled) {
1521
+ ModuleForAxios.prototype.require = function (id) {
1522
+ const result = origRequireAxios.apply(this, arguments)
1523
+ const idStr = typeof id === 'string' ? id.replace(/\\/g, '/') : ''
1524
+ const isAxios = idStr === 'axios' || idStr.endsWith('/axios') || (idStr.includes('axios') && idStr.indexOf('axios-') === -1)
1525
+ if (isAxios && result && typeof result === 'object' && !result.__azifyLoggerPatched && applyAxiosPatch) {
1526
+ try { applyAxiosPatch(result) } catch (_) {}
1527
+ }
1528
+ return result
1529
+ }
1530
+ ModuleForAxios.prototype.require.__azifyAxiosHookInstalled = true
1531
+ }
609
1532
  const axios = require('axios')
610
1533
  if (axios) {
611
- const { getRequestContext, runWithRequestContext } = require('./store')
1534
+ const { getRequestContext, getLastJobContext, runWithRequestContext } = require('./store')
612
1535
 
613
1536
  const buildUrl = (config) => {
614
1537
  const url = config.url || ''
@@ -648,7 +1571,7 @@ try {
648
1571
  return config
649
1572
  }
650
1573
 
651
- const ctx = getRequestContext()
1574
+ const ctx = getRequestContext() || getLastJobContext()
652
1575
  const traceId = (ctx?.traceId) || randomUUID()
653
1576
  const parentSpanId = (ctx?.spanId) || null
654
1577
  const requestId = (ctx?.requestId) || randomUUID()
@@ -686,6 +1609,7 @@ try {
686
1609
  !!traceId
687
1610
 
688
1611
  if (shouldLogRequest) {
1612
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') try { process.stderr.write('[AZIFY-DBG] axios REQUEST ' + method + ' ' + String(url).slice(0, 80) + '\n') } catch (_) {}
689
1613
  sendOutboundLog('info', `[REQUEST] ${method} ${url}`, requestMeta)
690
1614
  }
691
1615
 
@@ -717,7 +1641,7 @@ try {
717
1641
 
718
1642
  if (shouldLogResponse || hasTraceHeaders) {
719
1643
  const finalUrl = (url && url !== 'unknown') ? url : (marker?.meta?.url || response.config?.url || response.request?.responseURL || url)
720
-
1644
+
721
1645
  if (finalUrl && finalUrl !== 'unknown') {
722
1646
  let meta
723
1647
  let duration = 0
@@ -735,7 +1659,7 @@ try {
735
1659
  }
736
1660
  }
737
1661
  }
738
-
1662
+
739
1663
  if (marker && marker.meta) {
740
1664
  duration = Number((performance.now() - marker.start).toFixed(2))
741
1665
  let responseBodyString = stringifyBody(response.data)
@@ -752,7 +1676,7 @@ try {
752
1676
  }
753
1677
  } else {
754
1678
  const requestHeaders = response.config?.headers || {}
755
- const ctx = getRequestContext()
1679
+ const ctx = getRequestContext() || getLastJobContext()
756
1680
  const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
757
1681
  const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
758
1682
  const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
@@ -761,7 +1685,7 @@ try {
761
1685
  if (typeof responseBodyString === 'string' && responseBodyString.length > 5000) {
762
1686
  responseBodyString = responseBodyString.slice(0, 5000)
763
1687
  }
764
-
1688
+
765
1689
  meta = {
766
1690
  traceId,
767
1691
  spanId,
@@ -775,11 +1699,11 @@ try {
775
1699
  responseBody: responseBodyString
776
1700
  }
777
1701
  }
778
-
1702
+
779
1703
  markSource(meta, 'http-client')
780
1704
  const message = `[RESPONSE] ${meta.method} ${meta.url} ${response.status} ${meta.responseTimeMs}ms`
781
1705
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
782
-
1706
+
783
1707
  sendOutboundLog(level, message, meta)
784
1708
  }
785
1709
  }
@@ -807,7 +1731,7 @@ try {
807
1731
  if (shouldLogError && url && url !== 'unknown') {
808
1732
  let meta
809
1733
  let duration = 0
810
-
1734
+
811
1735
  if (marker && marker.meta) {
812
1736
  duration = Number((performance.now() - marker.start).toFixed(2))
813
1737
  meta = {
@@ -824,12 +1748,12 @@ try {
824
1748
  }
825
1749
  } else {
826
1750
  const requestHeaders = config?.headers || {}
827
- const ctx = getRequestContext()
1751
+ const ctx = getRequestContext() || getLastJobContext()
828
1752
  const traceId = requestHeaders['x-trace-id'] || requestHeaders['X-Trace-ID'] || ctx?.traceId || randomUUID()
829
1753
  const spanId = requestHeaders['x-span-id'] || requestHeaders['X-Span-ID'] || randomBytes(8).toString('hex')
830
1754
  const parentSpanId = requestHeaders['x-parent-span-id'] || requestHeaders['X-Parent-Span-ID'] || ctx?.spanId || null
831
1755
  const requestId = requestHeaders['x-request-id'] || requestHeaders['X-Request-ID'] || ctx?.requestId || randomUUID()
832
-
1756
+
833
1757
  meta = {
834
1758
  traceId,
835
1759
  spanId,
@@ -848,10 +1772,10 @@ try {
848
1772
  }
849
1773
  }
850
1774
  }
851
-
1775
+
852
1776
  markSource(meta, 'http-client')
853
1777
  const message = `[ERROR] ${meta.method} ${meta.url}`
854
-
1778
+
855
1779
  sendOutboundLog('error', message, meta)
856
1780
  }
857
1781
  } catch (err) {
@@ -864,13 +1788,27 @@ try {
864
1788
  return instance
865
1789
  }
866
1790
 
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)
1791
+ applyAxiosPatch = function (ax) {
1792
+ if (!ax || ax.__azifyLoggerPatched) return
1793
+ patchInstance(ax)
1794
+ if (ax.create) {
1795
+ const originalCreate = ax.create
1796
+ ax.create = function (config) {
1797
+ const instance = originalCreate.call(this, config)
1798
+ patchInstance(instance)
1799
+ return instance
1800
+ }
1801
+ }
1802
+ }
1803
+ applyAxiosPatch(axios)
1804
+ for (const key of Object.keys(require.cache || {})) {
1805
+ if (typeof key !== 'string') continue
1806
+ const keyNorm = key.replace(/\\/g, '/')
1807
+ if (keyNorm === 'axios' || keyNorm.endsWith('/axios') || (keyNorm.includes('node_modules') && keyNorm.includes('/axios'))) {
1808
+ const mod = require.cache[key]
1809
+ if (mod && mod.exports && typeof mod.exports === 'object' && !mod.exports.__azifyLoggerPatched) {
1810
+ try { applyAxiosPatch(mod.exports) } catch (_) {}
1811
+ }
874
1812
  }
875
1813
  }
876
1814
  }
@@ -882,7 +1820,7 @@ try {
882
1820
  if (!g.__azifyLoggerFetchPatched) {
883
1821
  g.__azifyLoggerFetchPatched = true
884
1822
 
885
- const { getRequestContext, runWithRequestContext } = require('./store')
1823
+ const { getRequestContext, getLastJobContext, runWithRequestContext } = require('./store')
886
1824
 
887
1825
  const originalFetch = globalThis.fetch.bind(globalThis)
888
1826
 
@@ -911,11 +1849,11 @@ try {
911
1849
  if (HTTP_CLIENT_MODE === 'off') {
912
1850
  return originalFetch(input, init)
913
1851
  }
914
-
1852
+
915
1853
  let request
916
1854
  let method = 'UNKNOWN'
917
1855
  let url = String(input)
918
-
1856
+
919
1857
  try {
920
1858
  request = ensureRequest(input, init)
921
1859
  method = request.method.toUpperCase()
@@ -943,7 +1881,7 @@ try {
943
1881
  return originalFetch(request)
944
1882
  }
945
1883
 
946
- const ctx = getRequestContext()
1884
+ const ctx = getRequestContext() || getLastJobContext()
947
1885
  const traceId = (ctx?.traceId) || randomUUID()
948
1886
  const parentSpanId = (ctx?.spanId) || null
949
1887
  const requestId = (ctx?.requestId) || randomUUID()
@@ -1057,10 +1995,10 @@ try {
1057
1995
  }
1058
1996
  return {}
1059
1997
  }
1060
-
1998
+
1061
1999
  let responseBodyString = null
1062
2000
  let responseHeaders = {}
1063
-
2001
+
1064
2002
  try {
1065
2003
  responseHeaders = headersToObject(response.headers)
1066
2004
  const contentType = response.headers.get('content-type') || ''
@@ -1075,7 +2013,7 @@ try {
1075
2013
  return null
1076
2014
  }
1077
2015
  }
1078
-
2016
+
1079
2017
  if (contentType.includes('application/json') || contentType.includes('text/')) {
1080
2018
  try {
1081
2019
  const clonedResponse = response.clone()
@@ -1117,18 +2055,18 @@ try {
1117
2055
  }
1118
2056
  } catch (_) {
1119
2057
  }
1120
-
2058
+
1121
2059
  const responseMeta = {
1122
2060
  ...requestMeta,
1123
2061
  statusCode: response.status,
1124
2062
  responseTimeMs: duration,
1125
2063
  responseHeaders
1126
2064
  }
1127
-
2065
+
1128
2066
  if (responseBodyString !== null) {
1129
2067
  responseMeta.responseBody = responseBodyString
1130
2068
  }
1131
-
2069
+
1132
2070
  markSource(responseMeta, 'http-client')
1133
2071
  const message = `[RESPONSE] ${method} ${url} ${response.status} ${duration}ms`
1134
2072
  const level = response.status >= 500 ? 'error' : response.status >= 400 ? 'warn' : 'info'
@@ -1141,77 +2079,5 @@ try {
1141
2079
  }
1142
2080
  } catch (_) {}
1143
2081
 
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
2082
  } catch (_) {}
1217
2083
  }