azify-logger 1.0.2

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.
@@ -0,0 +1,280 @@
1
+ const axios = require('axios')
2
+ const { als, runWithRequestContext, startRequestContext, getRequestContext } = require('./store')
3
+
4
+ function createRestifyLoggingMiddleware(options = {}) {
5
+ const config = {
6
+ serviceName: options.serviceName || process.env.APP_NAME || 'azipay',
7
+ loggerUrl: options.loggerUrl || process.env.AZIFY_LOGGER_URL || 'http://localhost:3000',
8
+ environment: options.environment || process.env.NODE_ENV || 'development'
9
+ }
10
+
11
+ async function sendLog(level, message, meta = {}) {
12
+ const logData = {
13
+ level,
14
+ message,
15
+ meta: {
16
+ ...meta,
17
+ service: {
18
+ name: config.serviceName,
19
+ version: '1.0.0'
20
+ },
21
+ environment: config.environment,
22
+ timestamp: new Date().toISOString(),
23
+ hostname: require('os').hostname()
24
+ }
25
+ }
26
+
27
+ try {
28
+ await axios.post(`${config.loggerUrl}/log`, logData, {
29
+ timeout: 5000
30
+ })
31
+ } catch (error) {}
32
+ }
33
+
34
+ return function azifyLoggingMiddleware(req, res, next) {
35
+ const startTime = Date.now()
36
+ const requestId = req.requestId || require('uuid').v4()
37
+ const middlewareId = require('uuid').v4().substring(0, 8)
38
+
39
+ const reqCtx = startRequestContext({ requestId })
40
+ req.__azifyTraceId = reqCtx.traceId
41
+ req.__azifySpanId = reqCtx.spanId
42
+ req.__azifyParentSpanId = reqCtx.parentSpanId
43
+
44
+ const requestTraceId = req.__azifyTraceId
45
+ const requestSpanId = req.__azifySpanId
46
+ const requestParentSpanId = req.__azifyParentSpanId
47
+
48
+ let baseUrl = req.url
49
+ if (baseUrl.includes('?')) {
50
+ baseUrl = baseUrl.substring(0, baseUrl.indexOf('?'))
51
+ }
52
+ baseUrl = baseUrl.replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '/{id}')
53
+ baseUrl = baseUrl.replace(/\/[0-9]+/g, '/{id}')
54
+
55
+ req._azifyRequestData = {
56
+ requestId,
57
+ method: req.method,
58
+ url: req.url,
59
+ baseUrl: baseUrl,
60
+ path: req.url,
61
+ headers: req.headers,
62
+ query: req.query,
63
+ userAgent: req.headers['user-agent'],
64
+ ip: req.connection.remoteAddress || req.socket.remoteAddress,
65
+ traceId: requestTraceId,
66
+ spanId: requestSpanId,
67
+ parentSpanId: requestParentSpanId
68
+ }
69
+
70
+ if (req.method === 'GET') {
71
+ sendLog('info', `[REQUEST] ${req.method} ${req.url}`, req._azifyRequestData)
72
+ } else {
73
+ if (req.body !== undefined) {
74
+ req._azifyRequestData.requestBody = req.body
75
+ }
76
+ sendLog('info', `[REQUEST] ${req.method} ${req.url}`, req._azifyRequestData)
77
+ }
78
+
79
+ res.on('finish', () => {
80
+ if (!responseLogged) {
81
+ logResponse()
82
+ responseLogged = true
83
+ }
84
+ })
85
+
86
+ res.on('close', () => {
87
+ if (!responseLogged) {
88
+ logResponse()
89
+ responseLogged = true
90
+ }
91
+ })
92
+
93
+ let sentBody
94
+ let responseLogged = false
95
+
96
+ const originalSend = res.send && res.send.bind(res)
97
+ if (originalSend) {
98
+ res.send = function patchedSend() {
99
+ try {
100
+ if (arguments.length === 1) {
101
+ sentBody = arguments[0]
102
+ } else if (arguments.length >= 2) {
103
+ sentBody = typeof arguments[0] === 'number' ? arguments[1] : (arguments[1] || arguments[0])
104
+ }
105
+ } catch (_) {}
106
+
107
+ if (!responseLogged) {
108
+ logResponse()
109
+ responseLogged = true
110
+ }
111
+
112
+ return originalSend.apply(this, arguments)
113
+ }
114
+ }
115
+
116
+
117
+ const originalStatus = res.status
118
+ res.status = function(code) {
119
+ res._actualStatusCode = code
120
+ return originalStatus.call(this, code)
121
+ }
122
+
123
+ const originalWriteHead = res.writeHead
124
+ res.writeHead = function(statusCode, statusMessage, headers) {
125
+ res._actualStatusCode = statusCode
126
+ if (typeof statusMessage === 'object') {
127
+ headers = statusMessage
128
+ statusMessage = undefined
129
+ }
130
+ return originalWriteHead.call(this, statusCode, statusMessage, headers)
131
+ }
132
+
133
+ const originalJsonMethod = res.json
134
+ res.json = function(code, body) {
135
+ try {
136
+ if (arguments.length === 1) {
137
+ sentBody = arguments[0]
138
+ } else if (arguments.length >= 2) {
139
+ sentBody = typeof arguments[0] === 'number' ? arguments[1] : (arguments[1] || arguments[0])
140
+ }
141
+ } catch (_) {}
142
+
143
+ if (typeof code === 'number') {
144
+ res._actualStatusCode = code
145
+ } else {
146
+ const errorObj = arguments.length === 1 ? arguments[0] : (typeof arguments[0] === 'number' ? arguments[1] : arguments[0])
147
+
148
+ if (errorObj && errorObj.constructor && errorObj.constructor.name === 'ErrCtor') {
149
+ const errorName = errorObj.toString()
150
+ if (errorName.includes('InternalServerError') || errorName.includes('InternalError')) {
151
+ res._actualStatusCode = 500
152
+ res._actualStatusCode = 500
153
+ } else if (errorName.includes('BadRequest') || errorName.includes('BadDigest')) {
154
+ res._actualStatusCode = 400
155
+ } else if (errorName.includes('NotFound')) {
156
+ res._actualStatusCode = 404
157
+ } else if (errorName.includes('Unauthorized')) {
158
+ res._actualStatusCode = 401
159
+ } else if (errorName.includes('Forbidden')) {
160
+ res._actualStatusCode = 403
161
+ } else {
162
+ res._actualStatusCode = 500
163
+ }
164
+ } else {
165
+ res._actualStatusCode = res.statusCode || 200
166
+ }
167
+ }
168
+
169
+ if (!responseLogged) {
170
+ logResponse()
171
+ responseLogged = true
172
+ }
173
+
174
+ return originalJsonMethod.apply(this, arguments)
175
+ }
176
+
177
+ res.on('finish', function() {
178
+ if (!responseLogged) {
179
+ logResponse()
180
+ responseLogged = true
181
+ }
182
+ })
183
+
184
+ const originalEnd = res.end
185
+ res.end = function(chunk, encoding) {
186
+ const duration = Date.now() - startTime
187
+
188
+ let responseBody = sentBody
189
+ try {
190
+ if (responseBody == null && chunk) {
191
+ if (Buffer.isBuffer(chunk)) {
192
+ responseBody = chunk.toString('utf8')
193
+ } else if (typeof chunk === 'string') {
194
+ responseBody = chunk
195
+ } else {
196
+ responseBody = JSON.stringify(chunk)
197
+ }
198
+ }
199
+ } catch (_) {}
200
+
201
+ if (!responseLogged) {
202
+ logResponse()
203
+ responseLogged = true
204
+ }
205
+
206
+ originalEnd.call(this, chunk, encoding)
207
+ }
208
+
209
+ function logResponse() {
210
+ const duration = Date.now() - startTime
211
+
212
+ let responseBody = sentBody
213
+
214
+ let serializedResponseBody
215
+ try {
216
+ if (typeof responseBody === 'string') {
217
+ serializedResponseBody = responseBody
218
+ } else if (Array.isArray(responseBody)) {
219
+ serializedResponseBody = JSON.stringify(responseBody)
220
+ } else if (responseBody && typeof responseBody === 'object') {
221
+ if (responseBody.toJSON && typeof responseBody.toJSON === 'function') {
222
+ serializedResponseBody = JSON.stringify(responseBody.toJSON())
223
+ } else if (responseBody.toString && typeof responseBody.toString === 'function' && responseBody.toString() !== '[object Object]') {
224
+ serializedResponseBody = responseBody.toString()
225
+ } else {
226
+ serializedResponseBody = JSON.stringify(responseBody, (key, value) => {
227
+ if (typeof value === 'function') {
228
+ return '[Function]'
229
+ }
230
+ if (value instanceof Error) {
231
+ return { name: value.name, message: value.message, stack: value.stack }
232
+ }
233
+ return value
234
+ }, null, 0)
235
+ }
236
+ } else {
237
+ serializedResponseBody = responseBody != null ? String(responseBody) : ''
238
+ }
239
+ } catch (error) {
240
+ try {
241
+ serializedResponseBody = JSON.stringify(responseBody, null, 2)
242
+ } catch (secondError) {
243
+ serializedResponseBody = '[Complex object - serialization failed]'
244
+ }
245
+ }
246
+
247
+ const statusCode = res._actualStatusCode || res._statusCode || res.statusCode || 200
248
+
249
+ const responseMessage = serializedResponseBody && serializedResponseBody.length > 0
250
+ ? `[RESPONSE] ${serializedResponseBody}`
251
+ : `[RESPONSE] ${req.method} ${req.url} ${statusCode} ${duration}ms`
252
+
253
+ const responseData = {
254
+ ...req._azifyRequestData,
255
+ requestBody: req.body,
256
+ statusCode: statusCode,
257
+ responseTime: duration,
258
+ responseHeaders: res.getHeaders ? res.getHeaders() : {},
259
+ responseBody: serializedResponseBody
260
+ }
261
+
262
+ try { res._azifyResponseLogged = true } catch (_) {}
263
+ sendLog('info', responseMessage, responseData)
264
+ }
265
+
266
+ req._azifyContext = reqCtx
267
+
268
+ try {
269
+ runWithRequestContext(reqCtx, () => {
270
+ next()
271
+ })
272
+ } catch (error) {
273
+ console.error('Error in azify middleware:', error)
274
+ next()
275
+ }
276
+ }
277
+ }
278
+
279
+ module.exports = createRestifyLoggingMiddleware
280
+
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "azify-logger",
3
+ "version": "1.0.2",
4
+ "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
+ "main": "index.js",
6
+ "type": "commonjs",
7
+ "scripts": {
8
+ "test": "echo \"No tests specified\" && exit 0"
9
+ },
10
+ "keywords": [
11
+ "logging",
12
+ "opensearch",
13
+ "centralized",
14
+ "azify"
15
+ ],
16
+ "author": "Azify",
17
+ "license": "MIT",
18
+ "dependencies": {
19
+ "@opentelemetry/api": "^1.7.0",
20
+ "@opentelemetry/auto-instrumentations-node": "^0.40.0",
21
+ "@opentelemetry/core": "^1.28.0",
22
+ "@opentelemetry/exporter-trace-otlp-http": "^0.45.0",
23
+ "@opentelemetry/resources": "^1.28.0",
24
+ "@opentelemetry/sdk-node": "^0.45.0",
25
+ "@opentelemetry/semantic-conventions": "^1.28.0",
26
+ "axios": "^1.6.0",
27
+ "cors": "^2.8.5",
28
+ "express": "^4.18.2",
29
+ "require-in-the-middle": "^7.4.0",
30
+ "uuid": "^9.0.1"
31
+ },
32
+ "engines": {
33
+ "node": ">=16.0.0"
34
+ },
35
+ "files": [
36
+ "index.js",
37
+ "store.js",
38
+ "register-otel.js",
39
+ "register-restify.js",
40
+ "middleware-restify.js",
41
+ "middleware-express.js",
42
+ "server.js",
43
+ "streams/",
44
+ "package.json",
45
+ "README.md"
46
+ ]
47
+ }
@@ -0,0 +1,39 @@
1
+ try {
2
+ const { NodeSDK } = require('@opentelemetry/sdk-node')
3
+ const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
4
+
5
+ const serviceName = process.env.OTEL_SERVICE_NAME || process.env.APP_NAME || 'app'
6
+ const serviceVersion = process.env.OTEL_SERVICE_VERSION || '1.0.0'
7
+
8
+ process.env.OTEL_SERVICE_NAME = serviceName
9
+ process.env.OTEL_SERVICE_VERSION = serviceVersion
10
+
11
+ console.log(`🔧 Initializing OpenTelemetry for service: ${serviceName}`)
12
+
13
+ const sdk = new NodeSDK({
14
+ serviceName: serviceName,
15
+ serviceVersion: serviceVersion,
16
+ instrumentations: [getNodeAutoInstrumentations({
17
+ '@opentelemetry/instrumentation-http': {
18
+ enabled: true,
19
+ requireParentforOutgoingSpans: false,
20
+ requireParentforIncomingSpans: false
21
+ },
22
+ '@opentelemetry/instrumentation-express': { enabled: true },
23
+ '@opentelemetry/instrumentation-restify': { enabled: true }
24
+ })]
25
+ })
26
+
27
+ sdk.start()
28
+ console.log(`✅ OpenTelemetry initialized for service: ${serviceName}`)
29
+
30
+ process.once('SIGTERM', () => {
31
+ sdk.shutdown()
32
+ .then(() => console.log('OpenTelemetry shut down successfully'))
33
+ .catch((error) => console.error('Error shutting down OpenTelemetry:', error))
34
+ .finally(() => process.exit(0))
35
+ })
36
+ } catch (error) {
37
+ console.warn('⚠️ OpenTelemetry não pôde ser inicializado:', error.message)
38
+ console.error('Error details:', error)
39
+ }
@@ -0,0 +1,203 @@
1
+ console.log('🔧 @azify/logger-client/register-restify carregando...')
2
+
3
+ function generateTraceId() {
4
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + Date.now().toString(36)
5
+ }
6
+
7
+ function generateSpanId() {
8
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 7)
9
+ }
10
+
11
+ const Module = require('module')
12
+ const originalRequire = Module.prototype.require
13
+
14
+ let patched = false
15
+
16
+ Module.prototype.require = function(id) {
17
+ const module = originalRequire.apply(this, arguments)
18
+
19
+ if (id && typeof id === 'string' && id.includes('restify')) {
20
+ }
21
+
22
+ if (!patched && (id === 'restify' || id.endsWith('/restify') || id.includes('restify/lib'))) {
23
+ patched = true
24
+ console.log(' ✅ Restify detectado! Aplicando patches...')
25
+
26
+ try {
27
+ let trace, context
28
+ try {
29
+ const otel = require('@opentelemetry/api')
30
+ trace = otel.trace
31
+ context = otel.context
32
+ } catch (_) {}
33
+
34
+ const ServerProto = module.Server && module.Server.prototype
35
+
36
+ if (!ServerProto) return module
37
+
38
+ function wrapAsyncMiddleware(handler) {
39
+ const isAsync = handler && handler.constructor && handler.constructor.name === 'AsyncFunction'
40
+ const takesThree = typeof handler === 'function' && handler.length === 3
41
+
42
+ if (isAsync && takesThree) {
43
+ return function wrappedAsyncMiddleware(req, res, next) {
44
+ try {
45
+ const promise = handler(req, res, next)
46
+ if (promise && typeof promise.then === 'function') {
47
+ promise.catch(next)
48
+ }
49
+ } catch (err) {
50
+ next(err)
51
+ }
52
+ }
53
+ }
54
+ return handler
55
+ }
56
+
57
+ if (ServerProto && !ServerProto.__azifyPatchedUse) {
58
+ const originalUse = ServerProto.use
59
+ ServerProto.use = function patchedUse(...handlers) {
60
+ const patched = handlers.map(h => wrapAsyncMiddleware(h))
61
+ return originalUse.apply(this, patched)
62
+ }
63
+
64
+ const originalPre = ServerProto.pre
65
+ ServerProto.pre = function patchedPre(...handlers) {
66
+ const patched = handlers.map(h => wrapAsyncMiddleware(h))
67
+ return originalPre.apply(this, patched)
68
+ }
69
+
70
+ ServerProto.__azifyPatchedUse = true
71
+ console.log('✅ Restify 11 async middleware patch aplicado')
72
+ }
73
+
74
+ if (ServerProto && !ServerProto.__azifyReqResLogged) {
75
+ const { createAzifyLoggerFromEnv } = require('./index.js')
76
+ const azifyLogger = createAzifyLoggerFromEnv()
77
+
78
+ ServerProto.use(function azifyAutoRR (req, res, next) {
79
+ if (req.__azifyLogged) return next()
80
+ req.__azifyLogged = true
81
+ const start = Date.now()
82
+
83
+ let traceId, spanId
84
+
85
+ if (trace && context) {
86
+ const span = trace.getSpan(context.active())
87
+ const spanContext = span?.spanContext()
88
+
89
+ if (spanContext) {
90
+ traceId = spanContext.traceId
91
+ spanId = spanContext.spanId
92
+ }
93
+ }
94
+
95
+ if (!traceId) {
96
+ traceId = generateTraceId()
97
+ spanId = generateSpanId()
98
+ }
99
+
100
+ req.__azifyTrace = { traceId, spanId }
101
+
102
+ const requestId = req.requestId || req.id || (req.getId && req.getId())
103
+
104
+ const requestMeta = {
105
+ requestId,
106
+ method: req.method,
107
+ url: req.url,
108
+ path: req.path(),
109
+ headers: req.headers,
110
+ query: req.query,
111
+ params: req.params,
112
+ userAgent: req.headers?.['user-agent'],
113
+ ip: (req.connection && req.connection.remoteAddress) || (req.socket && req.socket.remoteAddress),
114
+ requestBody: req.body,
115
+ traceId,
116
+ spanId
117
+ }
118
+
119
+ azifyLogger.info(`[REQUEST] ${req.method} ${req.url}`, requestMeta)
120
+
121
+ const originalSend = res.send
122
+ const originalJson = res.json
123
+
124
+ function logResponse(methodName, ...args) {
125
+ const statusCode = res.statusCode || (typeof args[0] === 'number' ? args[0] : 200)
126
+ const body = (args.length === 1) ? args[0] : (typeof args[0] === 'number' ? args[1] : (args[1] || args[0]))
127
+
128
+ const { traceId, spanId } = req.__azifyTrace || {}
129
+
130
+ const responseMeta = {
131
+ requestId,
132
+ method: req.method,
133
+ url: req.url,
134
+ statusCode,
135
+ responseTime: Date.now() - start,
136
+ responseBody: body,
137
+ responseHeaders: res.getHeaders?.() || {},
138
+ traceId,
139
+ spanId,
140
+ responseMethod: methodName
141
+ }
142
+
143
+ const message = (typeof body === 'string' && body.length > 0)
144
+ ? body
145
+ : `[RESPONSE] ${req.method} ${req.url} ${statusCode} ${Date.now() - start}ms`
146
+ azifyLogger.info(message, responseMeta)
147
+ }
148
+
149
+ res.send = function wrappedSend (...args) {
150
+ logResponse('send', ...args)
151
+ res.__azifyResponseLogged = true
152
+ return originalSend.apply(this, args)
153
+ }
154
+
155
+ if (originalJson) {
156
+ res.json = function wrappedJson (...args) {
157
+ logResponse('json', ...args)
158
+ res.__azifyResponseLogged = true
159
+ return originalJson.apply(this, args)
160
+ }
161
+ }
162
+
163
+ const originalEnd = res.end
164
+ res.end = function wrappedEnd(chunk, encoding) {
165
+ if (!res.__azifyResponseLogged && chunk) {
166
+ const { traceId, spanId } = req.__azifyTrace || {}
167
+
168
+ const responseMeta = {
169
+ requestId,
170
+ method: req.method,
171
+ url: req.url,
172
+ statusCode: res.statusCode,
173
+ responseTime: Date.now() - start,
174
+ responseBody: chunk,
175
+ responseHeaders: res.getHeaders?.() || {},
176
+ traceId,
177
+ spanId,
178
+ responseMethod: 'end'
179
+ }
180
+
181
+ const message = (typeof chunk === 'string' && chunk.length > 0)
182
+ ? chunk
183
+ : `[RESPONSE] ${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms`
184
+ azifyLogger.info(message, responseMeta)
185
+ res.__azifyResponseLogged = true
186
+ }
187
+
188
+ return originalEnd.call(this, chunk, encoding)
189
+ }
190
+
191
+ next()
192
+ })
193
+ ServerProto.__azifyReqResLogged = true
194
+ }
195
+ } catch (error) {
196
+ console.error('❌ Erro ao aplicar patches no Restify:', error.message)
197
+ }
198
+ }
199
+
200
+ return module
201
+ }
202
+
203
+ console.log(' ✅ Hook de interceptação do Restify registrado!')