azify-logger 1.0.16 → 1.0.18

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 CHANGED
@@ -86,9 +86,7 @@ const app = express()
86
86
  |----------|-----------------------------------|-----------|
87
87
  | `APP_NAME` | - | Nome da aplicação |
88
88
  | `AZIFY_LOGGER_URL` | `http://localhost:3001/log` | URL do logger |
89
- | `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318/v1/traces` | OTLP endpoint |
90
- | `NODE_ENV` | `development` | Ambiente |
91
- | `AZIFY_LOGGER_AUTOREG_DISABLE` | `""` | Se `"1"`, desativa auto-registro do OTEL |
89
+ | `NODE_ENV` | `development` | Ambiente |
92
90
 
93
91
  ### 🌐 URLs por Ambiente
94
92
 
@@ -102,14 +100,14 @@ NODE_ENV=development
102
100
  **Staging:**
103
101
  ```env
104
102
  APP_NAME=minha-app
105
- AZIFY_LOGGER_URL=http://logs.azify.dev
103
+ AZIFY_LOGGER_URL=https://logsdashboard.azify.dev/send
106
104
  NODE_ENV=staging
107
105
  ```
108
106
 
109
107
  **Production:**
110
108
  ```env
111
109
  APP_NAME=minha-app
112
- AZIFY_LOGGER_URL=http://logs.azify.com
110
+ AZIFY_LOGGER_URL=https://logsdashboard.azify.prd/send (a conigurar)
113
111
  NODE_ENV=production
114
112
  ```
115
113
 
@@ -167,6 +165,8 @@ if (createAzifyBunyanStream) {
167
165
  stream: createAzifyBunyanStream({
168
166
  loggerUrl: process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log',
169
167
  serviceName: process.env.APP_NAME || 'app'
168
+ loggerUrl: process.env.AZIFY_LOGGER_URL,
169
+ serviceName: process.env.APP_NAME
170
170
  })
171
171
  })
172
172
  }
@@ -220,7 +220,6 @@ logger.error('Erro', new Error('Falha'), { context: 'payment' })
220
220
 
221
221
  Se você precisa subir a infraestrutura do azify-logger:
222
222
 
223
- ### 1. Iniciar serviços
224
223
 
