azify-logger 1.0.54 → 1.0.55

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azify-logger",
3
- "version": "1.0.54",
3
+ "version": "1.0.55",
4
4
  "description": "Azify Logger Client - Centralized logging for OpenSearch",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -28,13 +28,70 @@ try {
28
28
  const MAX_EARLY_LOG_QUEUE = 2000
29
29
  const earlyLogQueue = []
30
30
 
31
+ function normalizePathEarly(p) {
32
+ if (!p) return '/'
33
+ const trimmed = String(p).replace(/\/+$/, '')
34
+ return trimmed === '' ? '/' : trimmed
35
+ }
36
+
37
+ /** Ignora esquema (http vs https): undici muitas vezes resolve como https e AZIFY_LOGGER_URL é http em dev. */
31
38
  function isLoggerUrl(urlStr) {
32
- const loggerUrl = process.env.AZIFY_LOGGER_URL
33
- if (!loggerUrl || typeof urlStr !== 'string' || !urlStr.trim()) return false
34
- const a = String(urlStr).trim().toLowerCase().replace(/\/+$/, '')
35
- const b = String(loggerUrl).trim().toLowerCase().replace(/\/+$/, '')
36
- if (!b) return false
37
- return a === b || a.startsWith(b + '/')
39
+ const loggerUrlRaw = process.env.AZIFY_LOGGER_URL || 'http://localhost:3001/log'
40
+ if (typeof urlStr !== 'string' || !urlStr.trim()) return false
41
+ try {
42
+ const base = new URL(String(loggerUrlRaw).trim())
43
+ const cand = new URL(String(urlStr).trim())
44
+ if (base.hostname.toLowerCase() !== cand.hostname.toLowerCase()) return false
45
+ const portBase = base.port || (base.protocol === 'https:' ? '443' : '80')
46
+ const portCand = cand.port || (cand.protocol === 'https:' ? '443' : '80')
47
+ if (String(portBase) !== String(portCand)) return false
48
+ const lp = normalizePathEarly(base.pathname)
49
+ const tp = normalizePathEarly(cand.pathname)
50
+ return tp === lp || tp.startsWith(lp + '/')
51
+ } catch (_) {
52
+ return false
53
+ }
54
+ }
55
+
56
+ /** undici.request(url, opts) / request(opts) — muitas chamadas não trazem origin; path pode ser URL absoluta ou só path + hostname. */
57
+ function resolveUrlStrFromUndiciRequestArgs(url, options) {
58
+ const opts = (typeof url === 'object' && url !== null && !(url instanceof URL)) ? url : options
59
+ const urlArg = (typeof url === 'string' || (url && (url.href || url instanceof URL))) ? url : (opts && (opts.url || opts.uri))
60
+ if (typeof urlArg === 'string') return urlArg
61
+ if (urlArg && typeof urlArg.href === 'string') return urlArg.href
62
+ if (urlArg && typeof urlArg.toString === 'function') return urlArg.toString()
63
+ if (opts && typeof opts.origin === 'string' && opts.path != null) {
64
+ return opts.origin + (String(opts.path).startsWith('/') ? '' : '/') + String(opts.path)
65
+ }
66
+ if (opts && typeof opts.path === 'string' && /^https?:\/\//i.test(opts.path)) {
67
+ return opts.path
68
+ }
69
+ if (opts && (opts.hostname || opts.host)) {
70
+ const h = String(opts.hostname || String(opts.host || '').split(':')[0]).trim()
71
+ if (h) {
72
+ const hl = h.toLowerCase()
73
+ const isLocal = hl === 'localhost' || hl === '127.0.0.1' || hl === '::1'
74
+ const proto = (opts.protocol && String(opts.protocol).replace(/:$/, '')) || (isLocal ? 'http' : 'https')
75
+ const pnum = opts.port != null && opts.port !== '' ? Number(opts.port) : NaN
76
+ const port = !Number.isNaN(pnum) && pnum !== 80 && pnum !== 443 ? ':' + pnum : ''
77
+ const p = opts.path != null ? String(opts.path) : '/'
78
+ return proto + '://' + h + port + (p.startsWith('/') ? p : '/' + p)
79
+ }
80
+ }
81
+ return 'unknown'
82
+ }
83
+
84
+ function resolveUrlStrFromDispatcherOpts(opts) {
85
+ if (!opts || typeof opts !== 'object') return 'unknown'
86
+ const origin = (opts.origin) || ''
87
+ const pathPart = opts.path != null ? opts.path : '/'
88
+ const pathStr = String(pathPart)
89
+ if (/^https?:\/\//i.test(pathStr)) return pathStr
90
+ if (origin) return origin + (pathStr.startsWith('/') ? pathStr : '/' + pathStr)
91
+ if (opts.hostname || opts.host) {
92
+ return resolveUrlStrFromUndiciRequestArgs(null, opts)
93
+ }
94
+ return 'unknown'
38
95
  }
39
96
 
40
97
  function send(level, message, meta) {
@@ -118,12 +175,7 @@ try {
118
175
  exports.request = function (url, options, callback) {
119
176
  const opts = (typeof url === 'object' && url !== null && !(url instanceof URL)) ? url : options
120
177
  const method = ((opts && opts.method) || 'GET').toUpperCase()
121
- let urlStr = 'unknown'
122
- const urlArg = (typeof url === 'string' || (url && (url.href || url instanceof URL))) ? url : (opts && (opts.url || opts.uri))
123
- if (typeof urlArg === 'string') urlStr = urlArg
124
- else if (urlArg && typeof urlArg.href === 'string') urlStr = urlArg.href
125
- else if (urlArg && typeof urlArg.toString === 'function') urlStr = urlArg.toString()
126
- else if (opts && typeof opts.origin === 'string' && opts.path != null) urlStr = opts.origin + (String(opts.path).startsWith('/') ? '' : '/') + String(opts.path)
178
+ const urlStr = resolveUrlStrFromUndiciRequestArgs(url, options)
127
179
  if (process.env.AZIFY_LOGGER_DEBUG === '1') { try { process.stderr.write('[AZIFY-PATCH] undici.request ' + method + ' ' + urlStr.slice(0, 80) + '\n') } catch (_) {} }
128
180
  const _ep = process.env.AZIFY_DEBUG_LOG_PATH
129
181
  if (_ep) { try { require('fs').appendFileSync(_ep, new Date().toISOString() + ' [AZIFY] WRAPPER exports.request ' + method + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
@@ -174,9 +226,7 @@ try {
174
226
  const origDisp = proto.request
175
227
  proto.request = function (opts, callback) {
176
228
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
177
- const origin = (opts && opts.origin) || ''
178
- const path = (opts && opts.path) != null ? opts.path : '/'
179
- const urlStr = origin ? (origin + (path.startsWith('/') ? path : '/' + path)) : 'unknown'
229
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
180
230
  httpLog('WRAPPER Dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
181
231
  const ctx = getRequestContext() || getLastJobContext()
182
232
  const otelCtx = getOtelTraceContext()
@@ -255,9 +305,7 @@ try {
255
305
  if (typeof origReq !== 'function') return d
256
306
  d.request = function (opts, callback) {
257
307
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
258
- const origin = (opts && opts.origin) || ''
259
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
260
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
308
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
261
309
  httpLog('WRAPPER getGlobalDispatcher().request ' + methodStr + ' ' + urlStr.slice(0, 80))
262
310
  const ctx = getRequestContext ? getRequestContext() : (getLastJobContext ? getLastJobContext() : null)
263
311
  const otelCtx = getOtelTraceContext()
@@ -333,9 +381,7 @@ try {
333
381
  const HTTP_CLIENT_MODE = deps ? deps.HTTP_CLIENT_MODE : 'all'
334
382
  d.request = function (opts, callback) {
335
383
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
336
- const origin = (opts && opts.origin) || ''
337
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
338
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
384
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
339
385
  httpLog('WRAPPER globalThis.dispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 80))
340
386
  const _dp = process.env.AZIFY_DEBUG_LOG_PATH
341
387
  if (_dp) { try { require('fs').appendFileSync(_dp, new Date().toISOString() + ' [AZIFY] WRAPPER globalDispatcher.request ' + methodStr + ' ' + urlStr.slice(0, 100) + '\n') } catch (_) {} }
@@ -608,14 +654,12 @@ try {
608
654
  const RequestHandler = mod.exports.RequestHandler
609
655
  mod.exports = function wrappedApiRequest(opts, callback) {
610
656
  const methodStr = String((opts && opts.method) || 'GET').toUpperCase()
611
- const urlPart = (opts && opts.origin) ? String(opts.origin).slice(0, 80) : (opts && opts.path) || ''
657
+ const urlStr = resolveUrlStrFromDispatcherOpts(opts)
658
+ const urlPart = urlStr !== 'unknown' ? urlStr.slice(0, 80) : ((opts && opts.origin) ? String(opts.origin).slice(0, 80) : (opts && opts.path) || '')
612
659
  if (HTTP_DEBUG) try { process.stderr.write('[AZIFY-PATCH] api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
613
660
  if (HTTP_VERBOSE || HTTP_DEBUG) {
614
661
  try { process.stderr.write('[AZIFY] HTTP wrapper api-request ' + methodStr + ' ' + urlPart + '\n') } catch (_) {}
615
662
  }
616
- const origin = (opts && opts.origin) || ''
617
- const pathPart = (opts && opts.path) != null ? opts.path : '/'
618
- const urlStr = origin ? (origin + (pathPart.startsWith('/') ? pathPart : '/' + pathPart)) : 'unknown'
619
663
  httpLog('WRAPPER api-request ' + methodStr + ' ' + urlStr.slice(0, 80))
620
664
  const ctx = getRequestContext() || getLastJobContext()
621
665
  const otelCtx = getOtelTraceContext()
package/register.js CHANGED
@@ -10,7 +10,12 @@ try {
10
10
  }
11
11
  const ModuleForRequire = require('module')
12
12
  const nodeRequireOriginal = ModuleForRequire.prototype.require
13
- const bunyan = require('bunyan')
13
+ let bunyan = null
14
+ try {
15
+ bunyan = require('bunyan')
16
+ } catch (_) {
17
+ /* bunyan é opcional; sem ele o patch Bunyan não aplica, mas HTTP/Pino/axios devem continuar */
18
+ }
14
19
  const createBunyanStream = require('./streams/bunyan')
15
20
  const { createHttpLoggerTransport } = require('./streams/httpQueue')
16
21
  const { getRequestContext } = require('./store')
@@ -145,12 +150,34 @@ try {
145
150
 
146
151
  const debug = process.env.AZIFY_LOGGER_DEBUG === '1'
147
152
  const httpVerbose = process.env.AZIFY_LOGGER_HTTP_VERBOSE === '1'
153
+
154
+ function extractUrlFromReqResMessage(msgStr) {
155
+ const s = String(msgStr)
156
+ const m1 = s.match(/\[(?:REQUEST|RESPONSE)\]\s+\S+\s+(https?:\/\/\S+)/i)
157
+ if (m1) return m1[1].replace(/[)\]}>.,;]+$/, '')
158
+ const m2 = s.match(/\[(?:REQUEST|RESPONSE)\]\s+\S+\s+(\S+)/i)
159
+ if (m2 && /^https?:\/\//i.test(m2[1])) return m2[1].replace(/[)\]}>.,;]+$/, '')
160
+ return null
161
+ }
162
+
163
+ const OPAQUE_NO_URL = 'https://opaque.invalid/azify-logger-no-url'
164
+
148
165
  function sendOutboundLog(level, message, meta) {
149
166
  try {
150
167
  const msgStr = String(message)
151
168
  const isReqRes = msgStr.includes('[REQUEST]') || msgStr.includes('[RESPONSE]')
169
+ if (isReqRes && meta && typeof meta === 'object') {
170
+ const u = meta.url
171
+ const badUrl =
172
+ u === 'unknown' ||
173
+ !String(u || '').trim() ||
174
+ String(u).toLowerCase() === 'unknown'
175
+ if (badUrl) {
176
+ const fromMsg = extractUrlFromReqResMessage(msgStr)
177
+ meta = fromMsg ? { ...meta, url: fromMsg } : { ...meta, url: OPAQUE_NO_URL }
178
+ }
179
+ }
152
180
  if (isReqRes && meta && meta.url && isLoggerApiCall({ url: meta.url })) return
153
- if (isReqRes && meta && (meta.url === 'unknown' || !String(meta.url || '').trim() || String(meta.url).toLowerCase() === 'unknown')) return
154
181
  const source = meta && meta.__source
155
182
  if (debug && source === 'pino-stdout') {
156
183
  if (debug) try { process.stderr.write('[azify-logger] SENDOUTBOUND_ENTER level=' + level + ' source=' + source + ' msg=' + msgStr.slice(0, 60) + '\n') } catch (_) {}
@@ -350,9 +377,16 @@ try {
350
377
 
351
378
  try {
352
379
  const target = new URL(candidate, normalizedLoggerOrigin)
353
- const targetOrigin = `${target.protocol}//${target.host}`
380
+ if (loggerEndpoint.hostname.toLowerCase() !== target.hostname.toLowerCase()) {
381
+ return false
382
+ }
383
+ const portLogger = loggerEndpoint.port || (loggerEndpoint.protocol === 'https:' ? '443' : '80')
384
+ const portTarget = target.port || (target.protocol === 'https:' ? '443' : '80')
385
+ if (String(portLogger) !== String(portTarget)) {
386
+ return false
387
+ }
354
388
  const targetPath = normalizePath(target.pathname)
355
- return targetOrigin === normalizedLoggerOrigin && targetPath === normalizedLoggerPath
389
+ return targetPath === normalizedLoggerPath || targetPath.startsWith(normalizedLoggerPath + '/')
356
390
  } catch (_) {
357
391
  if (typeof candidate === 'string') {
358
392
  const relativePath = normalizePath(candidate)
@@ -363,22 +397,24 @@ try {
363
397
  return false
364
398
  }
365
399
 
366
- const originalCreate = bunyan.createLogger
367
- bunyan.createLogger = function patchedCreateLogger(options) {
368
- const logger = originalCreate.call(bunyan, options)
369
- try {
370
- const level = process.env.AZIFY_LOG_LEVEL || (options && options.level) || 'info'
371
- const loggerUrl = process.env.AZIFY_LOGGER_URL
372
- const serviceName = process.env.APP_NAME || (options && options.name)
373
- const environment = process.env.NODE_ENV
374
-
375
- logger.addStream({
376
- level,
377
- type: 'raw',
378
- stream: createBunyanStream({ loggerUrl, serviceName, environment })
379
- })
380
- } catch (_) {}
381
- return logger
400
+ if (bunyan && typeof bunyan.createLogger === 'function') {
401
+ const originalCreate = bunyan.createLogger
402
+ bunyan.createLogger = function patchedCreateLogger(options) {
403
+ const logger = originalCreate.call(bunyan, options)
404
+ try {
405
+ const level = process.env.AZIFY_LOG_LEVEL || (options && options.level) || 'info'
406
+ const loggerUrl = process.env.AZIFY_LOGGER_URL
407
+ const serviceName = process.env.APP_NAME || (options && options.name)
408
+ const environment = process.env.NODE_ENV
409
+
410
+ logger.addStream({
411
+ level,
412
+ type: 'raw',
413
+ stream: createBunyanStream({ loggerUrl, serviceName, environment })
414
+ })
415
+ } catch (_) {}
416
+ return logger
417
+ }
382
418
  }
383
419
 
384
420
  const getOtelTraceContext = () => {
package/server.js CHANGED
@@ -186,7 +186,11 @@ async function ensureIndexTemplate() {
186
186
  settings: {
187
187
  number_of_shards: 1,
188
188
  number_of_replicas: 0,
189
- 'index.refresh_interval': '5s'
189
+ 'index.refresh_interval': '5s',
190
+ 'index.mapping.total_fields.limit': Math.min(
191
+ Math.max(Number(process.env.AZIFY_LOGGER_OPENSEARCH_TOTAL_FIELDS_LIMIT) || 5000, 1000),
192
+ 20000
193
+ )
190
194
  },
191
195
  mappings: {
192
196
  properties: {
@@ -2327,14 +2331,22 @@ function _drainOpenSearchQueue() {
2327
2331
  if (_openSearchCircuitRetryTimer.unref) _openSearchCircuitRetryTimer.unref()
2328
2332
  }
2329
2333
  }
2330
- if (_openSearchFailuresInRow <= 2) {
2331
- const status = error?.response?.status
2332
- const errorMsg = error?.response?.data?.error?.reason ||
2333
- error?.response?.data?.message ||
2334
- error?.message ||
2335
- 'Erro desconhecido'
2336
- console.error('❌ Falha ao enviar log para OpenSearch', { status, message: errorMsg })
2337
- if (error?.code) console.error('⚙️ Código de erro:', error.code)
2334
+ const status = error?.response?.status
2335
+ const rawMsg = error?.response?.data?.error?.reason ||
2336
+ error?.response?.data?.error?.type ||
2337
+ error?.response?.data?.message ||
2338
+ error?.message ||
2339
+ 'Erro desconhecido'
2340
+ const errorMsg = typeof rawMsg === 'string' ? rawMsg.slice(0, 800) : String(rawMsg).slice(0, 800)
2341
+ const logPayload = {
2342
+ index: job.indexName,
2343
+ serviceName: job.serviceName,
2344
+ status,
2345
+ message: errorMsg
2346
+ }
2347
+ console.error('❌ Falha ao enviar log para OpenSearch', logPayload)
2348
+ if (error?.code && _openSearchFailuresInRow <= 10) {
2349
+ console.error('⚙️ Código de erro:', error.code)
2338
2350
  }
2339
2351
  })
2340
2352
  .finally(() => {