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