azify-logger 1.0.43 → 1.0.44

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
@@ -95,16 +95,16 @@ O `azify-logger` inclui um OpenTelemetry Collector configurado para receber trac
95
95
  Para habilitar o envio de traces, configure a variável de ambiente na sua aplicação:
96
96
 
97
97
  ```bash
98
- OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/v1/traces
98
+ OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:3001/v1/traces
99
99
  ```
100
100
 
101
101
  ### URLs
102
102
 
103
103
  | Ambiente | URL |
104
104
  |----------|-----|
105
- | **Development** | `http://localhost:4318/v1/traces` |
105
+ | **Development (Docker)** | `http://localhost:3001/v1/traces` |
106
106
  | **Staging** | `https://logsdashboard.azify.dev/v1/traces` |
107
- | **Production** | `https://cadence.aztech.host/v1/traces` |
107
+ | **Production** | `https://cadence.aztech.host/v1/traces` |
108
108
 
109
109
  ## 🔍 Como Funciona
110
110
 
@@ -0,0 +1,164 @@
1
+ require('./otel-env')
2
+ const { startRequestContext, runWithRequestContext, getRequestContext } = require('./store')
3
+ const { createHttpLoggerTransport } = require('./streams/httpQueue')
4
+ const { sendSpanToOtel, setEndpointOverride } = require('./trace-export')
5
+ const os = require('os')
6
+
7
+ let trace, otelContext
8
+ try {
9
+ const otelApi = require('@opentelemetry/api')
10
+ trace = otelApi.trace
11
+ otelContext = otelApi.context
12
+ } catch (_) {
13
+ trace = { getSpan: () => null }
14
+ otelContext = { active: () => ({}) }
15
+ }
16
+
17
+ function fastUUID() {
18
+ const timestamp = Date.now().toString(36)
19
+ const randomPart = Math.random().toString(36).substring(2, 15)
20
+ const randomPart2 = Math.random().toString(36).substring(2, 15)
21
+ return `${timestamp}-${randomPart}-${randomPart2}`.substring(0, 36)
22
+ }
23
+
24
+ function sanitizeTraceHex(value) {
25
+ if (!value || typeof value !== 'string') return null
26
+ const hex = value.replace(/[^0-9a-fA-F]/g, '').toLowerCase()
27
+ if (!hex) return null
28
+ return hex.padEnd(32, '0').slice(0, 32)
29
+ }
30
+
31
+ function createPinoHttpLoggingMiddleware(options = {}) {
32
+ const svcName = options.serviceName || process.env.APP_NAME
33
+ if (svcName) {
34
+ process.env.OTEL_SERVICE_NAME = svcName
35
+ process.env.SERVICE_NAME = svcName
36
+ }
37
+ const otelEndpoint = options.otelEndpoint || options.otelExporterEndpoint || process.env.OTEL_EXPORTER_OTLP_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
38
+ if (otelEndpoint) {
39
+ setEndpointOverride(otelEndpoint)
40
+ }
41
+ const config = {
42
+ serviceName: svcName,
43
+ loggerUrl: options.loggerUrl || process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log',
44
+ environment: options.environment || process.env.NODE_ENV,
45
+ logRequest: options.logRequest !== false && process.env.AZIFY_LOGGER_LOG_REQUEST !== 'false',
46
+ }
47
+
48
+ const transport = createHttpLoggerTransport(config.loggerUrl, {})
49
+ const hostname = os.hostname()
50
+ const serviceObj = config.serviceName ? { name: config.serviceName, version: '1.0.0' } : null
51
+
52
+ function getOtelTraceContext() {
53
+ try {
54
+ const activeContext = otelContext.active()
55
+ const span = trace.getSpan(activeContext)
56
+ if (span) {
57
+ const spanContext = span.spanContext()
58
+ if (spanContext && spanContext.traceId && spanContext.spanId) {
59
+ const traceHex = spanContext.traceId.replace(/-/g, '')
60
+ return {
61
+ traceId: traceHex.length === 32 ? `${traceHex.substring(0, 8)}-${traceHex.substring(8, 12)}-${traceHex.substring(12, 16)}-${traceHex.substring(16, 20)}-${traceHex.substring(20, 32)}` : spanContext.traceId,
62
+ spanId: spanContext.spanId,
63
+ parentSpanId: null
64
+ }
65
+ }
66
+ }
67
+ } catch (_) {}
68
+ return null
69
+ }
70
+
71
+ function sendLog(level, message, meta = {}) {
72
+ if (!transport || typeof transport.enqueue !== 'function') return
73
+
74
+ transport.enqueue({
75
+ level,
76
+ message,
77
+ meta
78
+ }, { 'content-type': 'application/json' })
79
+ }
80
+
81
+ return function azifyPinoLoggingMiddleware(req, res, next) {
82
+ const startTime = Date.now()
83
+ const method = req.method
84
+ const url = req.url
85
+ const path = req.path || url
86
+
87
+ let reqCtx = null
88
+
89
+ function ensureRequestContext() {
90
+ if (reqCtx) return reqCtx
91
+
92
+ const otelCtx = getOtelTraceContext()
93
+ const traceHex = otelCtx ? otelCtx.traceId.replace(/-/g, '').substring(0, 32) : sanitizeTraceHex(req.headers['x-trace-id'])
94
+
95
+ reqCtx = startRequestContext({
96
+ requestId: req.requestId || fastUUID(),
97
+ traceHex: traceHex || undefined,
98
+ parentSpanId: otelCtx?.parentSpanId || req.headers['x-parent-span-id'] || null
99
+ })
100
+
101
+ if (otelCtx) {
102
+ reqCtx.traceId = otelCtx.traceId
103
+ reqCtx.spanId = otelCtx.spanId
104
+ }
105
+
106
+ return reqCtx
107
+ }
108
+
109
+ const ctx = ensureRequestContext()
110
+
111
+ req.traceId = ctx.traceId
112
+ req.spanId = ctx.spanId
113
+ req.parentSpanId = ctx.parentSpanId
114
+ req.requestId = ctx.requestId
115
+
116
+ const logWithContext = (level, message, meta) => {
117
+ const otelCtx = getOtelTraceContext()
118
+ const ctx = getRequestContext() || ensureRequestContext()
119
+
120
+ meta.traceId = otelCtx?.traceId || meta.traceId || ctx.traceId
121
+ meta.spanId = otelCtx?.spanId || meta.spanId || ctx.spanId
122
+ meta.parentSpanId = otelCtx?.parentSpanId || meta.parentSpanId || ctx.parentSpanId
123
+ sendLog(level, message, meta)
124
+ }
125
+
126
+ if (config.logRequest) {
127
+ logWithContext('info', `[REQUEST] ${method} ${path}`, {
128
+ method,
129
+ url: path,
130
+ hostname,
131
+ service: serviceObj,
132
+ environment: config.environment
133
+ })
134
+ }
135
+
136
+ const originalEnd = res.end
137
+ res.end = function(...args) {
138
+ res.end = originalEnd
139
+
140
+ const duration = Date.now() - startTime
141
+ const statusCode = res.statusCode
142
+
143
+ if (config.logRequest) {
144
+ logWithContext('info', `[RESPONSE] ${method} ${path}`, {
145
+ method,
146
+ url: path,
147
+ statusCode,
148
+ duration,
149
+ hostname,
150
+ service: serviceObj,
151
+ environment: config.environment
152
+ })
153
+ }
154
+
155
+ return res.end.apply(res, args)
156
+ }
157
+
158
+ runWithRequestContext(ctx, () => {
159
+ next()
160
+ })
161
+ }
162
+ }
163
+
164
+ module.exports = createPinoHttpLoggingMiddleware
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azify-logger",
3
- "version": "1.0.43",
3
+ "version": "1.0.44",
4
4
  "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -94,9 +94,11 @@
