@trojs/logger 2.1.1 → 2.2.0

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@trojs/logger",
3
3
  "description": "Winston logger for TroJS",
4
- "version": "2.1.1",
4
+ "version": "2.2.0",
5
5
  "author": {
6
6
  "name": "Pieter Wigboldus",
7
7
  "url": "https://trojs.org/"
@@ -68,5 +68,8 @@
68
68
  "funding": {
69
69
  "type": "github",
70
70
  "url": "https://github.com/sponsors/w3nl"
71
+ },
72
+ "overrides": {
73
+ "eslint-plugin-sonarjs@=3.0.6": "3.0.5"
71
74
  }
72
75
  }
@@ -1,8 +1,54 @@
1
1
  /* eslint-disable no-param-reassign */
2
+ /* eslint sonarjs/cognitive-complexity: ["error", 16] */
2
3
  import stackDriver from '../helpers/stackdriver.js'
3
4
 
4
5
  const SYMBOL_MESSAGE = Symbol.for('message')
5
6
 
7
+ const safeJsonReplacer = (maxDepth = 5, maxStringLength = 1000) => {
8
+ const parentMap = new WeakMap()
9
+ const depthMap = new WeakMap()
10
+
11
+ return function (key, value) {
12
+ // Handle circular references and depth tracking
13
+ if (typeof value === 'object' && value !== null) {
14
+ // Detect cycles by walking up the parent chain
15
+ let current = this
16
+ while (current && typeof current === 'object') {
17
+ if (current === value) {
18
+ return '[Circular]'
19
+ }
20
+ current = parentMap.get(current)
21
+ }
22
+
23
+ // Record parent for this value
24
+ if (this && typeof this === 'object') {
25
+ parentMap.set(value, this)
26
+ }
27
+ // Track depth per object
28
+ const parentDepth = depthMap.get(this) || 0
29
+ const currentDepth = parentDepth + 1
30
+
31
+ if (currentDepth > maxDepth) {
32
+ return '[Max Depth Exceeded]'
33
+ }
34
+
35
+ depthMap.set(value, currentDepth)
36
+ }
37
+
38
+ // Truncate long strings
39
+ if (typeof value === 'string' && value.length > maxStringLength) {
40
+ return `${value.substring(0, maxStringLength)}... [truncated]`
41
+ }
42
+
43
+ // Handle functions
44
+ if (typeof value === 'function') {
45
+ return '[Function]'
46
+ }
47
+
48
+ return value
49
+ }
50
+ }
51
+
6
52
  export default ({ winston, logger }) => {
7
53
  const defaultLevel = 'trace'
8
54
  const stackTrace = logger?.debug ?? false
@@ -109,7 +155,33 @@ export default ({ winston, logger }) => {
109
155
  winston.format.errors({ stack: stackTrace }),
110
156
  normalizeMessage(),
111
157
  winston.format(stackDriver({ level: logger?.level, defaultLevel }))(),
112
- winston.format.json()
158
+ winston.format((info) => {
159
+ // Safe JSON serialization with error handling
160
+ try {
161
+ const serialized = JSON.stringify(info, safeJsonReplacer(5, 1000))
162
+ return { ...info, [SYMBOL_MESSAGE]: serialized }
163
+ } catch (error) {
164
+ // Fallback for serialization errors
165
+ let safeSymbolMessage
166
+ try {
167
+ const fallbackPayload = {
168
+ message: info?.message,
169
+ level: info?.level,
170
+ error: 'Failed to serialize log entry'
171
+ }
172
+ safeSymbolMessage = JSON.stringify(fallbackPayload, safeJsonReplacer(2, 500))
173
+ } catch {
174
+ safeSymbolMessage = '{"error":"Failed to serialize log entry"}'
175
+ }
176
+
177
+ return {
178
+ level: info?.level,
179
+ message: info?.message || 'Serialization error',
180
+ error: error?.message || 'Unknown serialization error',
181
+ [SYMBOL_MESSAGE]: safeSymbolMessage
182
+ }
183
+ }
184
+ })()
113
185
  )
114
186
 
115
187
  const simpleFormatter = winston.format.combine(