azify-logger 1.0.26 → 1.0.29
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/README.md +26 -5
- package/index.js +40 -17
- package/middleware-express.js +267 -366
- package/middleware-fastify.js +348 -0
- package/middleware-restify.js +147 -303
- package/package.json +31 -30
- package/queue/fileQueue.js +100 -0
- package/queue/redisQueue.js +179 -0
- package/queue/workerManager.js +111 -0
- package/register-otel.js +63 -13
- package/register.js +364 -99
- package/sampling.js +79 -0
- package/scripts/redis-worker.js +439 -0
- package/server.js +169 -74
- package/store.js +10 -4
- package/streams/bunyan.d.ts +26 -0
- package/streams/bunyan.js +39 -8
- package/streams/httpQueue.js +342 -0
- package/streams/pino.d.ts +38 -0
- package/streams/pino.js +44 -7
package/middleware-express.js
CHANGED
|
@@ -1,413 +1,314 @@
|
|
|
1
|
-
const
|
|
2
|
-
const {
|
|
1
|
+
const { startRequestContext } = require('./store')
|
|
2
|
+
const { createHttpLoggerTransport } = require('./streams/httpQueue')
|
|
3
|
+
const { Worker } = require('worker_threads')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const os = require('os')
|
|
3
6
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* @returns {Function} Express middleware function
|
|
11
|
-
* @example
|
|
12
|
-
* const azifyMiddleware = require('azify-logger/middleware-express');
|
|
13
|
-
* app.use(azifyMiddleware({ serviceName: 'my-app' }));
|
|
14
|
-
*/
|
|
15
|
-
function createExpressLoggingMiddleware(options = {}) {
|
|
16
|
-
const config = {
|
|
17
|
-
serviceName: options.serviceName || process.env.APP_NAME || 'assemble',
|
|
18
|
-
loggerUrl: options.loggerUrl || process.env.AZIFY_LOGGER_URL || 'http://localhost:3001',
|
|
19
|
-
environment: options.environment || process.env.NODE_ENV || 'development'
|
|
20
|
-
}
|
|
7
|
+
function fastUUID() {
|
|
8
|
+
const timestamp = Date.now().toString(36)
|
|
9
|
+
const randomPart = Math.random().toString(36).substring(2, 15)
|
|
10
|
+
const randomPart2 = Math.random().toString(36).substring(2, 15)
|
|
11
|
+
return `${timestamp}-${randomPart}-${randomPart2}`.substring(0, 36)
|
|
12
|
+
}
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
async function sendLog(level, message, meta = {}) {
|
|
30
|
-
const logData = {
|
|
31
|
-
level,
|
|
32
|
-
message,
|
|
33
|
-
meta: {
|
|
34
|
-
...meta,
|
|
35
|
-
service: {
|
|
36
|
-
name: config.serviceName,
|
|
37
|
-
version: '1.0.0'
|
|
38
|
-
},
|
|
39
|
-
environment: config.environment,
|
|
40
|
-
timestamp: new Date().toISOString(),
|
|
41
|
-
hostname: require('os').hostname()
|
|
42
|
-
}
|
|
43
|
-
}
|
|
14
|
+
function sanitizeTraceHex(value) {
|
|
15
|
+
if (!value || typeof value !== 'string') return null
|
|
16
|
+
const hex = value.replace(/[^0-9a-fA-F]/g, '').toLowerCase()
|
|
17
|
+
if (!hex) return null
|
|
18
|
+
return hex.padEnd(32, '0').slice(0, 32)
|
|
19
|
+
}
|
|
44
20
|
|
|
21
|
+
function safeSerializeBody(payload) {
|
|
22
|
+
if (payload == null) return ''
|
|
23
|
+
if (typeof payload === 'string') return payload
|
|
24
|
+
if (Buffer.isBuffer(payload)) return payload.toString('utf8')
|
|
25
|
+
try {
|
|
26
|
+
return JSON.stringify(payload)
|
|
27
|
+
} catch (err) {
|
|
45
28
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Erro ao enviar log:', error.message)
|
|
29
|
+
return String(payload)
|
|
30
|
+
} catch {
|
|
31
|
+
return ''
|
|
51
32
|
}
|
|
52
33
|
}
|
|
34
|
+
}
|
|
53
35
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
36
|
+
function normalizeChunkToBuffer(chunk) {
|
|
37
|
+
if (chunk == null) return null
|
|
38
|
+
if (Buffer.isBuffer(chunk)) return chunk
|
|
39
|
+
if (typeof chunk === 'string') return Buffer.from(chunk)
|
|
40
|
+
if (chunk && chunk.type === 'Buffer' && Array.isArray(chunk.data)) {
|
|
41
|
+
return Buffer.from(chunk.data)
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
return Buffer.from(String(chunk))
|
|
45
|
+
} catch {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const HEADER_WHITELIST = new Set([
|
|
51
|
+
'content-type',
|
|
52
|
+
'content-length',
|
|
53
|
+
'accept',
|
|
54
|
+
'accept-encoding',
|
|
55
|
+
'user-agent',
|
|
56
|
+
'host',
|
|
57
|
+
'x-request-id',
|
|
58
|
+
'x-trace-id',
|
|
59
|
+
'x-span-id',
|
|
60
|
+
'x-parent-span-id'
|
|
61
|
+
])
|
|
62
|
+
|
|
63
|
+
function pickHeaders(source) {
|
|
64
|
+
if (!source || typeof source !== 'object') {
|
|
65
|
+
return {}
|
|
66
|
+
}
|
|
67
|
+
const result = {}
|
|
68
|
+
for (const key in source) {
|
|
69
|
+
const lower = key.toLowerCase()
|
|
70
|
+
if (!HEADER_WHITELIST.has(lower)) continue
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
if (existingTraceId) {
|
|
85
|
-
reqCtx = {
|
|
86
|
-
traceId: existingTraceId,
|
|
87
|
-
spanId: existingSpanId || require('uuid').v4().substring(0, 16),
|
|
88
|
-
parentSpanId: existingParentSpanId,
|
|
89
|
-
requestId: requestId
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
reqCtx = startRequestContext({ requestId })
|
|
93
|
-
}
|
|
72
|
+
if (!Object.prototype.hasOwnProperty.call(source, key)) continue
|
|
94
73
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
log: console.log,
|
|
101
|
-
info: console.info,
|
|
102
|
-
warn: console.warn,
|
|
103
|
-
error: console.error
|
|
74
|
+
const value = source[key]
|
|
75
|
+
if (Array.isArray(value)) {
|
|
76
|
+
result[key] = value.map(String)
|
|
77
|
+
} else if (value != null) {
|
|
78
|
+
result[key] = String(value)
|
|
104
79
|
}
|
|
80
|
+
}
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
84
|
+
function createExpressLoggingMiddleware(options = {}) {
|
|
85
|
+
const config = {
|
|
86
|
+
serviceName: options.serviceName || process.env.APP_NAME,
|
|
87
|
+
loggerUrl: options.loggerUrl || process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log',
|
|
88
|
+
environment: options.environment || process.env.NODE_ENV,
|
|
89
|
+
captureResponseBody: options.captureResponseBody !== false && process.env.AZIFY_LOGGER_CAPTURE_RESPONSE_BODY !== 'false',
|
|
90
|
+
captureRequestBody: options.captureRequestBody !== false && process.env.AZIFY_LOGGER_CAPTURE_REQUEST_BODY !== 'false',
|
|
91
|
+
logRequest: options.logRequest !== false && process.env.AZIFY_LOGGER_LOG_REQUEST !== 'false',
|
|
92
|
+
captureHeaders: options.captureHeaders !== undefined ? options.captureHeaders : process.env.AZIFY_LOGGER_CAPTURE_HEADERS === 'true'
|
|
93
|
+
}
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
const message = args.map(String).join(' ')
|
|
118
|
-
sendLog('info', message, {
|
|
119
|
-
traceId: requestTraceId,
|
|
120
|
-
spanId: requestSpanId,
|
|
121
|
-
parentSpanId: requestParentSpanId,
|
|
122
|
-
requestId: requestId
|
|
123
|
-
})
|
|
124
|
-
}
|
|
95
|
+
const transport = createHttpLoggerTransport(config.loggerUrl, {})
|
|
125
96
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
97
|
+
const hostname = os.hostname()
|
|
98
|
+
|
|
99
|
+
const serviceObj = config.serviceName ? { name: config.serviceName, version: '1.0.0' } : null
|
|
100
|
+
|
|
101
|
+
const workerPool = []
|
|
102
|
+
const cpuCount = Math.max(1, (os.cpus() || []).length || 1)
|
|
103
|
+
const WORKER_POOL_SIZE = Math.min(8, Math.max(2, cpuCount))
|
|
104
|
+
const maxWorkers = Math.min(WORKER_POOL_SIZE, cpuCount)
|
|
105
|
+
let workerIndex = 0
|
|
106
|
+
let poolInitialized = false
|
|
107
|
+
|
|
108
|
+
function initWorkerPool() {
|
|
109
|
+
try {
|
|
110
|
+
const workerPath = path.join(__dirname, 'utils', 'bodyWorker.js')
|
|
111
|
+
for (let i = 0; i < maxWorkers; i++) {
|
|
112
|
+
try {
|
|
113
|
+
const worker = new Worker(workerPath)
|
|
114
|
+
workerPool.push(worker)
|
|
115
|
+
} catch (err) {
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
134
120
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
parentSpanId: requestParentSpanId,
|
|
142
|
-
requestId: requestId
|
|
143
|
-
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function ensureWorkerPool() {
|
|
124
|
+
if (!poolInitialized && (config.captureResponseBody || config.captureRequestBody)) {
|
|
125
|
+
poolInitialized = true
|
|
126
|
+
initWorkerPool()
|
|
144
127
|
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getWorker() {
|
|
131
|
+
if (workerPool.length === 0) return null
|
|
132
|
+
const worker = workerPool[workerIndex % workerPool.length]
|
|
133
|
+
workerIndex++
|
|
134
|
+
return worker
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function sendLog(level, message, meta = {}) {
|
|
138
|
+
if (!transport || typeof transport.enqueue !== 'function') return
|
|
145
139
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
transport.enqueue({
|
|
141
|
+
level,
|
|
142
|
+
message,
|
|
143
|
+
meta
|
|
144
|
+
}, { 'content-type': 'application/json' })
|
|
145
|
+
}
|
|
152
146
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
147
|
+
return function azifyExpressLoggingMiddleware(req, res, next) {
|
|
148
|
+
const startTime = Date.now()
|
|
149
|
+
const method = req.method
|
|
150
|
+
const url = req.url
|
|
151
|
+
const path = req.path || url
|
|
152
|
+
|
|
153
|
+
let responseChunk = null
|
|
154
|
+
let responseChunkCaptured = false
|
|
155
|
+
let logSent = false
|
|
156
|
+
|
|
157
|
+
let requestId, traceId, spanId, parentSpanId, clientIp, query, cachedHeaders
|
|
158
|
+
let idsCreated = false
|
|
159
|
+
let headersCached = false
|
|
160
|
+
let reqCtx = null
|
|
161
|
+
|
|
162
|
+
next()
|
|
163
|
+
|
|
164
|
+
const logWithContext = (level, message, meta) => {
|
|
165
|
+
if (!reqCtx) {
|
|
166
|
+
const traceHex = sanitizeTraceHex(req.headers['x-trace-id'])
|
|
167
|
+
reqCtx = startRequestContext({
|
|
168
|
+
requestId: requestId || req.requestId || fastUUID(),
|
|
169
|
+
traceHex,
|
|
170
|
+
parentSpanId: req.headers['x-parent-span-id'] || null
|
|
171
|
+
})
|
|
161
172
|
}
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
meta.traceId = meta.traceId || reqCtx.traceId
|
|
174
|
+
meta.spanId = meta.spanId || reqCtx.spanId
|
|
175
|
+
meta.parentSpanId = meta.parentSpanId || reqCtx.parentSpanId
|
|
176
|
+
sendLog(level, message, meta)
|
|
164
177
|
}
|
|
165
|
-
|
|
166
|
-
function
|
|
167
|
-
if (
|
|
178
|
+
|
|
179
|
+
function ensureIds() {
|
|
180
|
+
if (idsCreated) return
|
|
181
|
+
idsCreated = true
|
|
168
182
|
|
|
169
|
-
|
|
170
|
-
const sensitiveFields = ['password', 'token', 'secret', 'apiKey', 'api_key', 'accessToken', 'access_token', 'refreshToken', 'refresh_token', 'clientSecret', 'client_secret']
|
|
183
|
+
query = req.query || null
|
|
171
184
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
if (!reqCtx) {
|
|
186
|
+
const traceHex = sanitizeTraceHex(req.headers['x-trace-id'])
|
|
187
|
+
reqCtx = startRequestContext({
|
|
188
|
+
requestId: req.requestId || fastUUID(),
|
|
189
|
+
traceHex,
|
|
190
|
+
parentSpanId: req.headers['x-parent-span-id'] || null
|
|
191
|
+
})
|
|
176
192
|
}
|
|
177
193
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
baseUrl: baseUrl,
|
|
186
|
-
path: req.url,
|
|
187
|
-
headers: sanitizeHeaders(req.headers || {}),
|
|
188
|
-
query: req.query || {},
|
|
189
|
-
userAgent: (req.headers && req.headers['user-agent']) || 'unknown',
|
|
190
|
-
ip: (req.connection && req.connection.remoteAddress) || (req.socket && req.socket.remoteAddress) || req.ip || 'unknown',
|
|
191
|
-
traceId: requestTraceId,
|
|
192
|
-
spanId: requestSpanId,
|
|
193
|
-
parentSpanId: requestParentSpanId
|
|
194
|
+
requestId = reqCtx.requestId || req.requestId || fastUUID()
|
|
195
|
+
traceId = reqCtx.traceId
|
|
196
|
+
spanId = reqCtx.spanId
|
|
197
|
+
parentSpanId = reqCtx.parentSpanId || req.headers['x-parent-span-id'] || null
|
|
198
|
+
|
|
199
|
+
clientIp = req.ip || req.connection?.remoteAddress || req.socket?.remoteAddress || 'unknown'
|
|
200
|
+
req.requestId = requestId
|
|
194
201
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
202
|
+
|
|
203
|
+
function ensureHeaders() {
|
|
204
|
+
if (headersCached) return
|
|
205
|
+
headersCached = true
|
|
206
|
+
if (config.captureHeaders) {
|
|
207
|
+
cachedHeaders = pickHeaders(req.headers || {})
|
|
208
|
+
} else {
|
|
209
|
+
cachedHeaders = {}
|
|
201
210
|
}
|
|
202
|
-
sendLog('info', `[REQUEST] ${req.method} ${req.url}`, req._azifyRequestData)
|
|
203
211
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (!
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
|
|
213
|
+
function emitResponseLog(meta, chunk) {
|
|
214
|
+
if (!config.captureResponseBody || chunk == null) {
|
|
215
|
+
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
216
|
+
return
|
|
209
217
|
}
|
|
210
|
-
console.log = originalConsole.log
|
|
211
|
-
console.info = originalConsole.info
|
|
212
|
-
console.warn = originalConsole.warn
|
|
213
|
-
console.error = originalConsole.error
|
|
214
|
-
})
|
|
215
218
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
console.log = originalConsole.log
|
|
222
|
-
console.info = originalConsole.info
|
|
223
|
-
console.warn = originalConsole.warn
|
|
224
|
-
console.error = originalConsole.error
|
|
225
|
-
})
|
|
219
|
+
if (!meta.response) meta.response = {}
|
|
220
|
+
meta.response.body = safeSerializeBody(chunk)
|
|
221
|
+
logWithContext('info', `[RESPONSE] ${method} ${url}`, meta)
|
|
222
|
+
}
|
|
226
223
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (originalSend) {
|
|
232
|
-
res.send = function patchedSend() {
|
|
233
|
-
try {
|
|
234
|
-
if (arguments.length === 1) {
|
|
235
|
-
sentBody = arguments[0]
|
|
236
|
-
} else if (arguments.length >= 2) {
|
|
237
|
-
sentBody = typeof arguments[0] === 'number' ? arguments[1] : (arguments[1] || arguments[0])
|
|
238
|
-
}
|
|
239
|
-
} catch (_) {}
|
|
224
|
+
if (config.logRequest) {
|
|
225
|
+
process.nextTick(() => {
|
|
226
|
+
ensureIds()
|
|
227
|
+
ensureHeaders()
|
|
240
228
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
229
|
+
const request = {
|
|
230
|
+
id: requestId,
|
|
231
|
+
method,
|
|
232
|
+
url,
|
|
233
|
+
path,
|
|
234
|
+
ip: clientIp
|
|
244
235
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const originalStatus = res.status
|
|
251
|
-
res.status = function(code) {
|
|
252
|
-
res._actualStatusCode = code
|
|
253
|
-
return originalStatus.call(this, code)
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const originalWriteHead = res.writeHead
|
|
257
|
-
res.writeHead = function(statusCode, statusMessage, headers) {
|
|
258
|
-
res._actualStatusCode = statusCode
|
|
259
|
-
if (typeof statusMessage === 'object') {
|
|
260
|
-
headers = statusMessage
|
|
261
|
-
statusMessage = undefined
|
|
262
|
-
}
|
|
263
|
-
return originalWriteHead.call(this, statusCode, statusMessage, headers)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const originalJsonMethod = res.json
|
|
267
|
-
res.json = function(code, body) {
|
|
268
|
-
try {
|
|
269
|
-
if (arguments.length === 1) {
|
|
270
|
-
sentBody = arguments[0]
|
|
271
|
-
} else if (arguments.length >= 2) {
|
|
272
|
-
sentBody = typeof arguments[0] === 'number' ? arguments[1] : (arguments[1] || arguments[0])
|
|
236
|
+
if (query) request.query = query
|
|
237
|
+
if (config.captureHeaders && cachedHeaders) request.headers = cachedHeaders
|
|
238
|
+
if (config.captureRequestBody && req.body !== undefined && req.body != null) {
|
|
239
|
+
request.body = safeSerializeBody(req.body)
|
|
273
240
|
}
|
|
274
|
-
} catch (_) {}
|
|
275
|
-
|
|
276
|
-
if (typeof code === 'number') {
|
|
277
|
-
res._actualStatusCode = code
|
|
278
|
-
} else {
|
|
279
|
-
const errorObj = arguments.length === 1 ? arguments[0] : (typeof arguments[0] === 'number' ? arguments[1] : arguments[0])
|
|
280
241
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
} else if (errorName.includes('Unauthorized')) {
|
|
290
|
-
res._actualStatusCode = 401
|
|
291
|
-
} else if (errorName.includes('Forbidden')) {
|
|
292
|
-
res._actualStatusCode = 403
|
|
293
|
-
} else {
|
|
294
|
-
res._actualStatusCode = 500
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
res._actualStatusCode = res.statusCode || 200
|
|
242
|
+
const meta = {
|
|
243
|
+
traceId: reqCtx.traceId,
|
|
244
|
+
spanId: reqCtx.spanId,
|
|
245
|
+
parentSpanId: reqCtx.parentSpanId || null,
|
|
246
|
+
requestId,
|
|
247
|
+
request,
|
|
248
|
+
timestamp: Date.now(),
|
|
249
|
+
hostname
|
|
298
250
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return originalJsonMethod.apply(this, arguments)
|
|
251
|
+
if (serviceObj) meta.service = serviceObj
|
|
252
|
+
if (config.environment) meta.environment = config.environment
|
|
253
|
+
|
|
254
|
+
logWithContext('info', `[REQUEST] ${method} ${url}`, meta)
|
|
255
|
+
})
|
|
307
256
|
}
|
|
257
|
+
|
|
258
|
+
const originalEnd = res.end.bind(res)
|
|
308
259
|
|
|
309
|
-
res.
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
responseLogged = true
|
|
260
|
+
res.end = (chunk, encoding) => {
|
|
261
|
+
if (logSent) {
|
|
262
|
+
return originalEnd(chunk, encoding)
|
|
313
263
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const originalEnd = res.end
|
|
317
|
-
res.end = function(chunk, encoding) {
|
|
318
|
-
const duration = Date.now() - startTime
|
|
264
|
+
logSent = true
|
|
319
265
|
|
|
320
|
-
|
|
321
|
-
try {
|
|
322
|
-
if (responseBody == null && chunk) {
|
|
323
|
-
if (Buffer.isBuffer(chunk)) {
|
|
324
|
-
responseBody = chunk.toString('utf8')
|
|
325
|
-
} else if (typeof chunk === 'string') {
|
|
326
|
-
responseBody = chunk
|
|
327
|
-
} else {
|
|
328
|
-
responseBody = JSON.stringify(chunk)
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
} catch (_) {}
|
|
266
|
+
const result = originalEnd(chunk, encoding)
|
|
332
267
|
|
|
333
|
-
if (!
|
|
334
|
-
|
|
335
|
-
|
|
268
|
+
if (chunk != null && config.captureResponseBody && !responseChunkCaptured) {
|
|
269
|
+
responseChunk = chunk
|
|
270
|
+
responseChunkCaptured = true
|
|
336
271
|
}
|
|
337
|
-
|
|
338
|
-
originalEnd.call(this, chunk, encoding)
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Logs the response data to azify-logger
|
|
343
|
-
* @private
|
|
344
|
-
*/
|
|
345
|
-
function logResponse() {
|
|
346
|
-
const duration = Date.now() - startTime
|
|
347
272
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
if (responseBody.toJSON && typeof responseBody.toJSON === 'function') {
|
|
358
|
-
serializedResponseBody = JSON.stringify(responseBody.toJSON())
|
|
359
|
-
} else if (responseBody.toString && typeof responseBody.toString === 'function' && responseBody.toString() !== '[object Object]') {
|
|
360
|
-
serializedResponseBody = responseBody.toString()
|
|
361
|
-
} else {
|
|
362
|
-
serializedResponseBody = JSON.stringify(responseBody, (key, value) => {
|
|
363
|
-
if (typeof value === 'function') {
|
|
364
|
-
return '[Function]'
|
|
365
|
-
}
|
|
366
|
-
if (value instanceof Error) {
|
|
367
|
-
return { name: value.name, message: value.message, stack: value.stack }
|
|
368
|
-
}
|
|
369
|
-
return value
|
|
370
|
-
}, null, 0)
|
|
371
|
-
}
|
|
372
|
-
} else {
|
|
373
|
-
serializedResponseBody = responseBody != null ? String(responseBody) : ''
|
|
273
|
+
process.nextTick(() => {
|
|
274
|
+
ensureIds()
|
|
275
|
+
|
|
276
|
+
const statusCode = res.statusCode || 200
|
|
277
|
+
const duration = Date.now() - startTime
|
|
278
|
+
const response = { statusCode, durationMs: duration }
|
|
279
|
+
|
|
280
|
+
if (config.captureHeaders) {
|
|
281
|
+
ensureHeaders()
|
|
374
282
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
283
|
+
|
|
284
|
+
const requestObj = {
|
|
285
|
+
id: requestId,
|
|
286
|
+
method,
|
|
287
|
+
url,
|
|
288
|
+
path,
|
|
289
|
+
ip: clientIp
|
|
380
290
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
sendLog('info', responseMessage, responseData)
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
req._azifyContext = reqCtx
|
|
403
|
-
|
|
404
|
-
try {
|
|
405
|
-
runWithRequestContext(reqCtx, () => {
|
|
406
|
-
next()
|
|
291
|
+
if (query) requestObj.query = query
|
|
292
|
+
if (config.captureHeaders && cachedHeaders) requestObj.headers = cachedHeaders
|
|
293
|
+
|
|
294
|
+
const meta = {
|
|
295
|
+
traceId: reqCtx.traceId,
|
|
296
|
+
spanId: reqCtx.spanId,
|
|
297
|
+
parentSpanId: reqCtx.parentSpanId || null,
|
|
298
|
+
requestId,
|
|
299
|
+
request: requestObj,
|
|
300
|
+
response,
|
|
301
|
+
timestamp: Date.now(),
|
|
302
|
+
hostname
|
|
303
|
+
}
|
|
304
|
+
if (serviceObj) meta.service = serviceObj
|
|
305
|
+
if (config.environment) meta.environment = config.environment
|
|
306
|
+
|
|
307
|
+
const chunkToProcess = (responseChunk !== null && responseChunkCaptured) ? responseChunk : null
|
|
308
|
+
emitResponseLog(meta, chunkToProcess)
|
|
407
309
|
})
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
next()
|
|
310
|
+
|
|
311
|
+
return result
|
|
411
312
|
}
|
|
412
313
|
}
|
|
413
314
|
}
|