azify-logger 1.0.43 → 1.0.45

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