azify-logger 1.0.44 → 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +84 -7
- package/init.js +21 -11
- package/middleware-express.js +46 -44
- package/middleware-pino.js +19 -10
- package/package.json +3 -1
- package/preload.js +15 -0
- package/register-http-client-early.js +816 -0
- package/register.js +1219 -366
- package/sampling.js +1 -1
- package/server.js +77 -10
- package/store.js +37 -11
- package/streams/httpQueue.js +24 -12
- package/streams/pino.js +45 -10
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
if (process.env.AZIFY_LOGGER_DISABLE === '1') return
|
|
3
|
+
try {
|
|
4
|
+
const HTTP_DEBUG = process.env.AZIFY_LOGGER_HTTP_DEBUG === '1' || process.env.AZIFY_LOGGER_DEBUG === '1'
|
|
5
|
+
function httpLog(msg) {
|
|
6
|
+
if (HTTP_DEBUG) try { process.stderr.write('[AZIFY-HTTP] ' + msg + '\n') } catch (_) {}
|
|
7
|
+
}
|
|
8
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
9
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: module loading\n') } catch (_) {}
|
|
10
|
+
}
|
|
11
|
+
const Module = require('module')
|
|
12
|
+
const path = require('path')
|
|
13
|
+
const EARLY_DIR = __dirname
|
|
14
|
+
|
|
15
|
+
function getOtelTraceContext() {
|
|
16
|
+
try {
|
|
17
|
+
const otelApi = require('@opentelemetry/api')
|
|
18
|
+
const span = otelApi.trace.getSpan(otelApi.context.active())
|
|
19
|
+
if (!span) return null
|
|
20
|
+
const ctx = span.spanContext()
|
|
21
|
+
return ctx && ctx.traceId ? { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId || null } : null
|
|
22
|
+
} catch (_) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const HTTP_VERBOSE = process.env.AZIFY_LOGGER_HTTP_VERBOSE === '1'
|
|
28
|
+
const MAX_EARLY_LOG_QUEUE = 2000
|
|
29
|
+
const earlyLogQueue = []
|
|
30
|
+
|
|
31
|
+
function isLoggerUrl(urlStr) {
|
|
32
|
+
const loggerUrl = process.env.AZIFY_LOGGER_URL
|
|
33
|
+
if (!loggerUrl || typeof urlStr !== 'string' || !urlStr.trim()) return false
|
|
34
|
+
const a = String(urlStr).trim().toLowerCase().replace(/\/+$/, '')
|
|
35
|
+
const b = String(loggerUrl).trim().toLowerCase().replace(/\/+$/, '')
|
|
36
|
+
if (!b) return false
|
|
37
|
+
return a === b || a.startsWith(b + '/')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function send(level, message, meta) {
|
|
41
|
+
try {
|
|
42
|
+
const msgStr = String(message)
|
|
43
|
+
const isReqRes = msgStr.indexOf('[REQUEST]') !== -1 || msgStr.indexOf('[RESPONSE]') !== -1
|
|
44
|
+
if (HTTP_VERBOSE && isReqRes) {
|
|
45
|
+
try { process.stderr.write('[AZIFY] HTTP send ' + msgStr.slice(0, 70) + ' global=' + (typeof global.__AZIFY_SEND_OUTBOUND_LOG === 'function') + '\n') } catch (_) {}
|
|
46
|
+
}
|
|
47
|
+
const fn = typeof global.__AZIFY_SEND_OUTBOUND_LOG === 'function' ? global.__AZIFY_SEND_OUTBOUND_LOG : null
|
|
48
|
+
if (isReqRes && (HTTP_VERBOSE || process.env.AZIFY_LOGGER_DEBUG === '1')) {
|
|
49
|
+
try { process.stderr.write('[AZIFY-DBG] early.send ' + msgStr.slice(0, 50) + ' hasFn=' + !!fn + ' queueLen=' + earlyLogQueue.length + '\n') } catch (_) {}
|
|
50
|
+
}
|
|
51
|
+
if (fn) {
|
|
52
|
+
if (meta && meta.__source === 'http-client') {
|
|
53
|
+
httpLog('SEND -> ' + msgStr.slice(0, 90))
|
|
54
|
+
}
|
|
55
|
+
fn(level, message, meta)
|
|
56
|
+
} else {
|
|
57
|
+
if (meta && meta.__source === 'http-client') httpLog('SEND_QUEUED (no global yet) ' + msgStr.slice(0, 60))
|
|
58
|
+
if (earlyLogQueue.length >= MAX_EARLY_LOG_QUEUE) earlyLogQueue.shift()
|
|
59
|
+
earlyLogQueue.push({ level, message, meta })
|
|
60
|
+
}
|
|
61
|
+
} catch (_) {}
|
|
62
|
+
}
|
|
63
|
+
function flushEarlyLogQueue() {
|
|
64
|
+
const fn = typeof global.__AZIFY_SEND_OUTBOUND_LOG === 'function' ? global.__AZIFY_SEND_OUTBOUND_LOG : null
|
|
65
|
+
if (!fn || !earlyLogQueue.length) return
|
|
66
|
+
const n = earlyLogQueue.length
|
|
67
|
+
httpLog('FLUSH_QUEUE n=' + n)
|
|
68
|
+
while (earlyLogQueue.length) {
|
|
69
|
+
const entry = earlyLogQueue.shift()
|
|
70
|
+
try { fn(entry.level, entry.message, entry.meta) } catch (_) {}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let storeSamplingLoaded = null
|
|
75
|
+
function loadStoreAndSampling() {
|
|
76
|
+
if (storeSamplingLoaded !== null) return storeSamplingLoaded
|
|
77
|
+
try {
|
|
78
|
+
const store = require(path.join(EARLY_DIR, 'store'))
|
|
79
|
+
const sampling = require(path.join(EARLY_DIR, 'sampling'))
|
|
80
|
+
storeSamplingLoaded = {
|
|
81
|
+
getRequestContext: store.getRequestContext,
|
|
82
|
+
getLastJobContext: store.getLastJobContext,
|
|
83
|
+
toTraceIdHex: store.toTraceIdHex,
|
|
84
|
+
markSource: sampling.markSource,
|
|
85
|
+
HTTP_CLIENT_MODE: sampling.HTTP_CLIENT_MODE
|
|
86
|
+
}
|
|
87
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
88
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: HTTP_CLIENT_MODE=' + storeSamplingLoaded.HTTP_CLIENT_MODE + '\n') } catch (_) {}
|
|
89
|
+
}
|
|
90
|
+
return storeSamplingLoaded
|
|
91
|
+
} catch (e) {
|
|
92
|
+
storeSamplingLoaded = false
|
|
93
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
94
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: loadStoreAndSampling failed: ' + (e && e.message) + '\n') } catch (_) {}
|
|
95
|
+
}
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function patchUndiciExports(exports) {
|
|
101
|
+
const hasRequest = exports && typeof exports.request === 'function' && !exports.request.__azifyPatched
|
|
102
|
+
const hasFetch = exports && typeof exports.fetch === 'function' && !exports.fetch.__azifyPatched
|
|
103
|
+
if (!exports || (!hasRequest && !hasFetch)) return
|
|
104
|
+
const deps = loadStoreAndSampling()
|
|
105
|
+
const noDeps = !deps
|
|
106
|
+
if (noDeps && process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
107
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: patching undici without deps (minimal wrapper)\n') } catch (_) {}
|
|
108
|
+
}
|
|
109
|
+
const getRequestContext = deps ? deps.getRequestContext : () => null
|
|
110
|
+
const getLastJobContext = deps ? deps.getLastJobContext : () => null
|
|
111
|
+
const toTraceIdHex = deps ? deps.toTraceIdHex : (x) => (typeof x === 'string' ? x : (x && String(x)) || '')
|
|
112
|
+
const markSource = deps ? deps.markSource : () => {}
|
|
113
|
+
const HTTP_CLIENT_MODE = deps ? deps.HTTP_CLIENT_MODE : 'all'
|
|
114
|
+
httpLog('PATCH_EXPORTS request=' + !!hasRequest + ' fetch=' + !!hasFetch + (noDeps ? ' noDeps=1' : ''))
|
|
115
|
+
if (HTTP_DEBUG) try { process.stderr.write('[AZIFY] undici PATCHED (request=' + !!hasRequest + ' fetch=' + !!hasFetch + ')\n') } catch (_) {}
|
|
116
|
+
if (hasRequest) {
|
|
117
|
+
const origRequest = exports.request
|
|
118
|
+
exports.request = function (url, options, callback) {
|
|
119
|
+
const opts = (typeof url === 'object' && url !== null && !(url instanceof URL)) ? url : options
|
|
120
|
+
const method = ((opts && opts.method) || 'GET').toUpperCase()
|
|
121
|
+
let urlStr = 'unknown'
|
|
122
|
+
const urlArg = (typeof url === 'string' || (url && (url.href || url instanceof URL))) ? url : (opts && (opts.url || opts.uri))
|
|
123
|
+
if (typeof urlArg === 'string') urlStr = urlArg
|
|
124
|
+
else if (urlArg && typeof urlArg.href === 'string') urlStr = urlArg.href
|
|
125
|
+
else if (urlArg && typeof urlArg.toString === 'function') urlStr = urlArg.toString()
|
|
126
|
+
else if (opts && typeof opts.origin === 'string' && opts.path != null) urlStr = opts.origin + (String(opts.path).startsWith('/') ? '' : '/') + String(opts.path)
|
|
127
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') { try { process.stderr.write('[AZIFY-PATCH] undici.request ' + method + ' ' + urlStr.slice(0, 80) + '\n') } catch (_) {} }
|
|
128
|
+
const _ep = process.env.AZIFY_DEBUG_LOG_PATH
|
|
129
|
+
if (_ep) { try { require('fs').appendFileSync(_ep, new Date().toISOString() + ' [AZIFY] WRAPPER exports.request ' + method + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
|
|
130
|
+
if (HTTP_VERBOSE || HTTP_DEBUG) {
|
|
131
|
+
try { process.stderr.write('[AZIFY] HTTP wrapper undici.request ' + method + ' ' + urlStr.slice(0, 80) + '\n') } catch (_) {}
|
|
132
|
+
}
|
|
133
|
+
httpLog('WRAPPER undici.request ' + method + ' ' + urlStr.slice(0, 80))
|
|
134
|
+
const ctx = getRequestContext() || getLastJobContext()
|
|
135
|
+
const otelCtx = getOtelTraceContext()
|
|
136
|
+
const traceCtx = ctx || otelCtx
|
|
137
|
+
const rawTraceId = traceCtx && traceCtx.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
138
|
+
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
139
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method, url: urlStr }
|
|
140
|
+
markSource(requestMeta, 'http-client')
|
|
141
|
+
const start = require('perf_hooks').performance.now()
|
|
142
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
143
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${method} ${urlStr}`, requestMeta)
|
|
144
|
+
const wrappedCb = callback ? function (err, data) {
|
|
145
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
146
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
147
|
+
if (err) send('error', `[RESPONSE] ${method} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
148
|
+
else if (data) send('info', `[RESPONSE] ${method} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
149
|
+
}
|
|
150
|
+
return callback(err, data)
|
|
151
|
+
} : undefined
|
|
152
|
+
if (wrappedCb) return origRequest.call(this, url, options, wrappedCb)
|
|
153
|
+
const p = origRequest.call(this, url, options)
|
|
154
|
+
if (p && typeof p.then === 'function') {
|
|
155
|
+
return p.then(
|
|
156
|
+
(data) => {
|
|
157
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
158
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) send('info', `[RESPONSE] ${method} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
159
|
+
return data
|
|
160
|
+
},
|
|
161
|
+
(err) => {
|
|
162
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
163
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${method} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
164
|
+
throw err
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
return p
|
|
169
|
+
}
|
|
170
|
+
exports.request.__azifyPatched = true
|
|
171
|
+
}
|
|
172
|
+
if (exports.Dispatcher && exports.Dispatcher.prototype && typeof exports.Dispatcher.prototype.request === 'function' && !exports.Dispatcher.prototype.request.__azifyPatched) {
|
|
173
|
+
const proto = exports.Dispatcher.prototype
|
|
174
|
+
const origDisp = proto.request
|
|
175
|
+
proto.request = function (opts, callback) {
|
|
176
|
+
const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
|
|
177
|
+
const origin = (opts && opts.origin) || ''
|
|
178
|
+
const path = (opts && opts.path) != null ? opts.path : '/'
|
|
179
|
+
const urlStr = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
|
|
180
|
+
httpLog('WRAPPER Dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
181
|
+
const ctx = getRequestContext() || getLastJobContext()
|
|
182
|
+
const otelCtx = getOtelTraceContext()
|
|
183
|
+
const traceCtx = ctx || otelCtx
|
|
184
|
+
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
185
|
+
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
186
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
187
|
+
markSource(requestMeta, 'http-client')
|
|
188
|
+
const start = require('perf_hooks').performance.now()
|
|
189
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
190
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
191
|
+
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
192
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
193
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
194
|
+
if (err) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
195
|
+
else if (data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
196
|
+
}
|
|
197
|
+
return callback(err, data)
|
|
198
|
+
} : undefined
|
|
199
|
+
const ret = wrappedCb ? origDisp.call(this, opts, wrappedCb) : origDisp.call(this, opts)
|
|
200
|
+
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
201
|
+
return ret.then(
|
|
202
|
+
(data) => {
|
|
203
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
204
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
205
|
+
return data
|
|
206
|
+
},
|
|
207
|
+
(err) => {
|
|
208
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
209
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
210
|
+
throw err
|
|
211
|
+
}
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
return ret
|
|
215
|
+
}
|
|
216
|
+
proto.request.__azifyPatched = true
|
|
217
|
+
}
|
|
218
|
+
if (typeof exports.fetch === 'function' && !exports.fetch.__azifyPatched && deps) {
|
|
219
|
+
const origFetch = exports.fetch
|
|
220
|
+
exports.fetch = function (input, init) {
|
|
221
|
+
const methodStr = String((init && init.method) || 'GET').toUpperCase()
|
|
222
|
+
const urlStr = typeof input === 'string' ? input : (input && (input.url || input.href)) ? String(input.url || input.href) : String(input)
|
|
223
|
+
httpLog('WRAPPER undici.fetch ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
224
|
+
const ctx = getRequestContext() || getLastJobContext()
|
|
225
|
+
const otelCtx = getOtelTraceContext()
|
|
226
|
+
const traceCtx = ctx || otelCtx
|
|
227
|
+
const rawTraceId = traceCtx && traceCtx.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
228
|
+
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
229
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
230
|
+
markSource(requestMeta, 'http-client')
|
|
231
|
+
const start = require('perf_hooks').performance.now()
|
|
232
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
233
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
234
|
+
return origFetch.apply(this, arguments).then(
|
|
235
|
+
function (response) {
|
|
236
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
237
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && response) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${response.status} ${duration}ms`, { ...requestMeta, statusCode: response.status, responseTimeMs: duration })
|
|
238
|
+
return response
|
|
239
|
+
},
|
|
240
|
+
function (err) {
|
|
241
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
242
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
243
|
+
throw err
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
exports.fetch.__azifyPatched = true
|
|
248
|
+
}
|
|
249
|
+
if (exports.getGlobalDispatcher && typeof exports.getGlobalDispatcher === 'function' && !exports.getGlobalDispatcher.__azifyPatched) {
|
|
250
|
+
const origGetGlobal = exports.getGlobalDispatcher
|
|
251
|
+
exports.getGlobalDispatcher = function getGlobalDispatcherPatched() {
|
|
252
|
+
const d = origGetGlobal.call(this)
|
|
253
|
+
if (!d || (d.request && d.request.__azifyPatched)) return d
|
|
254
|
+
const origReq = d.request
|
|
255
|
+
if (typeof origReq !== 'function') return d
|
|
256
|
+
d.request = function (opts, callback) {
|
|
257
|
+
const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
|
|
258
|
+
const origin = (opts && opts.origin) || ''
|
|
259
|
+
const pathPart = (opts && opts.path) != null ? opts.path : '/'
|
|
260
|
+
const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
|
|
261
|
+
httpLog('WRAPPER getGlobalDispatcher().request ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
262
|
+
const ctx = getRequestContext ? getRequestContext() : (getLastJobContext ? getLastJobContext() : null)
|
|
263
|
+
const otelCtx = getOtelTraceContext()
|
|
264
|
+
const traceCtx = ctx || otelCtx
|
|
265
|
+
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
266
|
+
const traceId = typeof rawTraceId === 'string' ? (toTraceIdHex ? toTraceIdHex(rawTraceId) : rawTraceId) : rawTraceId
|
|
267
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
268
|
+
if (markSource) markSource(requestMeta, 'http-client')
|
|
269
|
+
const start = require('perf_hooks').performance.now()
|
|
270
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
271
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
272
|
+
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
273
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
274
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
275
|
+
if (err) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
276
|
+
else if (data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
277
|
+
}
|
|
278
|
+
return callback(err, data)
|
|
279
|
+
} : undefined
|
|
280
|
+
const ret = wrappedCb ? origReq.call(this, opts, wrappedCb) : origReq.call(this, opts)
|
|
281
|
+
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
282
|
+
return ret.then(
|
|
283
|
+
(data) => {
|
|
284
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
285
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
286
|
+
return data
|
|
287
|
+
},
|
|
288
|
+
(err) => {
|
|
289
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
290
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
291
|
+
throw err
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
return ret
|
|
296
|
+
}
|
|
297
|
+
d.request.__azifyPatched = true
|
|
298
|
+
return d
|
|
299
|
+
}
|
|
300
|
+
exports.getGlobalDispatcher.__azifyPatched = true
|
|
301
|
+
httpLog('PATCH_EXPORTS getGlobalDispatcher wrapped')
|
|
302
|
+
}
|
|
303
|
+
patchGlobalDispatcherInPlace()
|
|
304
|
+
}
|
|
305
|
+
let _globalDispatcherCheckTicks = 0
|
|
306
|
+
function patchGlobalDispatcherInPlace() {
|
|
307
|
+
try {
|
|
308
|
+
const sym = Symbol.for('undici.globalDispatcher.1')
|
|
309
|
+
const d = typeof globalThis !== 'undefined' && globalThis[sym]
|
|
310
|
+
_globalDispatcherCheckTicks++
|
|
311
|
+
if (HTTP_DEBUG && (_globalDispatcherCheckTicks <= 3 || _globalDispatcherCheckTicks % 20 === 0)) {
|
|
312
|
+
try {
|
|
313
|
+
const reqFromProto = d && Object.getPrototypeOf(d) && Object.getPrototypeOf(d).request
|
|
314
|
+
process.stderr.write('[AZIFY] GLOBAL_DISPATCHER_CHECK tick=' + _globalDispatcherCheckTicks + ' hasD=' + !!d + ' hasRequest=' + !!(d && typeof d.request === 'function') + ' protoRequest=' + (typeof reqFromProto === 'function') + ' alreadyPatched=' + !!(d && d.request && d.request.__azifyGlobalPatched) + '\n')
|
|
315
|
+
} catch (_) {}
|
|
316
|
+
}
|
|
317
|
+
if (!d) return
|
|
318
|
+
let origReq = null
|
|
319
|
+
if (typeof d.request === 'function') origReq = d.request
|
|
320
|
+
else {
|
|
321
|
+
let proto = Object.getPrototypeOf(d)
|
|
322
|
+
while (proto) {
|
|
323
|
+
if (typeof proto.request === 'function') { origReq = proto.request; break }
|
|
324
|
+
proto = Object.getPrototypeOf(proto)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (!origReq || origReq.__azifyGlobalPatched) return
|
|
328
|
+
const deps = loadStoreAndSampling()
|
|
329
|
+
const getRequestContext = deps ? deps.getRequestContext : () => null
|
|
330
|
+
const getLastJobContext = deps ? deps.getLastJobContext : () => null
|
|
331
|
+
const toTraceIdHex = deps ? deps.toTraceIdHex : (x) => (typeof x === 'string' ? x : (x && String(x)) || '')
|
|
332
|
+
const markSource = deps ? deps.markSource : () => {}
|
|
333
|
+
const HTTP_CLIENT_MODE = deps ? deps.HTTP_CLIENT_MODE : 'all'
|
|
334
|
+
d.request = function (opts, callback) {
|
|
335
|
+
const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
|
|
336
|
+
const origin = (opts && opts.origin) || ''
|
|
337
|
+
const pathPart = (opts && opts.path) != null ? opts.path : '/'
|
|
338
|
+
const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
|
|
339
|
+
httpLog('WRAPPER globalThis.dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
340
|
+
const _dp = process.env.AZIFY_DEBUG_LOG_PATH
|
|
341
|
+
if (_dp) { try { require('fs').appendFileSync(_dp, new Date().toISOString() + ' [AZIFY] WRAPPER globalDispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
|
|
342
|
+
const ctx = getRequestContext ? getRequestContext() : (getLastJobContext ? getLastJobContext() : null)
|
|
343
|
+
const otelCtx = getOtelTraceContext()
|
|
344
|
+
const traceCtx = ctx || otelCtx
|
|
345
|
+
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
346
|
+
const traceId = typeof rawTraceId === 'string' ? (toTraceIdHex ? toTraceIdHex(rawTraceId) : rawTraceId) : rawTraceId
|
|
347
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
348
|
+
if (markSource) markSource(requestMeta, 'http-client')
|
|
349
|
+
const start = require('perf_hooks').performance.now()
|
|
350
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
351
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
352
|
+
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
353
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
354
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
355
|
+
if (err) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
356
|
+
else if (data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
357
|
+
}
|
|
358
|
+
return callback(err, data)
|
|
359
|
+
} : undefined
|
|
360
|
+
const ret = wrappedCb ? origReq.call(this, opts, wrappedCb) : origReq.call(this, opts)
|
|
361
|
+
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
362
|
+
return ret.then(
|
|
363
|
+
(data) => {
|
|
364
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
365
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
366
|
+
return data
|
|
367
|
+
},
|
|
368
|
+
(err) => {
|
|
369
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
370
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
371
|
+
throw err
|
|
372
|
+
}
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
return ret
|
|
376
|
+
}
|
|
377
|
+
d.request.__azifyGlobalPatched = true
|
|
378
|
+
httpLog('PATCH_EXPORTS globalThis.dispatcher.request wrapped')
|
|
379
|
+
try { process.stderr.write('[AZIFY] global dispatcher.request patchado — requests serão logados\n') } catch (_) {}
|
|
380
|
+
const debugLogPath = process.env.AZIFY_DEBUG_LOG_PATH
|
|
381
|
+
if (debugLogPath) {
|
|
382
|
+
try { require('fs').appendFileSync(debugLogPath, new Date().toISOString() + ' [AZIFY] global dispatcher patchado\n') } catch (_) {}
|
|
383
|
+
}
|
|
384
|
+
} catch (e) {
|
|
385
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
386
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: patch globalThis dispatcher: ' + (e && e.message) + '\n') } catch (_) {}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function isMainUndiciCacheKey(key) {
|
|
392
|
+
if (!key || typeof key !== 'string') return false
|
|
393
|
+
const k = key.replace(/\\/g, '/')
|
|
394
|
+
return k.indexOf('undici') !== -1 && k.indexOf('undici-package') === -1 && k.indexOf('undici/lib') === -1 && (k.endsWith('undici/index.js') || k.endsWith('undici.js'))
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
let patchedUndiciSingleton = null
|
|
398
|
+
function tryPatchAndProxyUndici(result, logLabel) {
|
|
399
|
+
if (!result || typeof result !== 'object') return null
|
|
400
|
+
if (result.request && result.request.__azifyPatched) {
|
|
401
|
+
patchedUndiciSingleton = result
|
|
402
|
+
return result
|
|
403
|
+
}
|
|
404
|
+
if (!result.request && !result.fetch) return null
|
|
405
|
+
const needsPatch = (typeof result.request === 'function' && !result.request.__azifyPatched) || (typeof result.fetch === 'function' && !result.fetch.__azifyPatched)
|
|
406
|
+
if (!needsPatch) return result
|
|
407
|
+
try {
|
|
408
|
+
patchUndiciExports(result)
|
|
409
|
+
} catch (e) {
|
|
410
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
411
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: ' + logLabel + ' patch threw: ' + (e && e.message) + '\n') } catch (_) {}
|
|
412
|
+
}
|
|
413
|
+
return result
|
|
414
|
+
}
|
|
415
|
+
patchedUndiciSingleton = result
|
|
416
|
+
if (HTTP_DEBUG) try { process.stderr.write('[AZIFY-DBG] undici PATCHED (first load) label=' + (logLabel || 'REQUIRE') + '\n') } catch (_) {}
|
|
417
|
+
try {
|
|
418
|
+
const cache = require.cache
|
|
419
|
+
if (cache && typeof cache === 'object') {
|
|
420
|
+
for (const key of Object.keys(cache)) {
|
|
421
|
+
if (isMainUndiciCacheKey(key) && cache[key].exports !== result) {
|
|
422
|
+
cache[key].exports = result
|
|
423
|
+
httpLog('REQUIRE first-load sync cache -> singleton key=' + String(key).slice(-55))
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} catch (_) {}
|
|
428
|
+
httpLog((logLabel || 'REQUIRE') + ' patched in place (no proxy)')
|
|
429
|
+
return result
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const origRequire = Module.prototype.require
|
|
433
|
+
if (typeof origRequire === 'function' && !Module.prototype.require.__azifyEarlyPatched) {
|
|
434
|
+
Module.prototype.require = function (id) {
|
|
435
|
+
const idStr = typeof id === 'string' ? id : ''
|
|
436
|
+
const isUndiciId = idStr === 'undici' || (idStr.indexOf('undici') !== -1 && idStr.indexOf('undici-package') === -1)
|
|
437
|
+
let result
|
|
438
|
+
if (HTTP_DEBUG && isUndiciId && idStr) {
|
|
439
|
+
const caller = (this && typeof this.filename === 'string') ? this.filename : (this && this.id) || 'unknown'
|
|
440
|
+
try { process.stderr.write('[AZIFY] require("undici") caller=' + String(caller).replace(/\\/g, '/').slice(-100) + ' singleton=' + !!patchedUndiciSingleton + '\n') } catch (_) {}
|
|
441
|
+
}
|
|
442
|
+
if (HTTP_DEBUG && idStr && (idStr === 'undici' || idStr.includes('undici'))) {
|
|
443
|
+
const caller = (this && typeof this.filename === 'string') ? this.filename : (this && this.id) || 'unknown'
|
|
444
|
+
try { process.stderr.write('[AZIFY-DBG] require("' + idStr.slice(-70) + '") caller=' + String(caller).slice(-90) + ' singleton=' + !!patchedUndiciSingleton + '\n') } catch (_) {}
|
|
445
|
+
}
|
|
446
|
+
if (isUndiciId && patchedUndiciSingleton) {
|
|
447
|
+
try {
|
|
448
|
+
const cache = require.cache
|
|
449
|
+
if (cache && typeof cache === 'object') {
|
|
450
|
+
for (const key of Object.keys(cache)) {
|
|
451
|
+
if (isMainUndiciCacheKey(key) && cache[key].exports !== patchedUndiciSingleton) {
|
|
452
|
+
cache[key].exports = patchedUndiciSingleton
|
|
453
|
+
httpLog('REQUIRE undici cache[all main] -> singleton key=' + String(key).slice(-60))
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
try {
|
|
457
|
+
const resolvedPath = Module._resolveFilename(idStr, this, false)
|
|
458
|
+
if (resolvedPath && cache[resolvedPath]) cache[resolvedPath].exports = patchedUndiciSingleton
|
|
459
|
+
} catch (_) {}
|
|
460
|
+
}
|
|
461
|
+
if (typeof patchedUndiciSingleton.request === 'function' && !patchedUndiciSingleton.request.__azifyPatched) {
|
|
462
|
+
patchUndiciExports(patchedUndiciSingleton)
|
|
463
|
+
}
|
|
464
|
+
patchGlobalDispatcherInPlace()
|
|
465
|
+
return patchedUndiciSingleton
|
|
466
|
+
} catch (_) {}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
result = origRequire.apply(this, arguments)
|
|
470
|
+
if (result && typeof result === 'object' && patchedUndiciSingleton && result !== patchedUndiciSingleton) {
|
|
471
|
+
const hasRequest = typeof result.request === 'function'
|
|
472
|
+
const hasDispatcher = result.Dispatcher && result.Dispatcher.prototype
|
|
473
|
+
if (hasRequest && hasDispatcher && (result.fetch || result.Pool)) {
|
|
474
|
+
try {
|
|
475
|
+
const cache = require.cache
|
|
476
|
+
if (cache && typeof cache === 'object') {
|
|
477
|
+
for (const key of Object.keys(cache)) {
|
|
478
|
+
if (cache[key].exports === result) {
|
|
479
|
+
cache[key].exports = patchedUndiciSingleton
|
|
480
|
+
httpLog('REQUIRE by-result -> singleton key=' + String(key).slice(-55))
|
|
481
|
+
break
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch (_) {}
|
|
486
|
+
return patchedUndiciSingleton
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (!isUndiciId) {
|
|
490
|
+
return result
|
|
491
|
+
}
|
|
492
|
+
httpLog('REQUIRE undici id=' + idStr.slice(-60) + ' singleton=' + !!patchedUndiciSingleton)
|
|
493
|
+
if (patchedUndiciSingleton && result !== patchedUndiciSingleton) {
|
|
494
|
+
try {
|
|
495
|
+
const cache = require.cache
|
|
496
|
+
if (cache && typeof cache === 'object') {
|
|
497
|
+
let resolvedPath
|
|
498
|
+
try { resolvedPath = Module._resolveFilename(idStr, this, false) } catch (_) {}
|
|
499
|
+
if (resolvedPath && cache[resolvedPath]) cache[resolvedPath].exports = patchedUndiciSingleton
|
|
500
|
+
for (const key of Object.keys(cache)) {
|
|
501
|
+
if (cache[key].exports === result) {
|
|
502
|
+
cache[key].exports = patchedUndiciSingleton
|
|
503
|
+
httpLog('REQUIRE undici cache -> singleton key=' + String(key).slice(-60))
|
|
504
|
+
break
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (typeof patchedUndiciSingleton.request === 'function' && !patchedUndiciSingleton.request.__azifyPatched) {
|
|
509
|
+
patchUndiciExports(patchedUndiciSingleton)
|
|
510
|
+
}
|
|
511
|
+
patchGlobalDispatcherInPlace()
|
|
512
|
+
} catch (_) {}
|
|
513
|
+
return patchedUndiciSingleton
|
|
514
|
+
}
|
|
515
|
+
if (result && (result.request || result.fetch) && !result.request?.__azifyPatched) {
|
|
516
|
+
const prox = tryPatchAndProxyUndici(result, 'REQUIRE')
|
|
517
|
+
if (prox) return prox
|
|
518
|
+
}
|
|
519
|
+
let cacheKeyUndici = null
|
|
520
|
+
if (result && typeof result === 'object') {
|
|
521
|
+
try {
|
|
522
|
+
const cache = require.cache
|
|
523
|
+
if (cache && typeof cache === 'object') {
|
|
524
|
+
for (const key of Object.keys(cache)) {
|
|
525
|
+
const mod = cache[key]
|
|
526
|
+
if (mod && mod.exports === result && typeof key === 'string') {
|
|
527
|
+
const k = key.replace(/\\/g, '/')
|
|
528
|
+
if (k.indexOf('undici') !== -1 && k.indexOf('undici-package') === -1) {
|
|
529
|
+
cacheKeyUndici = key
|
|
530
|
+
break
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} catch (_) {}
|
|
536
|
+
}
|
|
537
|
+
if (cacheKeyUndici && isUndiciShaped(result)) {
|
|
538
|
+
const kNorm = cacheKeyUndici.replace(/\\/g, '/')
|
|
539
|
+
const isMainUndiciEntry = (kNorm.endsWith('undici/index.js') || kNorm.endsWith('undici.js')) && kNorm.indexOf('undici/lib') === -1
|
|
540
|
+
if (isMainUndiciEntry && patchedUndiciSingleton) {
|
|
541
|
+
try {
|
|
542
|
+
const cache = require.cache
|
|
543
|
+
const mod = cache[cacheKeyUndici]
|
|
544
|
+
if (mod && mod.exports === result) {
|
|
545
|
+
mod.exports = patchedUndiciSingleton
|
|
546
|
+
httpLog('REQUIRE undici by path -> singleton id=' + idStr.slice(-50))
|
|
547
|
+
}
|
|
548
|
+
} catch (_) {}
|
|
549
|
+
return patchedUndiciSingleton
|
|
550
|
+
}
|
|
551
|
+
if (isMainUndiciEntry) {
|
|
552
|
+
const prox = tryPatchAndProxyUndici(result, 'REQUIRE(path)')
|
|
553
|
+
if (prox) {
|
|
554
|
+
httpLog('REQUIRE undici by path id=' + idStr.slice(-50))
|
|
555
|
+
return prox
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return result
|
|
560
|
+
}
|
|
561
|
+
Module.prototype.require.__azifyEarlyPatched = true
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const origLoad = Module._load
|
|
565
|
+
if (typeof origLoad === 'function' && !Module._load.__azifyEarlyPatched) {
|
|
566
|
+
Module._load = function (request, parent, isMain) {
|
|
567
|
+
const pathStr = typeof request === 'string' ? request.replace(/\\/g, '/') : ''
|
|
568
|
+
const isUndici = pathStr === 'undici' || (pathStr.indexOf('undici') !== -1 && pathStr.indexOf('undici-package') === -1)
|
|
569
|
+
if (HTTP_DEBUG && isUndici) {
|
|
570
|
+
try { process.stderr.write('[AZIFY] _LOAD undici path=' + pathStr.replace(/\\/g, '/').slice(-90) + ' parent=' + (parent && parent.filename ? String(parent.filename).replace(/\\/g, '/').slice(-80) : '') + '\n') } catch (_) {}
|
|
571
|
+
}
|
|
572
|
+
const isMainUndici = isUndici && pathStr.indexOf('undici/lib') === -1 && (pathStr === 'undici' || pathStr.endsWith('undici/index.js') || pathStr.endsWith('/undici.js'))
|
|
573
|
+
if (isUndici && patchedUndiciSingleton) {
|
|
574
|
+
const result = origLoad.apply(this, arguments)
|
|
575
|
+
try {
|
|
576
|
+
const cache = require.cache
|
|
577
|
+
if (cache && typeof cache === 'object' && result && typeof result === 'object') {
|
|
578
|
+
for (const key of Object.keys(cache)) {
|
|
579
|
+
const mod = cache[key]
|
|
580
|
+
if (mod && mod.exports === result) {
|
|
581
|
+
mod.exports = patchedUndiciSingleton
|
|
582
|
+
httpLog('_LOAD undici cache -> singleton key=' + String(key).slice(-60))
|
|
583
|
+
break
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
} catch (_) {}
|
|
588
|
+
return patchedUndiciSingleton
|
|
589
|
+
}
|
|
590
|
+
const result = origLoad.apply(this, arguments)
|
|
591
|
+
let returnValue = result
|
|
592
|
+
try {
|
|
593
|
+
let resolvedPath = pathStr
|
|
594
|
+
if (parent && parent.filename && typeof request === 'string') {
|
|
595
|
+
try { resolvedPath = Module._resolveFilename(request, parent, false) } catch (_) {}
|
|
596
|
+
}
|
|
597
|
+
const resolvedNorm = String(resolvedPath).replace(/\\/g, '/')
|
|
598
|
+
if (resolvedNorm.indexOf('undici') !== -1 && resolvedNorm.indexOf('api-request') !== -1 && resolvedNorm.indexOf('undici-package') === -1) {
|
|
599
|
+
const mod = require.cache[resolvedPath]
|
|
600
|
+
if (mod && mod.exports && typeof mod.exports === 'function' && !mod.exports.__azifyApiRequestPatched) {
|
|
601
|
+
const deps = loadStoreAndSampling()
|
|
602
|
+
const getRequestContext = deps ? deps.getRequestContext : () => null
|
|
603
|
+
const getLastJobContext = deps ? deps.getLastJobContext : () => null
|
|
604
|
+
const toTraceIdHex = deps ? deps.toTraceIdHex : (x) => (typeof x === 'string' ? x : (x && String(x)) || '')
|
|
605
|
+
const markSource = deps ? deps.markSource : () => {}
|
|
606
|
+
const HTTP_CLIENT_MODE = deps ? deps.HTTP_CLIENT_MODE : 'all'
|
|
607
|
+
const origRequest = mod.exports
|
|
608
|
+
const RequestHandler = mod.exports.RequestHandler
|
|
609
|
+
mod.exports = function wrappedApiRequest(opts, callback) {
|
|
610
|
+
const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
|
|
611
|
+
const urlPart = (opts && opts.origin) ? String(opts.origin).slice(0, 80) : (opts && opts.path) || ''
|
|
612
|
+
if (HTTP_DEBUG) try { process.stderr.write('[AZIFY-PATCH] api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
|
|
613
|
+
if (HTTP_VERBOSE || HTTP_DEBUG) {
|
|
614
|
+
try { process.stderr.write('[AZIFY] HTTP wrapper api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
|
|
615
|
+
}
|
|
616
|
+
const origin = (opts && opts.origin) || ''
|
|
617
|
+
const pathPart = (opts && opts.path) != null ? opts.path : '/'
|
|
618
|
+
const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
|
|
619
|
+
httpLog('WRAPPER api-request ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
620
|
+
const ctx = getRequestContext() || getLastJobContext()
|
|
621
|
+
const otelCtx = getOtelTraceContext()
|
|
622
|
+
const traceCtx = ctx || otelCtx
|
|
623
|
+
const rawTraceId = (traceCtx && traceCtx.traceId) || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
624
|
+
const traceId = typeof rawTraceId === 'string' ? (toTraceIdHex ? toTraceIdHex(rawTraceId) : rawTraceId) : rawTraceId
|
|
625
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
626
|
+
if (markSource) markSource(requestMeta, 'http-client')
|
|
627
|
+
const start = require('perf_hooks').performance.now()
|
|
628
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
629
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
630
|
+
const wrappedCb = typeof callback === 'function' ? function (err, data) {
|
|
631
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
632
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) {
|
|
633
|
+
if (err) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
634
|
+
else if (data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
635
|
+
}
|
|
636
|
+
return callback(err, data)
|
|
637
|
+
} : undefined
|
|
638
|
+
const ret = wrappedCb ? origRequest.call(this, opts, wrappedCb) : origRequest.call(this, opts)
|
|
639
|
+
if (ret && typeof ret.then === 'function' && !wrappedCb) {
|
|
640
|
+
return ret.then(
|
|
641
|
+
(data) => {
|
|
642
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
643
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && data) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${data.statusCode} ${duration}ms`, { ...requestMeta, statusCode: data.statusCode, responseTimeMs: duration })
|
|
644
|
+
return data
|
|
645
|
+
},
|
|
646
|
+
(err) => {
|
|
647
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
648
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
649
|
+
throw err
|
|
650
|
+
}
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
return ret
|
|
654
|
+
}
|
|
655
|
+
mod.exports.RequestHandler = RequestHandler
|
|
656
|
+
mod.exports.__azifyApiRequestPatched = true
|
|
657
|
+
returnValue = mod.exports
|
|
658
|
+
if (parent && parent.exports && typeof parent.exports === 'object') {
|
|
659
|
+
parent.exports.request = mod.exports
|
|
660
|
+
httpLog('_LOAD undici api-request.js: parent.exports.request = wrapper')
|
|
661
|
+
}
|
|
662
|
+
httpLog('_LOAD undici api-request.js patched (returning wrapper)')
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
} catch (e) {
|
|
666
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
667
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: patch api-request: ' + (e && e.message) + '\n') } catch (_) {}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (isUndici) {
|
|
671
|
+
httpLog('_LOAD undici path=' + pathStr.slice(-90))
|
|
672
|
+
try {
|
|
673
|
+
if (result && typeof result === 'object' && (result.request || result.fetch)) {
|
|
674
|
+
patchUndiciExports(result)
|
|
675
|
+
patchedUndiciSingleton = result
|
|
676
|
+
httpLog('_LOAD undici patched in place (no proxy)')
|
|
677
|
+
}
|
|
678
|
+
} catch (e) {
|
|
679
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
680
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: patchUndiciExports threw: ' + (e && e.message) + '\n') } catch (_) {}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return returnValue
|
|
685
|
+
}
|
|
686
|
+
Module._load.__azifyEarlyPatched = true
|
|
687
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
688
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: Module._load hook installed\n') } catch (_) {}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function patchGlobalFetch() {
|
|
693
|
+
try {
|
|
694
|
+
if (typeof globalThis.fetch !== 'function') return
|
|
695
|
+
if (globalThis.fetch.__azifyPatched) return
|
|
696
|
+
const deps = loadStoreAndSampling()
|
|
697
|
+
if (!deps || deps.HTTP_CLIENT_MODE === 'off') return
|
|
698
|
+
const { getRequestContext, getLastJobContext, toTraceIdHex, markSource, HTTP_CLIENT_MODE } = deps
|
|
699
|
+
const origFetch = globalThis.fetch
|
|
700
|
+
globalThis.fetch = function (input, init) {
|
|
701
|
+
const method = (init && init.method) || (input && input.method) || 'GET'
|
|
702
|
+
const methodStr = String(method).toUpperCase()
|
|
703
|
+
const urlStr = typeof input === 'string' ? input : (input && (input.url || input.href)) ? String(input.url || input.href) : String(input)
|
|
704
|
+
httpLog('WRAPPER globalFetch ' + methodStr + ' ' + urlStr.slice(0, 80))
|
|
705
|
+
const ctx = getRequestContext() || getLastJobContext()
|
|
706
|
+
const otelCtx = getOtelTraceContext()
|
|
707
|
+
const traceCtx = ctx || otelCtx
|
|
708
|
+
const rawTraceId = traceCtx && traceCtx.traceId || (require('crypto').randomUUID && require('crypto').randomUUID())
|
|
709
|
+
const traceId = typeof rawTraceId === 'string' ? toTraceIdHex(rawTraceId) : rawTraceId
|
|
710
|
+
const requestMeta = { traceId, spanId: (traceCtx && traceCtx.spanId) || null, parentSpanId: (traceCtx && traceCtx.parentSpanId) || null, requestId: (traceCtx && traceCtx.requestId) || null, method: methodStr, url: urlStr }
|
|
711
|
+
markSource(requestMeta, 'http-client')
|
|
712
|
+
const start = require('perf_hooks').performance.now()
|
|
713
|
+
const skipLog = isLoggerUrl(urlStr)
|
|
714
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('info', `[REQUEST] ${methodStr} ${urlStr}`, requestMeta)
|
|
715
|
+
return origFetch.apply(this, arguments).then(
|
|
716
|
+
function (response) {
|
|
717
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
718
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog && response) send('info', `[RESPONSE] ${methodStr} ${urlStr} ${response.status} ${duration}ms`, { ...requestMeta, statusCode: response.status, responseTimeMs: duration })
|
|
719
|
+
return response
|
|
720
|
+
},
|
|
721
|
+
function (err) {
|
|
722
|
+
const duration = Number((require('perf_hooks').performance.now() - start).toFixed(2))
|
|
723
|
+
if (HTTP_CLIENT_MODE !== 'off' && !skipLog) send('error', `[RESPONSE] ${methodStr} ${urlStr} ERROR ${duration}ms`, { ...requestMeta, error: err && err.message, responseTimeMs: duration })
|
|
724
|
+
throw err
|
|
725
|
+
}
|
|
726
|
+
)
|
|
727
|
+
}
|
|
728
|
+
globalThis.fetch.__azifyPatched = true
|
|
729
|
+
if (process.env.AZIFY_LOGGER_DEBUG === '1') {
|
|
730
|
+
try { process.stderr.write('[azify-logger] register-http-client-early: global fetch patched\n') } catch (_) {}
|
|
731
|
+
}
|
|
732
|
+
} catch (_) {}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function isUndiciShaped(exports) {
|
|
736
|
+
if (!exports || typeof exports !== 'object') return false
|
|
737
|
+
const hasRequest = typeof exports.request === 'function'
|
|
738
|
+
const hasFetch = typeof exports.fetch === 'function'
|
|
739
|
+
const hasDispatcher = exports.Dispatcher && typeof exports.Dispatcher.prototype === 'object' && typeof exports.Dispatcher.prototype.request === 'function'
|
|
740
|
+
return hasRequest || hasFetch || hasDispatcher
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function getPatchedUndiciSingleton() {
|
|
744
|
+
return patchedUndiciSingleton
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
function patchUndiciInCache() {
|
|
748
|
+
try {
|
|
749
|
+
patchGlobalFetch()
|
|
750
|
+
const cache = require.cache
|
|
751
|
+
if (!cache || typeof cache !== 'object') return
|
|
752
|
+
const singleton = patchedUndiciSingleton
|
|
753
|
+
let patched = 0
|
|
754
|
+
let byPath = 0
|
|
755
|
+
let byShape = 0
|
|
756
|
+
let apiRequestWrapper = null
|
|
757
|
+
for (const key of Object.keys(cache)) {
|
|
758
|
+
if (typeof key !== 'string') continue
|
|
759
|
+
const p = key.replace(/\\/g, '/')
|
|
760
|
+
if (p.indexOf('undici') !== -1 && p.indexOf('api-request') !== -1 && p.indexOf('undici-package') === -1) {
|
|
761
|
+
const mod = cache[key]
|
|
762
|
+
if (mod && mod.exports && typeof mod.exports === 'function' && mod.exports.__azifyApiRequestPatched) {
|
|
763
|
+
apiRequestWrapper = mod.exports
|
|
764
|
+
break
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
for (const key of Object.keys(cache)) {
|
|
769
|
+
if (typeof key !== 'string') continue
|
|
770
|
+
const mod = cache[key]
|
|
771
|
+
if (!mod || !mod.exports) continue
|
|
772
|
+
const ex = mod.exports
|
|
773
|
+
const p = key.replace(/\\/g, '/')
|
|
774
|
+
const pathHasUndici = p.indexOf('undici') !== -1 && !p.includes('undici-package')
|
|
775
|
+
const shapeMatch = isUndiciShaped(ex)
|
|
776
|
+
if (!pathHasUndici && !shapeMatch) continue
|
|
777
|
+
if (pathHasUndici) byPath++
|
|
778
|
+
if (shapeMatch && !pathHasUndici) byShape++
|
|
779
|
+
if (apiRequestWrapper && pathHasUndici && p.indexOf('undici/lib/api') !== -1 && (p.endsWith('api/index.js') || p.endsWith('lib/api.js'))) {
|
|
780
|
+
if (ex && typeof ex.request !== 'undefined' && ex.request !== apiRequestWrapper) {
|
|
781
|
+
ex.request = apiRequestWrapper
|
|
782
|
+
httpLog('CACHE_SCAN lib/api.request -> wrapper key=' + String(key).slice(-60))
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const isMainUndici = pathHasUndici && (p.endsWith('undici/index.js') || p.endsWith('undici.js')) && p.indexOf('undici/lib') === -1
|
|
786
|
+
const beforeRequest = ex && ex.request && ex.request.__azifyPatched
|
|
787
|
+
const beforeFetch = ex && ex.fetch && ex.fetch.__azifyPatched
|
|
788
|
+
patchUndiciExports(mod.exports)
|
|
789
|
+
const afterRequest = ex && ex.request && ex.request.__azifyPatched
|
|
790
|
+
const afterFetch = ex && ex.fetch && ex.fetch.__azifyPatched
|
|
791
|
+
if ((afterRequest && !beforeRequest) || (afterFetch && !beforeFetch)) patched++
|
|
792
|
+
if (isMainUndici && singleton) {
|
|
793
|
+
if (ex && ex !== singleton && typeof ex.request === 'function' && singleton.request && singleton.request.__azifyPatched) {
|
|
794
|
+
ex.request = singleton.request
|
|
795
|
+
httpLog('CACHE_SCAN main undici mutate in place key=' + String(key).slice(-60))
|
|
796
|
+
}
|
|
797
|
+
mod.exports = singleton
|
|
798
|
+
httpLog('CACHE_SCAN main undici -> singleton key=' + String(key).slice(-70))
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
httpLog('CACHE_SCAN byPath=' + byPath + ' byShape=' + byShape + ' patched=' + patched)
|
|
802
|
+
if (HTTP_DEBUG) try {
|
|
803
|
+
process.stderr.write('[AZIFY] patchUndiciInCache byPath=' + byPath + ' byShape=' + byShape + ' patched=' + patched + '\n')
|
|
804
|
+
if (byPath > 0 || patched > 0) {
|
|
805
|
+
process.stderr.write('[AZIFY] undici no cache foi patchado — ao disparar job, espere [AZIFY-PATCH] e REQUEST/RESPONSE enqueued\n')
|
|
806
|
+
}
|
|
807
|
+
} catch (_) {}
|
|
808
|
+
patchGlobalDispatcherInPlace()
|
|
809
|
+
} catch (_) {}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
patchGlobalFetch()
|
|
813
|
+
module.exports = { patchUndiciExports, patchUndiciInCache, patchGlobalFetch, patchGlobalDispatcherInPlace, flushEarlyLogQueue, getPatchedUndiciSingleton }
|
|
814
|
+
} catch (_) {
|
|
815
|
+
module.exports = { patchUndiciExports: function () {}, patchUndiciInCache: function () {}, patchGlobalFetch: function () {}, patchGlobalDispatcherInPlace: function () {}, flushEarlyLogQueue: function () {}, getPatchedUndiciSingleton: function () { return null } }
|
|
816
|
+
}
|