azify-logger 1.0.17 → 1.0.19

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 CHANGED
@@ -1,5 +1,6 @@
1
- if (process.env.AZIFY_LOGGER_DISABLE === '1') return
2
-
1
+ if (process.env.AZIFY_LOGGER_DISABLE === '1') {
2
+ module.exports = {}
3
+ } else {
3
4
  const Module = require('module')
4
5
  const originalRequire = Module.prototype.require
5
6
 
@@ -70,3 +71,4 @@ Module.prototype.require = function(id) {
70
71
 
71
72
  return result
72
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.17",
3
+ "version": "1.0.19",
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,8 +36,17 @@
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",
package/register-otel.js CHANGED
@@ -8,8 +8,6 @@ try {
8
8
  process.env.OTEL_SERVICE_NAME = serviceName
9
9
  process.env.OTEL_SERVICE_VERSION = serviceVersion
10
10
 
11
- console.log(`🔧 Initializing OpenTelemetry for service: ${serviceName}`)
12
-
13
11
  const sdk = new NodeSDK({
14
12
  serviceName: serviceName,
15
13
  serviceVersion: serviceVersion,
@@ -25,15 +23,9 @@ try {
25
23
  })
26
24
 
27
25
  sdk.start()
28
- console.log(`✅ OpenTelemetry initialized for service: ${serviceName}`)
29
26
 
30
27
  process.once('SIGTERM', () => {
31
28
  sdk.shutdown()
32
- .then(() => console.log('OpenTelemetry shut down successfully'))
33
- .catch((error) => console.error('Error shutting down OpenTelemetry:', error))
34
29
  .finally(() => process.exit(0))
35
30
  })
36
- } catch (error) {
37
- console.warn('⚠️ OpenTelemetry não pôde ser inicializado:', error.message)
38
- console.error('Error details:', error)
39
- }
31
+ } catch (_) {}
package/register.js CHANGED
@@ -1,5 +1,6 @@
1
- if (process.env.AZIFY_LOGGER_DISABLE === '1') return
2
-
1
+ if (process.env.AZIFY_LOGGER_DISABLE === '1') {
2
+ module.exports = {}
3
+ } else {
3
4
  try {
4
5
  const bunyan = require('bunyan')
5
6
  const createBunyanStream = require('./streams/bunyan')
@@ -56,18 +57,6 @@ try {
56
57
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
57
58
  }
58
59
  }
59
- // else {
60
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
61
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
62
- // if (traceMatch) {
63
- // const extractedTraceId = traceMatch[1]
64
- // if (typeof obj === 'object' && obj !== null) {
65
- // obj.traceId = extractedTraceId
66
- // } else {
67
- // obj = { traceId: extractedTraceId, msg: obj }
68
- // }
69
- // }
70
- // }
71
60
  return originalInfo.call(this, obj, msg, ...args)
72
61
  }
73
62
 
@@ -83,18 +72,6 @@ try {
83
72
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
84
73
  }
85
74
  }
86
- // else {
87
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
88
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
89
- // if (traceMatch) {
90
- // const extractedTraceId = traceMatch[1]
91
- // if (typeof obj === 'object' && obj !== null) {
92
- // obj.traceId = extractedTraceId
93
- // } else {
94
- // obj = { traceId: extractedTraceId, msg: obj }
95
- // }
96
- // }
97
- // }
98
75
  return originalError.call(this, obj, msg, ...args)
99
76
  }
100
77
 
@@ -110,18 +87,6 @@ try {
110
87
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
111
88
  }
112
89
  }
113
- // else {
114
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
115
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
116
- // if (traceMatch) {
117
- // const extractedTraceId = traceMatch[1]
118
- // if (typeof obj === 'object' && obj !== null) {
119
- // obj.traceId = extractedTraceId
120
- // } else {
121
- // obj = { traceId: extractedTraceId, msg: obj }
122
- // }
123
- // }
124
- // }
125
90
  return originalWarn.call(this, obj, msg, ...args)
126
91
  }
127
92
 
@@ -137,19 +102,6 @@ try {
137
102
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
138
103
  }
139
104
  }
140
- // else {
141
- // // Extract traceId from message if it exists
142
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
143
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
144
- // if (traceMatch) {
145
- // const extractedTraceId = traceMatch[1]
146
- // if (typeof obj === 'object' && obj !== null) {
147
- // obj.traceId = extractedTraceId
148
- // } else {
149
- // obj = { traceId: extractedTraceId, msg: obj }
150
- // }
151
- // }
152
- // }
153
105
  return originalDebug.call(this, obj, msg, ...args)
154
106
  }
155
107
 
@@ -165,18 +117,6 @@ try {
165
117
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
166
118
  }
167
119
  }
168
- // else {
169
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
170
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
171
- // if (traceMatch) {
172
- // const extractedTraceId = traceMatch[1]
173
- // if (typeof obj === 'object' && obj !== null) {
174
- // obj.traceId = extractedTraceId
175
- // } else {
176
- // obj = { traceId: extractedTraceId, msg: obj }
177
- // }
178
- // }
179
- // }
180
120
  return originalFatal.call(this, obj, msg, ...args)
181
121
  }
182
122
 
@@ -192,19 +132,6 @@ try {
192
132
  obj = { traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId, requestId: ctx.requestId, msg: obj }
193
133
  }
194
134
  }
195
- // else {
196
- // // Extract traceId from message if it exists
197
- // const messageStr = typeof obj === 'string' ? obj : (typeof msg === 'string' ? msg : '')
198
- // const traceMatch = messageStr.match(/\[([a-f0-9-]{8}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{4}-[a-f0-9-]{12})\]/)
199
- // if (traceMatch) {
200
- // const extractedTraceId = traceMatch[1]
201
- // if (typeof obj === 'object' && obj !== null) {
202
- // obj.traceId = extractedTraceId
203
- // } else {
204
- // obj = { traceId: extractedTraceId, msg: obj }
205
- // }
206
- // }
207
- // }
208
135
  return originalTrace.call(this, obj, msg, ...args)
209
136
  }
210
137
 
@@ -403,7 +330,7 @@ try {
403
330
  const ctx = getRequestContext()
404
331
 
405
332
  if (ctx && ctx.traceId) {
406
- const headers = options?.headers || {}
333
+ const headers = (options && options.headers) || {}
407
334
  options = {
408
335
  ...options,
409
336
  headers: {
@@ -526,3 +453,4 @@ try {
526
453
  }
527
454
  } catch (_) {}
528
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
  }