94
94
  "middleware-restify.js",
95
95
  "middleware-express.js",
96
96
  "middleware-express.d.ts",
97
+ "middleware-pino.js",
97
98
  "middleware-fastify.js",
98
99
  "server.js",
99
100
  "sampling.js",
101
+ "pino-config.js",
100
102
  "queue/",
101
103
  "scripts/redis-worker.js",
102
104
  "streams/",
package/pino-config.js ADDED
@@ -0,0 +1,26 @@
1
+ const createPinoStream = require('./streams/pino')
2
+
3
+ /**
4
+ * Helper para configurar Pino com azify-logger de forma simples
5
+ */
6
+ function createAzifyPinoTransport(options = {}) {
7
+ const loggerUrl = options.loggerUrl || process.env.AZIFY_LOGGER_URL
8
+ const serviceName = options.serviceName || process.env.APP_NAME
9
+ const environment = options.environment || process.env.NODE_ENV
10
+
11
+ if (!loggerUrl) {
12
+ return null
13
+ }
14
+
15
+ return {
16
+ target: require.resolve('./streams/pino'),
17
+ options: {
18
+ loggerUrl,
19
+ serviceName,
20
+ environment,
21
+ ...options
22
+ }
23
+ }
24
+ }
25
+
26
+ module.exports = { createAzifyPinoTransport }