azify-logger 1.0.43 → 1.0.45

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/store.js CHANGED
@@ -1,6 +1,15 @@
1
1
  const { AsyncLocalStorage } = require('async_hooks')
2
2
 
3
3
  const als = new AsyncLocalStorage()
4
+ let lastJobContext = null
5
+
6
+ function setLastJobContext(ctx) {
7
+ lastJobContext = ctx || null
8
+ }
9
+
10
+ function getLastJobContext() {
11
+ return lastJobContext
12
+ }
4
13
 
5
14
  function fastGenerateId(length = 16) {
6
15
  const chars = '0123456789abcdef'
@@ -12,21 +21,22 @@ function fastGenerateId(length = 16) {
12
21
  return result
13
22
  }
14
23
 
15
- function toTraceId(hex32) {
16
- const h = (hex32 || '').padStart(32, '0').slice(0, 32)
17
- return `${h.substring(0, 8)}-${h.substring(8, 12)}-${h.substring(12, 16)}-${h.substring(16, 20)}-${h.substring(20, 32)}`
24
+ function toTraceIdHex(hex32) {
25
+ const raw = hex32 != null && typeof hex32 === 'string' ? String(hex32).replace(/[^0-9a-fA-F]/g, '').slice(0, 32) : ''
26
+ const hex = raw || fastGenerateId(16)
27
+ return hex.padStart(32, '0').slice(0, 32)
18
28
  }
19
29
 
20
- function startRequestContext(initial = {}) {
30
+ function startRequestContext(initial) {
31
+ if (!initial || typeof initial !== 'object') initial = {}
21
32
  const traceHex = initial.traceHex || fastGenerateId(16)
22
33
  const spanHex = initial.spanHex || fastGenerateId(8)
23
- const ctx = {
24
- traceId: toTraceId(traceHex),
25
- spanId: spanHex,
26
- parentSpanId: initial.parentSpanId || null,
27
- requestId: initial.requestId
34
+ return {
35
+ traceId: toTraceIdHex(traceHex),
36
+ spanId: spanHex != null && spanHex !== '' ? String(spanHex).slice(0, 16) : fastGenerateId(8),
37
+ parentSpanId: initial.parentSpanId != null && initial.parentSpanId !== '' ? String(initial.parentSpanId) : null,
38
+ requestId: initial.requestId != null && initial.requestId !== '' ? String(initial.requestId) : null
28
39
  }
29
- return ctx
30
40
  }
31
41
 
32
42
  function runWithRequestContext(ctx, fn) {
@@ -37,11 +47,27 @@ function getRequestContext() {
37
47
  return als.getStore() || null
38
48
  }
39
49
 
50
+ function mergeTraceIntoJobPayload(data, traceContext) {
51
+ if (!traceContext || !traceContext.traceId) return data
52
+ const base = data && typeof data === 'object' && !Array.isArray(data) ? data : {}
53
+ return {
54
+ ...base,
55
+ traceId: traceContext.traceId,
56
+ spanId: traceContext.spanId ?? null,
57
+ parentSpanId: traceContext.parentSpanId ?? null,
58
+ requestId: traceContext.requestId ?? null
59
+ }
60
+ }
61
+
40
62
  module.exports = {
41
63
  als,
42
64
  startRequestContext,
43
65
  runWithRequestContext,
44
- getRequestContext
66
+ getRequestContext,
67
+ getLastJobContext,
68
+ setLastJobContext,
69
+ mergeTraceIntoJobPayload,
70
+ toTraceIdHex
45
71
  }
46
72
 
47
73
 
@@ -84,7 +84,7 @@ function resolveRedisConfig (overrides = {}) {
84
84
  (overrides.redis && overrides.redis.url) ||
85
85
  process.env.AZIFY_LOGGER_REDIS_URL ||
86
86
  DEFAULT_REDIS_URL
87
-
87
+
88
88
  if (!redisUrl) {
89
89
  return null
90
90
  }
@@ -108,11 +108,13 @@ function createHttpLoggerTransport (loggerUrl, overrides) {
108
108
  if (!loggerUrl) {
109
109
  return noopTransport
110
110
  }
111
+ overrides = overrides || {}
111
112
 
112
113
  const options = resolveOptions(overrides)
113
114
  const redisConfig = resolveRedisConfig(overrides)
115
+ const hasRedisPassword = redisConfig && redisConfig.password != null && String(redisConfig.password).trim() !== ''
114
116
 
115
- if (redisConfig) {
117
+ if (redisConfig && hasRedisPassword) {
116
118
  try {
117
119
  return createRedisStreamTransport(loggerUrl, options, redisConfig)
118
120
  } catch (err) {
@@ -124,9 +126,7 @@ function createHttpLoggerTransport (loggerUrl, overrides) {
124
126
  }
125
127
 
126
128
  const redisUrl = overrides.redisUrl || (overrides.redis && overrides.redis.url) || process.env.AZIFY_LOGGER_REDIS_URL || DEFAULT_REDIS_URL
127
- const password = overrides.redisPassword ?? (overrides.redis && overrides.redis.password) ?? process.env.AZIFY_LOGGER_REDIS_PASSWORD
128
- const pass = password != null && String(password).trim() !== '' ? String(password).trim() : undefined
129
- if (redisUrl && !pass) {
129
+ if (redisUrl && !hasRedisPassword) {
130
130
  if (typeof global.__azifyLoggerRedisPasswordWarned === 'undefined') {
131
131
  global.__azifyLoggerRedisPasswordWarned = true
132
132
  process.stderr.write('[azify-logger] ⚠️ Redis requires a password. No password set. Using direct HTTP (no Redis). Set AZIFY_LOGGER_REDIS_PASSWORD to use Redis.\n')
@@ -250,7 +250,7 @@ function buildInlineTransport (loggerUrl, options) {
250
250
 
251
251
  flushTimer = setTimeout(() => {
252
252
  flushTimer = null
253
- void flushQueue()
253
+ flushQueue().catch(() => {})
254
254
  }, delay)
255
255
 
256
256
  if (typeof flushTimer.unref === 'function') {
@@ -278,9 +278,15 @@ function buildInlineTransport (loggerUrl, options) {
278
278
  try {
279
279
  for (let i = 0; i < batch.length; i += 1) {
280
280
  const { payload, headers } = batch[i]
281
+ let body
282
+ try {
283
+ body = typeof payload === 'string' ? payload : JSON.stringify(payload)
284
+ } catch (_) {
285
+ continue
286
+ }
281
287
  try {
282
- await axios.post(loggerUrl, payload, {
283
- headers,
288
+ await axios.post(loggerUrl, body, {
289
+ headers: { ...headers, 'Content-Type': 'application/json' },
284
290
  timeout: options.timeout,
285
291
  httpAgent,
286
292
  httpsAgent,
@@ -298,11 +304,9 @@ function buildInlineTransport (loggerUrl, options) {
298
304
  if (remaining.length) {
299
305
  queue.unshift(...remaining)
300
306
  }
301
-
302
- throw error
303
307
  }
304
308
  }
305
- } catch (_) {
309
+ } catch (e) {
306
310
  } finally {
307
311
  flushing = false
308
312
  if (queue.length) {
@@ -311,6 +315,12 @@ function buildInlineTransport (loggerUrl, options) {
311
315
  }
312
316
  }
313
317
 
318
+ const isRequestOrResponsePayload = (p) => {
319
+ if (!p || typeof p !== 'object') return false
320
+ const msg = typeof p.message === 'string' ? p.message : ''
321
+ return msg.includes('[REQUEST]') || msg.includes('[RESPONSE]')
322
+ }
323
+
314
324
  const enqueue = (payload, headers = {}) => {
315
325
  if (queue.length >= options.maxQueueSize) {
316
326
  queue.shift()
@@ -319,7 +329,9 @@ function buildInlineTransport (loggerUrl, options) {
319
329
  queue.push({ payload, headers })
320
330
 
321
331
  if (queue.length >= options.batchSize) {
322
- void flushQueue()
332
+ flushQueue().catch(() => {})
333
+ } else if (isRequestOrResponsePayload(payload)) {
334
+ scheduleFlush(0)
323
335
  } else {
324
336
  scheduleFlush()
325
337
  }
package/streams/pino.js CHANGED
@@ -34,21 +34,65 @@ function createPinoStream(options = {}) {
34
34
  const levelMap = { 60: 'fatal', 50: 'error', 40: 'warn', 30: 'info', 20: 'debug', 10: 'trace' }
35
35
  const level = levelMap[record.level] || 'info'
36
36
 
37
- let traceId, spanId
37
+ let traceId, spanId, parentSpanId
38
38
  try {
39
- const { getRequestContext } = require('../store')
39
+ const { getRequestContext, getLastJobContext, toTraceIdHex } = require('../store')
40
40
  const ctx = getRequestContext()
41
41
  if (ctx && ctx.traceId) {
42
42
  traceId = ctx.traceId
43
43
  spanId = ctx.spanId
44
- } else {
44
+ parentSpanId = ctx.parentSpanId
45
+ }
46
+
47
+ if (!traceId) {
48
+ const jobCtx = getLastJobContext()
49
+ if (jobCtx && jobCtx.traceId) {
50
+ traceId = jobCtx.traceId
51
+ spanId = jobCtx.spanId
52
+ parentSpanId = jobCtx.parentSpanId
53
+ }
54
+ }
55
+
56
+ if (!traceId && (record.traceId || (record.data && record.data.traceId))) {
57
+ traceId = record.traceId || (record.data && record.data.traceId)
58
+ spanId = record.spanId != null ? record.spanId : (record.data && record.data.spanId)
59
+ parentSpanId = record.parentSpanId != null ? record.parentSpanId : (record.data && record.data.parentSpanId)
60
+ }
61
+
62
+ if (!traceId && record.req && record.req.traceId) {
63
+ traceId = record.req.traceId
64
+ spanId = record.req.spanId
65
+ parentSpanId = record.req.parentSpanId
66
+ }
67
+
68
+ if (!traceId) {
69
+ const msgStr = record.msg || record.message || ''
70
+ if (typeof msgStr === 'string' && /traceId/i.test(msgStr)) {
71
+ const m = msgStr.match(/"traceId"\s*:\s*"([^"]+)"/)
72
+ if (m) {
73
+ traceId = m[1]
74
+ const s = msgStr.match(/"spanId"\s*:\s*"([^"]+)"/)
75
+ spanId = s ? s[1] : null
76
+ const p = msgStr.match(/"parentSpanId"\s*:\s*"([^"]*)"/)
77
+ parentSpanId = (p && p[1]) ? p[1] : null
78
+ }
79
+ }
80
+ }
81
+
82
+ if (!traceId) {
45
83
  const span = trace.getSpan(context.active())
46
84
  const spanContext = span && span.spanContext()
47
- if (spanContext) {
85
+ if (spanContext && spanContext.traceId) {
48
86
  traceId = spanContext.traceId
49
87
  spanId = spanContext.spanId
88
+ parentSpanId = spanContext.parentSpanId
50
89
  }
51
90
  }
91
+
92
+ if (traceId && typeof traceId === 'string') {
93
+ traceId = toTraceIdHex(traceId)
94
+ if (spanId != null) spanId = String(spanId).slice(0, 16)
95
+ }
52
96
  } catch (_) {}
53
97
 
54
98
  const headers = {}
@@ -70,15 +114,23 @@ function createPinoStream(options = {}) {
70
114
  hostname: require('os').hostname(),
71
115
  requestId: record.requestId || record.req_id || record.reqId,
72
116
  ...(traceId && { traceId }),
73
- ...(spanId && { spanId })
117
+ ...(spanId && { spanId }),
118
+ ...(parentSpanId && { parentSpanId })
74
119
  }
75
120
 
76
- if (!shouldSample(level, 'logger')) {
121
+ const isErrorOrWarn = level === 'error' || level === 'fatal' || level === 'warn'
122
+ if (!isErrorOrWarn && !shouldSample(level, 'logger')) {
77
123
  return
78
124
  }
79
125
 
80
126
  const payload = { level, message: record.msg || record.message || 'log', meta }
81
- transport.enqueue(payload, headers)
127
+ try {
128
+ transport.enqueue(payload, headers)
129
+ } catch (err) {
130
+ if (process.env.AZIFY_LOGGER_DEBUG === '1') {
131
+ process.stderr.write(`[azify-logger] pino stream enqueue error: ${err && err.message}\n`)
132
+ }
133
+ }
82
134
  }
83
135
  }
84
136
  }