225
224
  ```bash
226
225
  ./start-docker.sh
package/index.js CHANGED
@@ -1,6 +1,19 @@
1
1
  const axios = require('axios')
2
- const { context, propagation, trace } = require('@opentelemetry/api')
3
- const { W3CTraceContextPropagator } = require('@opentelemetry/core')
2
+
3
+ let context, propagation, trace, W3CTraceContextPropagator
4
+ try {
5
+ const otelApi = require('@opentelemetry/api')
6
+ const otelCore = require('@opentelemetry/core')
7
+ context = otelApi.context
8
+ propagation = otelApi.propagation
9
+ trace = otelApi.trace
10
+ W3CTraceContextPropagator = otelCore.W3CTraceContextPropagator
11
+ } catch (_) {
12
+ context = { active: () => ({}) }
13
+ propagation = { setGlobalPropagator: () => {} }
14
+ trace = { getSpan: () => null }
15
+ W3CTraceContextPropagator = class {}
16
+ }
4
17
 
5
18
  if (process.env.AZIFY_LOGGER_AUTOREG_DISABLE !== '1') {
6
19
  try { require('./register-otel') } catch (_) {}
@@ -41,7 +54,7 @@ class AzifyLogger {
41
54
  */
42
55
  async log(level, message, meta = {}) {
43
56
  const span = trace.getSpan(context.active())
44
- const spanContext = span?.spanContext()
57
+ const spanContext = span && span.spanContext()
45
58
 
46
59
  const logData = {
47
60
  level,
@@ -50,7 +63,7 @@ class AzifyLogger {
50
63
  ...meta,
51
64
  service: {
52
65
  name: this.options.serviceName,
53
- version: meta.service?.version || '1.0.0'
66
+ version: (meta.service && meta.service.version) || '1.0.0'
54
67
  },
55
68
  environment: this.options.environment,
56
69
  timestamp: new Date().toISOString(),
package/init.js ADDED
@@ -0,0 +1,74 @@
1
+ if (process.env.AZIFY_LOGGER_DISABLE === '1') {
2
+ module.exports = {}
3
+ } else {
4
+ const Module = require('module')
5
+ const originalRequire = Module.prototype.require
6
+
7
+ Module.prototype.require = function(id) {
8
+ const result = originalRequire.call(this, id)
9
+
10
+ if (id === '@nestjs/common') {
11
+ if (result && result.Logger) {
12
+ const originalLoggerConstructor = result.Logger
13
+
14
+ function PatchedLogger(context) {
15
+ const instance = new originalLoggerConstructor(context)
16
+
17
+ instance.log = function(message, context) {
18
+ const { getRequestContext } = require('./store')
19
+ const ctx = getRequestContext()
20
+ if (ctx && ctx.traceId) {
21
+ return originalLoggerConstructor.prototype.log.call(this, message, context)
22
+ }
23
+ return originalLoggerConstructor.prototype.log.call(this, message, context)
24
+ }
25
+
26
+ instance.error = function(message, trace, context) {
27
+ const { getRequestContext } = require('./store')
28
+ const ctx = getRequestContext()
29
+ if (ctx && ctx.traceId) {
30
+ return originalLoggerConstructor.prototype.error.call(this, message, trace, context)
31
+ }
32
+ return originalLoggerConstructor.prototype.error.call(this, message, trace, context)
33
+ }
34
+
35
+ instance.warn = function(message, context) {
36
+ const { getRequestContext } = require('./store')
37
+ const ctx = getRequestContext()
38
+ if (ctx && ctx.traceId) {
39
+ return originalLoggerConstructor.prototype.warn.call(this, message, context)
40
+ }
41
+ return originalLoggerConstructor.prototype.warn.call(this, message, context)
42
+ }
43
+
44
+ instance.debug = function(message, context) {
45
+ const { getRequestContext } = require('./store')
46
+ const ctx = getRequestContext()
47
+ if (ctx && ctx.traceId) {
48
+ return originalLoggerConstructor.prototype.debug.call(this, message, context)
49
+ }
50
+ return originalLoggerConstructor.prototype.debug.call(this, message, context)
51
+ }
52
+
53
+ instance.verbose = function(message, context) {
54
+ const { getRequestContext } = require('./store')
55
+ const ctx = getRequestContext()
56
+ if (ctx && ctx.traceId) {
57
+ return originalLoggerConstructor.prototype.verbose.call(this, message, context)
58
+ }
59
+ return originalLoggerConstructor.prototype.verbose.call(this, message, context)
60
+ }
61
+
62
+ return instance
63
+ }
64
+
65
+ Object.setPrototypeOf(PatchedLogger, originalLoggerConstructor)
66
+ Object.assign(PatchedLogger, originalLoggerConstructor)
67
+
68
+ result.Logger = PatchedLogger
69
+ }
70
+ }
71
+
72
+ return result
73
+ }
74
+ }
@@ -186,8 +186,8 @@ function createExpressLoggingMiddleware(options = {}) {
186
186
  path: req.url,
187
187
  headers: sanitizeHeaders(req.headers || {}),
188
188
  query: req.query || {},
189
- userAgent: req.headers?.['user-agent'] || 'unknown',
190
- ip: req.connection?.remoteAddress || req.socket?.remoteAddress || req.ip || 'unknown',
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
191
  traceId: requestTraceId,
192
192
  spanId: requestSpanId,
193
193
  parentSpanId: requestParentSpanId
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azify-logger",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,13 +27,6 @@
27
27
  "author": "Azify",
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
- "@opentelemetry/api": "^1.7.0",
31
- "@opentelemetry/auto-instrumentations-node": "^0.40.0",
32
- "@opentelemetry/core": "^1.28.0",
33
- "@opentelemetry/exporter-trace-otlp-http": "^0.45.0",
34
- "@opentelemetry/resources": "^1.28.0",
35
- "@opentelemetry/sdk-node": "^0.45.0",
36
- "@opentelemetry/semantic-conventions": "^1.28.0",
37
30
  "axios": "^1.6.0",
38
31
  "cors": "^2.8.5",
39
32
  "express": "^4.18.2",
@@ -43,12 +36,23 @@
43
36
  "require-in-the-middle": "^7.4.0",
44
37
  "uuid": "^9.0.1"
45
38
  },
39
+ "optionalDependencies": {
40
+ "@opentelemetry/api": "^1.7.0",
41
+ "@opentelemetry/auto-instrumentations-node": "^0.40.0",
42
+ "@opentelemetry/core": "^1.28.0",
43
+ "@opentelemetry/exporter-trace-otlp-http": "^0.45.0",
44
+ "@opentelemetry/resources": "^1.28.0",
45
+ "@opentelemetry/sdk-node": "^0.45.0",
46
+ "@opentelemetry/semantic-conventions": "^1.28.0"
47
+ },
46
48
  "engines": {
47
- "node": ">=16.0.0"
49
+ "node": ">=12.0.0"
48
50
  },
49
51
  "files": [
50
52
  "index.js",
51
53
  "index.d.ts",
54
+ "init.js",
55
+ "register.js",
52
56
  "store.js",
53
57
  "register-otel.js",
54
58
  "register-restify.js",
package/register.js ADDED
@@ -0,0 +1,456 @@
1
+ if (process.env.AZIFY_LOGGER_DISABLE === '1') {
2
+ module.exports = {}
3
+ } else {
4
+ try {
5
+ const bunyan = require('bunyan')
6
+ const createBunyanStream = require('./streams/bunyan')
7
+ const { getRequestContext } = require('./store')
8
+
9
+ const originalCreate = bunyan.createLogger
10
+ bunyan.createLogger = function patchedCreateLogger(options) {
11
+ const logger = originalCreate.call(bunyan, options)
12
+ try {
13
+ const level = process.env.AZIFY_LOG_LEVEL || (options && options.level) || 'info'
14
+ const loggerUrl = process.env.AZIFY_LOGGER_URL || 'http://localhost:3001'
15
+ const serviceName = process.env.APP_NAME || (options && options.name) || 'app'
16
+ const environment = process.env.NODE_ENV || 'development'
17
+
18
+ logger.addStream({
19
+ level,
20
+ type: 'raw',
21
+ stream: createBunyanStream({ loggerUrl, serviceName, environment })
22
+ })
23
+ } catch (_) {}
24
+ return logger
25
+ }
26
+
27
+ try {
28
+ const Module = require('module')
29
+ const originalRequire = Module.prototype.require
30
+ const { getRequestContext } = require('./store')
31
+
32
+ Module.prototype.require = function(id) {
33
+ const result = originalRequire.call(this, id)
34
+ if (id === 'pino' || id.endsWith('/pino')) {
35
+ if (result && typeof result === 'function') {
36
+ const originalPino = result
37
+
38
+ const patchedPino = function(options, stream) {
39
+ const logger = originalPino(options, stream)
40
+
41
+ const originalInfo = logger.info
42
+ const originalError = logger.error
43
+ const originalWarn = logger.warn
44
+ const originalDebug = logger.debug
45
+ const originalFatal = logger.fatal
46
+ const originalTrace = logger.trace
47
+
48
+ logger.info = function(obj, msg, ...args) {
49
+ const ctx = getRequestContext()
50
+ if (ctx && ctx.traceId) {
51
+ if (typeof obj === 'object' && obj !== null) {
52
+ obj.traceId = ctx.traceId
53
+ obj.spanId = ctx.spanId
54
+ obj.parentSpanId = ctx.parentSpanId
55
+ obj.requestId = ctx.requestId
56
+ } else {
57
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
58
+ }
59
+ }
60
+ return originalInfo.call(this, obj, msg, ...args)
61
+ }
62
+
63
+ logger.error = function(obj, msg, ...args) {
64
+ const ctx = getRequestContext()
65
+ if (ctx && ctx.traceId) {
66
+ if (typeof obj === 'object' && obj !== null) {
67
+ obj.traceId = ctx.traceId
68
+ obj.spanId = ctx.spanId
69
+ obj.parentSpanId = ctx.parentSpanId
70
+ obj.requestId = ctx.requestId
71
+ } else {
72
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
73
+ }
74
+ }
75
+ return originalError.call(this, obj, msg, ...args)
76
+ }
77
+
78
+ logger.warn = function(obj, msg, ...args) {
79
+ const ctx = getRequestContext()
80
+ if (ctx && ctx.traceId) {
81
+ if (typeof obj === 'object' && obj !== null) {
82
+ obj.traceId = ctx.traceId
83
+ obj.spanId = ctx.spanId
84
+ obj.parentSpanId = ctx.parentSpanId
85
+ obj.requestId = ctx.requestId
86
+ } else {
87
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
88
+ }
89
+ }
90
+ return originalWarn.call(this, obj, msg, ...args)
91
+ }
92
+
93
+ logger.debug = function(obj, msg, ...args) {
94
+ const ctx = getRequestContext()
95
+ if (ctx && ctx.traceId) {
96
+ if (typeof obj === 'object' && obj !== null) {
97
+ obj.traceId = ctx.traceId
98
+ obj.spanId = ctx.spanId
99
+ obj.parentSpanId = ctx.parentSpanId
100
+ obj.requestId = ctx.requestId
101
+ } else {
102
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
103
+ }
104
+ }
105
+ return originalDebug.call(this, obj, msg, ...args)
106
+ }
107
+
108
+ logger.fatal = function(obj, msg, ...args) {
109
+ const ctx = getRequestContext()
110
+ if (ctx && ctx.traceId) {
111
+ if (typeof obj === 'object' && obj !== null) {
112
+ obj.traceId = ctx.traceId
113
+ obj.spanId = ctx.spanId
114
+ obj.parentSpanId = ctx.parentSpanId
115
+ obj.requestId = ctx.requestId
116
+ } else {
117
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
118
+ }
119
+ }
120
+ return originalFatal.call(this, obj, msg, ...args)
121
+ }
122
+
123
+ logger.trace = function(obj, msg, ...args) {
124
+ const ctx = getRequestContext()
125
+ if (ctx && ctx.traceId) {
126
+ if (typeof obj === 'object' && obj !== null) {
127
+ obj.traceId = ctx.traceId
128
+ obj.spanId = ctx.spanId
129
+ obj.parentSpanId = ctx.parentSpanId
130
+ obj.requestId = ctx.requestId
131
+ } else {
132
+ obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
133
+ }
134
+ }
135
+ return originalTrace.call(this, obj, msg, ...args)
136
+ }
137
+
138
+ return logger
139
+ }
140
+
141
+ Object.setPrototypeOf(patchedPino, originalPino)
142
+ Object.assign(patchedPino, originalPino)
143
+
144
+ return patchedPino
145
+ }
146
+ }
147
+
148
+ return result
149
+ }
150
+
151
+ } catch (_) {}
152
+
153
+ try {
154
+ const { Logger } = require('@nestjs/common')
155
+ if (Logger) {
156
+ const originalLog = Logger.prototype.log
157
+ const originalError = Logger.prototype.error
158
+ const originalWarn = Logger.prototype.warn
159
+ const originalDebug = Logger.prototype.debug
160
+ const originalVerbose = Logger.prototype.verbose
161
+
162
+ Logger.prototype.log = function(message, context) {
163
+ const { getRequestContext } = require('./store')
164
+ const ctx = getRequestContext()
165
+ if (ctx && ctx.traceId) {
166
+ return originalLog.call(this, message, context)
167
+ }
168
+ return originalLog.call(this, message, context)
169
+ }
170
+
171
+ Logger.prototype.error = function(message, trace, context) {
172
+ const { getRequestContext } = require('./store')
173
+ const ctx = getRequestContext()
174
+ if (ctx && ctx.traceId) {
175
+ return originalError.call(this, message, trace, context)
176
+ }
177
+ return originalError.call(this, message, trace, context)
178
+ }
179
+
180
+ Logger.prototype.warn = function(message, context) {
181
+ const { getRequestContext } = require('./store')
182
+ const ctx = getRequestContext()
183
+ if (ctx && ctx.traceId) {
184
+ return originalWarn.call(this, message, context)
185
+ }
186
+ return originalWarn.call(this, message, context)
187
+ }
188
+
189
+ Logger.prototype.debug = function(message, context) {
190
+ const { getRequestContext } = require('./store')
191
+ const ctx = getRequestContext()
192
+ if (ctx && ctx.traceId) {
193
+ return originalDebug.call(this, message, context)
194
+ }
195
+ return originalDebug.call(this, message, context)
196
+ }
197
+
198
+ Logger.prototype.verbose = function(message, context) {
199
+ const { getRequestContext } = require('./store')
200
+ const ctx = getRequestContext()
201
+ if (ctx && ctx.traceId) {
202
+ return originalVerbose.call(this, message, context)
203
+ }
204
+ return originalVerbose.call(this, message, context)
205
+ }
206
+
207
+ }
208
+ } catch (_) {}
209
+
210
+ try {
211
+ const originalConsoleLog = console.log
212
+ const originalConsoleError = console.error
213
+ const originalConsoleWarn = console.warn
214
+
215
+ console.log = function(...args) {
216
+ return originalConsoleLog.apply(this, args)
217
+ }
218
+
219
+ console.error = function(...args) {
220
+ return originalConsoleError.apply(this, args)
221
+ }
222
+
223
+ console.warn = function(...args) {
224
+ return originalConsoleWarn.apply(this, args)
225
+ }
226
+ } catch (_) {}
227
+
228
+ try {
229
+ const Module = require('module')
230
+ const originalRequire = Module.prototype.require
231
+
232
+ Module.prototype.require = function(id) {
233
+ const result = originalRequire.call(this, id)
234
+
235
+ if (id === 'nestjs-pino' || id.endsWith('/nestjs-pino') || id.includes('nestjs-pino')) {
236
+ if (result && result.Logger) {
237
+ const originalLoggerConstructor = result.Logger
238
+
239
+ function PatchedNestJSPinoLogger(context) {
240
+ const instance = new originalLoggerConstructor(context)
241
+
242
+ instance.error = function(message, trace, context) {
243
+ const { getRequestContext } = require('./store')
244
+ const ctx = getRequestContext()
245
+ if (ctx && ctx.traceId) {
246
+ return originalLoggerConstructor.prototype.error.call(this, message, trace, context)
247
+ }
248
+ return originalLoggerConstructor.prototype.error.call(this, message, trace, context)
249
+ }
250
+
251
+ instance.log = function(message, context) {
252
+ const { getRequestContext } = require('./store')
253
+ const ctx = getRequestContext()
254
+ if (ctx && ctx.traceId) {
255
+ return originalLoggerConstructor.prototype.log.call(this, message, context)
256
+ }
257
+ return originalLoggerConstructor.prototype.log.call(this, message, context)
258
+ }
259
+
260
+ instance.warn = function(message, context) {
261
+ const { getRequestContext } = require('./store')
262
+ const ctx = getRequestContext()
263
+ if (ctx && ctx.traceId) {
264
+ return originalLoggerConstructor.prototype.warn.call(this, message, context)
265
+ }
266
+ return originalLoggerConstructor.prototype.warn.call(this, message, context)
267
+ }
268
+
269
+ instance.debug = function(message, context) {
270
+ const { getRequestContext } = require('./store')
271
+ const ctx = getRequestContext()
272
+ if (ctx && ctx.traceId) {
273
+ return originalLoggerConstructor.prototype.debug.call(this, message, context)
274
+ }
275
+ return originalLoggerConstructor.prototype.debug.call(this, message, context)
276
+ }
277
+
278
+ instance.verbose = function(message, context) {
279
+ const { getRequestContext } = require('./store')
280
+ const ctx = getRequestContext()
281
+ if (ctx && ctx.traceId) {
282
+ return originalLoggerConstructor.prototype.verbose.call(this, message, context)
283
+ }
284
+ return originalLoggerConstructor.prototype.verbose.call(this, message, context)
285
+ }
286
+
287
+ return instance
288
+ }
289
+
290
+ Object.setPrototypeOf(PatchedNestJSPinoLogger, originalLoggerConstructor)
291
+ Object.assign(PatchedNestJSPinoLogger, originalLoggerConstructor)
292
+
293
+ result.Logger = PatchedNestJSPinoLogger
294
+ }
295
+
296
+ if (result && result.LoggerModule && result.LoggerModule.forRoot) {
297
+ const originalForRoot = result.LoggerModule.forRoot
298
+
299
+ result.LoggerModule.forRoot = function(options) {
300
+ const createPinoStream = require('./streams/pino')
301
+ const stream = createPinoStream()
302
+
303
+ const mergedOptions = {
304
+ ...options,
305
+ pinoHttp: {
306
+ ...(options && options.pinoHttp),
307
+ stream,
308
+ },
309
+ }
310
+
311
+ return originalForRoot.call(this, mergedOptions)
312
+ }
313
+
314
+ Object.setPrototypeOf(result.LoggerModule.forRoot, originalForRoot)
315
+ Object.assign(result.LoggerModule.forRoot, originalForRoot)
316
+ }
317
+ }
318
+
319
+ return result
320
+ }
321
+
322
+ } catch (_) {}
323
+
324
+ try {
325
+ const undici = require('undici')
326
+ if (undici && undici.request) {
327
+ const originalRequest = undici.request
328
+ undici.request = function(url, options, callback) {
329
+ const { getRequestContext } = require('./store')
330
+ const ctx = getRequestContext()
331
+
332
+ if (ctx && ctx.traceId) {
333
+ const headers = (options && options.headers) || {}
334
+ options = {
335
+ ...options,
336
+ headers: {
337
+ ...headers,
338
+ 'X-Trace-ID': ctx.traceId,
339
+ 'X-Span-ID': ctx.spanId || '',
340
+ 'X-Parent-Span-ID': ctx.parentSpanId || '',
341
+ 'X-Request-ID': ctx.requestId || require('uuid').v4()
342
+ }
343
+ }
344
+ }
345
+
346
+ return originalRequest.call(this, url, options, callback)
347
+ }
348
+ }
349
+ } catch (_) {}
350
+
351
+ try {
352
+ const axios = require('axios')
353
+ if (axios && axios.create) {
354
+ const originalCreate = axios.create
355
+ axios.create = function(config) {
356
+ const instance = originalCreate.call(this, config)
357
+
358
+ const originalRequest = instance.interceptors.request.use
359
+ instance.interceptors.request.use = function(fulfilled, rejected) {
360
+ return originalRequest.call(this, (config) => {
361
+ const { getRequestContext } = require('./store')
362
+ const ctx = getRequestContext()
363
+
364
+ if (ctx && ctx.traceId) {
365
+ config.headers = {
366
+ ...config.headers,
367
+ 'X-Trace-ID': ctx.traceId,
368
+ 'X-Span-ID': ctx.spanId || '',
369
+ 'X-Parent-Span-ID': ctx.parentSpanId || '',
370
+ 'X-Request-ID': ctx.requestId || require('uuid').v4()
371
+ }
372
+ }
373
+
374
+ return fulfilled ? fulfilled(config) : config
375
+ }, rejected)
376
+ }
377
+
378
+ return instance
379
+ }
380
+ }
381
+ } catch (_) {}
382
+
383
+ try {
384
+ const Bull = require('bull')
385
+ const { runWithRequestContext } = require('./store')
386
+
387
+ const originalAdd = Bull.prototype.add
388
+ Bull.prototype.add = function(name, data, opts) {
389
+ const ctx = getRequestContext()
390
+
391
+ if (ctx && ctx.traceId) {
392
+ data = {
393
+ ...data,
394
+ traceId: ctx.traceId,
395
+ spanId: ctx.spanId,
396
+ parentSpanId: ctx.parentSpanId,
397
+ requestId: ctx.requestId,
398
+ }
399
+ }
400
+
401
+ return originalAdd.call(this, name, data, opts)
402
+ }
403
+
404
+ const originalProcess = Bull.prototype.process
405
+ Bull.prototype.process = function(name, concurrency, handler) {
406
+ let actualName, actualConcurrency, actualHandler
407
+
408
+ if (typeof name === 'function') {
409
+ actualHandler = name
410
+ actualName = '__default__'
411
+ actualConcurrency = 1
412
+ } else if (typeof concurrency === 'function') {
413
+ actualHandler = concurrency
414
+ actualName = name
415
+ actualConcurrency = 1
416
+ } else {
417
+ actualName = name
418
+ actualConcurrency = concurrency
419
+ actualHandler = handler
420
+ }
421
+
422
+ const wrappedHandler = function(job, done) {
423
+ const { traceId, spanId, parentSpanId, requestId, ...jobData } = job.data
424
+
425
+ if (traceId && spanId) {
426
+ const ctx = {
427
+ traceId,
428
+ spanId,
429
+ parentSpanId,
430
+ requestId,
431
+ }
432
+
433
+ return runWithRequestContext(ctx, () => {
434
+ return actualHandler.call(this, job, done)
435
+ })
436
+ } else {
437
+ const { startRequestContext } = require('./store')
438
+ const newCtx = startRequestContext({ requestId: require('uuid').v4() })
439
+
440
+ return runWithRequestContext(newCtx, () => {
441
+ return actualHandler.call(this, job, done)
442
+ })
443
+ }
444
+ }
445
+
446
+ if (typeof name === 'function') {
447
+ return originalProcess.call(this, wrappedHandler)
448
+ } else if (typeof concurrency === 'function') {
449
+ return originalProcess.call(this, actualName, wrappedHandler)
450
+ } else {
451
+ return originalProcess.call(this, actualName, actualConcurrency, wrappedHandler)
452
+ }
453
+ }
454
+ } catch (_) {}
455
+ } catch (_) {}
456
+ }
package/server.js CHANGED
@@ -4,14 +4,27 @@ const axios = require('axios')
4
4
  const express = require('express')
5
5
  const cors = require('cors')
6
6
  const os = require('os')
7
- const { trace, context, propagation } = require('@opentelemetry/api')
8
- const { W3CTraceContextPropagator } = require('@opentelemetry/core')
7
+ let trace, context, propagation, W3CTraceContextPropagator
8
+ try {
9
+ const otelApi = require('@opentelemetry/api')
10
+ const otelCore = require('@opentelemetry/core')
11
+ trace = otelApi.trace
12
+ context = otelApi.context
13
+ propagation = otelApi.propagation
14
+ W3CTraceContextPropagator = otelCore.W3CTraceContextPropagator
15
+ } catch (_) {
16
+ trace = { getSpan: () => null }
17
+ context = { active: () => ({}) }
18
+ propagation = { extract: () => ({}), inject: () => {} }
19
+ W3CTraceContextPropagator = class {}
20
+ }
9
21
 
10
22
  const app = express()
11
23
 
12
24
  app.set('trust proxy', 1)
13
25
 
14
- app.use(express.json())
26
+ app.use(express.json({ limit: '10mb' }))
27
+ app.use(express.urlencoded({ extended: true, limit: '10mb' }))
15
28
  app.use(cors())
16
29
 
17
30
  const authEnabled = process.env.AZURE_AD_AUTH_ENABLED === 'true'
@@ -33,79 +46,121 @@ const propagator = new W3CTraceContextPropagator()
33
46
  const traceContextMap = new Map()
34
47
 
35
48
  /**
36
- * Ensures the OpenSearch index exists and is properly configured
49
+ * Creates an index template for dynamic log indices
50
+ * This allows OpenSearch to auto-create indices like logs-azipay, logs-assemble, etc.
37
51
  * @returns {Promise<void>}
38
52
  * @private
39
53
  */
40
- async function ensureIndex() {
41
- const indexName = 'logs-azify'
54
+ async function ensureIndexTemplate() {
55
+ const templateName = 'logs-template'
42
56
  const osUrl = process.env.OPENSEARCH_URL || 'http://localhost:9200'
57
+
43
58
  try {
44
- await axios.head(`${osUrl}/${indexName}`)
45
- console.log(`✅ Índice ${indexName} já existe no OpenSearch`)
46
- try {
47
- await axios.post(`${osUrl}/${indexName}/_mapping`, {
48
- properties: {
49
- traceId: { type: 'keyword' },
50
- spanId: { type: 'keyword' },
51
- parentSpanId: { type: 'keyword' },
52
- appName: { type: 'keyword' }
53
- }
54
- })
55
- } catch (mapErr) {}
56
- } catch (error) {
57
- if (error.response?.status === 404) {
58
- try {
59
- await axios.put(`${osUrl}/${indexName}`, {
60
- settings: {
61
- number_of_shards: 1,
62
- number_of_replicas: 0
63
- },
64
- mappings: {
65
- properties: {
66
- timestamp: { type: 'date' },
67
- level: { type: 'keyword' },
68
- message: { type: 'text' },
69
- service: {
70
- properties: {
71
- name: { type: 'keyword' },
72
- version: { type: 'keyword' }
73
- }
74
- },
75
- appName: { type: 'keyword' },
76
- traceId: { type: 'keyword' },
77
- spanId: { type: 'keyword' },
78
- parentSpanId: { type: 'keyword' },
79
- userId: { type: 'keyword' },
80
- requestId: { type: 'keyword' },
81
- method: { type: 'keyword' },
82
- url: { type: 'keyword' },
83
- statusCode: { type: 'integer' },
84
- responseTime: { type: 'float' },
85
- ip: { type: 'ip' },
86
- userAgent: { type: 'text' },
87
- environment: { type: 'keyword' },
88
- hostname: { type: 'keyword' },
89
- responseBody: { type: 'text' },
90
- error: {
91
- properties: {
92
- message: { type: 'text' },
93
- stack: { type: 'text' },
94
- name: { type: 'keyword' }
95
- }
59
+ await axios.put(`${osUrl}/_index_template/${templateName}`, {
60
+ index_patterns: ['logs-*'],
61
+ template: {
62
+ settings: {
63
+ number_of_shards: 1,
64
+ number_of_replicas: 0,
65
+ 'index.refresh_interval': '5s'
66
+ },
67
+ mappings: {
68
+ properties: {
69
+ timestamp: { type: 'date' },
70
+ level: { type: 'keyword' },
71
+ message: { type: 'text' },
72
+ service: {
73
+ properties: {
74
+ name: { type: 'keyword' },
75
+ version: { type: 'keyword' }
76
+ }
77
+ },
78
+ appName: { type: 'keyword' },
79
+ traceId: { type: 'keyword' },
80
+ spanId: { type: 'keyword' },
81
+ parentSpanId: { type: 'keyword' },
82
+ userId: { type: 'keyword' },
83
+ requestId: { type: 'keyword' },
84
+ method: { type: 'keyword' },
85
+ url: { type: 'keyword' },
86
+ statusCode: { type: 'integer' },
87
+ responseTime: { type: 'float' },
88
+ ip: { type: 'ip' },
89
+ userAgent: { type: 'text' },
90
+ environment: { type: 'keyword' },
91
+ hostname: { type: 'keyword' },
92
+ responseBody: { type: 'text' },
93
+ error: {
94
+ properties: {
95
+ message: { type: 'text' },
96
+ stack: { type: 'text' },
97
+ name: { type: 'keyword' }
96
98
  }
97
99
  }
98
100
  }
99
- })
100
- console.log(`✅ Índice ${indexName} criado no OpenSearch`)
101
- } catch (createError) {
102
- console.error('❌ Erro ao criar índice:', createError.message)
103
- }
101
+ }
102
+ },
103
+ priority: 500
104
+ })
105
+ console.log(`✅ Index template ${templateName} criado/atualizado no OpenSearch`)
106
+ console.log(` Índices serão criados automaticamente no formato: logs-{service-name}`)
107
+ } catch (error) {
108
+ console.error('❌ Erro ao criar index template:', error.message)
109
+ if (error.response) {
110
+ console.error(' Detalhes:', error.response.data)
104
111
  }
105
112
  }
106
113
  }
107
114
 
108
- ensureIndex()
115
+ ensureIndexTemplate()
116
+
117
+ /**
118
+ * Escapes HTML special characters to prevent XSS attacks
119
+ * This prevents script injection when data is rendered in OpenSearch Dashboards
120
+ * @param {string} str - String to escape
121
+ * @returns {string} Escaped string
122
+ */
123
+ function escapeHtml(str) {
124
+ if (typeof str !== 'string') return str
125
+ const map = {
126
+ '&': '&amp;',
127
+ '<': '&lt;',
128
+ '>': '&gt;',
129
+ '"': '&quot;',
130
+ "'": '&#x27;',
131
+ '/': '&#x2F;'
132
+ }
133
+ return str.replace(/[&<>"'/]/g, (m) => map[m])
134
+ }
135
+
136
+ /**
137
+ * Sanitizes a value recursively to prevent XSS attacks
138
+ * Escapes HTML special characters in strings that could be rendered in Dashboards
139
+ * @param {any} value - Value to sanitize
140
+ * @returns {any} Sanitized value
141
+ */
142
+ function sanitizeValue(value) {
143
+ if (typeof value === 'string') {
144
+ // Escape HTML special characters to prevent XSS when rendered in Dashboards
145
+ return escapeHtml(value)
146
+ }
147
+
148
+ if (Array.isArray(value)) {
149
+ return value.map(item => sanitizeValue(item))
150
+ }
151
+
152
+ if (value !== null && typeof value === 'object') {
153
+ const sanitized = {}
154
+ for (const key in value) {
155
+ if (value.hasOwnProperty(key)) {
156
+ sanitized[key] = sanitizeValue(value[key])
157
+ }
158
+ }
159
+ return sanitized
160
+ }
161
+
162
+ return value
163
+ }
109
164
 
110
165
  function generateTraceId() {
111
166
  return Math.random().toString(36).substring(2, 15) +
@@ -162,7 +217,18 @@ app.get('/', ensureAuthenticated, (req, res) => {
162
217
  })
163
218
 
164
219
  app.post('/log', async (req, res) => {
165
- const { level, message, meta } = req.body
220
+ let { level, message, meta } = req.body
221
+
222
+ if (typeof message === 'string') {
223
+ message = sanitizeValue(message)
224
+ }
225
+ if (meta) {
226
+ meta = sanitizeValue(meta)
227
+ }
228
+ if (typeof level === 'string') {
229
+ level = sanitizeValue(level)
230
+ }
231
+
166
232
  if (!level || !message) {
167
233
  return res.status(400).json({ success: false, message: 'Level and message are required.' })
168
234
  }
@@ -183,11 +249,11 @@ app.post('/log', async (req, res) => {
183
249
  return res.json({ success: true, message: 'Log filtrado (Prisma verboso)' })
184
250
  }
185
251
 
186
- const requestId = meta?.requestId
252
+ const requestId = meta && meta.requestId
187
253
 
188
254
  let traceContext = null
189
255
 
190
- if (meta?.traceId && meta?.spanId) {
256
+ if (meta && meta.traceId && meta.spanId) {
191
257
  traceContext = {
192
258
  traceId: meta.traceId,
193
259
  spanId: meta.spanId,
@@ -206,7 +272,8 @@ app.post('/log', async (req, res) => {
206
272
  }
207
273
  })
208
274
 
209
- const spanContext = trace.getSpan(extractedCtx)?.spanContext?.() || null
275
+ const span = trace.getSpan(extractedCtx)
276
+ const spanContext = (span && span.spanContext && span.spanContext()) || null
210
277
  if (spanContext && spanContext.traceId && spanContext.spanId) {
211
278
  traceContext = {
212
279
  traceId: spanContext.traceId,
@@ -230,16 +297,16 @@ app.post('/log', async (req, res) => {
230
297
  }
231
298
 
232
299
  const logEntry = {
233
- timestamp: meta?.timestamp || new Date().toISOString(),
300
+ timestamp: (meta && meta.timestamp) || new Date().toISOString(),
234
301
  level,
235
302
  message,
236
303
  service: {
237
- name: meta?.service?.name || 'unknown-service',
238
- version: meta?.service?.version || '1.0.0'
304
+ name: (meta && meta.service && meta.service.name) || 'unknown-service',
305
+ version: (meta && meta.service && meta.service.version) || '1.0.0'
239
306
  },
240
- appName: meta?.appName || meta?.service?.name || undefined,
241
- environment: meta?.environment || process.env.NODE_ENV || 'development',
242
- hostname: meta?.hostname || os.hostname(),
307
+ appName: (meta && meta.appName) || (meta && meta.service && meta.service.name) || undefined,
308
+ environment: (meta && meta.environment) || process.env.NODE_ENV || 'development',
309
+ hostname: (meta && meta.hostname) || os.hostname(),
243
310
  traceId: traceContext.traceId,
244
311
  spanId: traceContext.spanId,
245
312
  parentSpanId: traceContext.parentSpanId
@@ -255,12 +322,15 @@ app.post('/log', async (req, res) => {
255
322
 
256
323
  try {
257
324
  const osUrl = process.env.OPENSEARCH_URL || 'http://localhost:9200'
258
- await axios.post(`${osUrl}/logs-azify/_doc`, logEntry, {
325
+ const serviceName = (logEntry.service.name || 'unknown').toLowerCase().replace(/[^a-z0-9-]/g, '-')
326
+ const indexName = `logs-${serviceName}`
327
+
328
+ await axios.post(`${osUrl}/${indexName}/_doc`, logEntry, {
259
329
  headers: { 'Content-Type': 'application/json' }
260
330
  })
261
331
 
262
- console.log(`✅ [${level.toUpperCase()}] ${message} | traceId: ${traceContext.traceId.substring(0, 8)}... | service: ${logEntry.service.name}`)
263
- res.json({ success: true, message: 'Log enviado com sucesso' })
332
+ console.log(`✅ [${level.toUpperCase()}] ${message} | traceId: ${traceContext.traceId.substring(0, 8)}... | service: ${logEntry.service.name} | index: ${indexName}`)
333
+ res.json({ success: true, message: 'Log enviado com sucesso', index: indexName })
264
334
  } catch (error) {
265
335
  console.error('❌ Erro ao enviar log para OpenSearch:', error.message)
266
336
  if (error.response) {
package/streams/bunyan.js CHANGED
@@ -43,7 +43,7 @@ function createBunyanStream(options = {}) {
43
43
 
44
44
  const meta = {
45
45
  ...cleanRecord,
46
- service: { name: serviceName, version: record.service?.version || '1.0.0' },
46
+ service: { name: serviceName, version: (record.service && record.service.version) || '1.0.0' },
47
47
  environment,
48
48
  timestamp: new Date().toISOString(),
49
49
  hostname: require('os').hostname(),
@@ -54,7 +54,7 @@ function createBunyanStream(options = {}) {
54
54
 
55
55
  const payload = { level, message: record.msg || record.message || 'log', meta }
56
56
 
57
- axios.post(`${loggerUrl}`, payload, { headers, timeout: 3001 }).catch(() => {})
57
+ axios.post(`${loggerUrl}`, payload, { headers, timeout: 10000 }).catch(() => {})
58
58
  }
59
59
  }
60
60
  }
package/streams/pino.js CHANGED
@@ -1,5 +1,17 @@
1
- const { context, propagation, trace } = require('@opentelemetry/api')
2
- const { W3CTraceContextPropagator } = require('@opentelemetry/core')
1
+ let context, propagation, trace, W3CTraceContextPropagator
2
+ try {
3
+ const otelApi = require('@opentelemetry/api')
4
+ const otelCore = require('@opentelemetry/core')
5
+ context = otelApi.context
6
+ propagation = otelApi.propagation
7
+ trace = otelApi.trace
8
+ W3CTraceContextPropagator = otelCore.W3CTraceContextPropagator
9
+ } catch (_) {
10
+ context = { active: () => ({}) }
11
+ propagation = { setGlobalPropagator: () => {}, inject: () => {} }
12
+ trace = { getSpan: () => null }
13
+ W3CTraceContextPropagator = class {}
14
+ }
3
15
  const axios = require('axios')
4
16
 
5
17
  function createPinoStream(options = {}) {
@@ -23,7 +35,7 @@ function createPinoStream(options = {}) {
23
35
  spanId = ctx.spanId
24
36
  } else {
25
37
  const span = trace.getSpan(context.active())
26
- const spanContext = span?.spanContext()
38
+ const spanContext = span && span.spanContext()
27
39
  if (spanContext) {
28
40
  traceId = spanContext.traceId
29
41
  spanId = spanContext.spanId
@@ -44,7 +56,7 @@ function createPinoStream(options = {}) {
44
56
 
45
57
  const meta = {
46
58
  ...record,
47
- service: { name: serviceName, version: record.service?.version || '1.0.0' },
59
+ service: { name: serviceName, version: (record.service && record.service.version) || '1.0.0' },
48
60
  environment,
49
61
  timestamp: new Date().toISOString(),
50
62
  hostname: require('os').hostname(),
@@ -54,7 +66,7 @@ function createPinoStream(options = {}) {
54
66
  }
55
67
 
56
68
  const payload = { level, message: record.msg || record.message || 'log', meta }
57
- axios.post(`${loggerUrl}`, payload, { headers, timeout: 3001 }).catch(() => {})
69
+ axios.post(`${loggerUrl}`, payload, { headers, timeout: 10000 }).catch(() => {})
58
70
  }
59
71
  }
60
72
  }