diff options
Diffstat (limited to 'server/helpers/logger.ts')
-rw-r--r-- | server/helpers/logger.ts | 101 |
1 files changed, 59 insertions, 42 deletions
diff --git a/server/helpers/logger.ts b/server/helpers/logger.ts index 4fbaf8a73..9625c1b33 100644 --- a/server/helpers/logger.ts +++ b/server/helpers/logger.ts | |||
@@ -1,54 +1,18 @@ | |||
1 | // Thanks http://tostring.it/2014/06/23/advanced-logging-with-nodejs/ | ||
2 | import { stat } from 'fs-extra' | 1 | import { stat } from 'fs-extra' |
3 | import { omit } from 'lodash' | 2 | import { omit } from 'lodash' |
4 | import { join } from 'path' | 3 | import { join } from 'path' |
5 | import { format as sqlFormat } from 'sql-formatter' | 4 | import { format as sqlFormat } from 'sql-formatter' |
6 | import { createLogger, format, transports } from 'winston' | 5 | import { createLogger, format, transports } from 'winston' |
7 | import { FileTransportOptions } from 'winston/lib/winston/transports' | 6 | import { FileTransportOptions } from 'winston/lib/winston/transports' |
7 | import { context } from '@opentelemetry/api' | ||
8 | import { getSpanContext } from '@opentelemetry/api/build/src/trace/context-utils' | ||
8 | import { CONFIG } from '../initializers/config' | 9 | import { CONFIG } from '../initializers/config' |
9 | import { LOG_FILENAME } from '../initializers/constants' | 10 | import { LOG_FILENAME } from '../initializers/constants' |
10 | 11 | ||
11 | const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | 12 | const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT |
12 | 13 | ||
13 | function getLoggerReplacer () { | ||
14 | const seen = new WeakSet() | ||
15 | |||
16 | // Thanks: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#Examples | ||
17 | return (key: string, value: any) => { | ||
18 | if (key === 'cert') return 'Replaced by the logger to avoid large log message' | ||
19 | |||
20 | if (typeof value === 'object' && value !== null) { | ||
21 | if (seen.has(value)) return | ||
22 | |||
23 | seen.add(value) | ||
24 | } | ||
25 | |||
26 | if (value instanceof Set) { | ||
27 | return Array.from(value) | ||
28 | } | ||
29 | |||
30 | if (value instanceof Map) { | ||
31 | return Array.from(value.entries()) | ||
32 | } | ||
33 | |||
34 | if (value instanceof Error) { | ||
35 | const error = {} | ||
36 | |||
37 | Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] }) | ||
38 | |||
39 | return error | ||
40 | } | ||
41 | |||
42 | return value | ||
43 | } | ||
44 | } | ||
45 | |||
46 | const consoleLoggerFormat = format.printf(info => { | 14 | const consoleLoggerFormat = format.printf(info => { |
47 | const toOmit = [ 'label', 'timestamp', 'level', 'message', 'sql', 'tags' ] | 15 | let additionalInfos = JSON.stringify(getAdditionalInfo(info), removeCyclicValues(), 2) |
48 | |||
49 | const obj = omit(info, ...toOmit) | ||
50 | |||
51 | let additionalInfos = JSON.stringify(obj, getLoggerReplacer(), 2) | ||
52 | 16 | ||
53 | if (additionalInfos === undefined || additionalInfos === '{}') additionalInfos = '' | 17 | if (additionalInfos === undefined || additionalInfos === '{}') additionalInfos = '' |
54 | else additionalInfos = ' ' + additionalInfos | 18 | else additionalInfos = ' ' + additionalInfos |
@@ -68,7 +32,7 @@ const consoleLoggerFormat = format.printf(info => { | |||
68 | }) | 32 | }) |
69 | 33 | ||
70 | const jsonLoggerFormat = format.printf(info => { | 34 | const jsonLoggerFormat = format.printf(info => { |
71 | return JSON.stringify(info, getLoggerReplacer()) | 35 | return JSON.stringify(info, removeCyclicValues()) |
72 | }) | 36 | }) |
73 | 37 | ||
74 | const timestampFormatter = format.timestamp({ | 38 | const timestampFormatter = format.timestamp({ |
@@ -94,11 +58,14 @@ if (CONFIG.LOG.ROTATION.ENABLED) { | |||
94 | fileLoggerOptions.maxFiles = CONFIG.LOG.ROTATION.MAX_FILES | 58 | fileLoggerOptions.maxFiles = CONFIG.LOG.ROTATION.MAX_FILES |
95 | } | 59 | } |
96 | 60 | ||
97 | const logger = buildLogger() | ||
98 | |||
99 | function buildLogger (labelSuffix?: string) { | 61 | function buildLogger (labelSuffix?: string) { |
100 | return createLogger({ | 62 | return createLogger({ |
101 | level: CONFIG.LOG.LEVEL, | 63 | level: CONFIG.LOG.LEVEL, |
64 | defaultMeta: { | ||
65 | get traceId () { return getSpanContext(context.active())?.traceId }, | ||
66 | get spanId () { return getSpanContext(context.active())?.spanId }, | ||
67 | get traceFlags () { return getSpanContext(context.active())?.traceFlags } | ||
68 | }, | ||
102 | format: format.combine( | 69 | format: format.combine( |
103 | labelFormatter(labelSuffix), | 70 | labelFormatter(labelSuffix), |
104 | format.splat() | 71 | format.splat() |
@@ -118,6 +85,10 @@ function buildLogger (labelSuffix?: string) { | |||
118 | }) | 85 | }) |
119 | } | 86 | } |
120 | 87 | ||
88 | const logger = buildLogger() | ||
89 | |||
90 | // --------------------------------------------------------------------------- | ||
91 | |||
121 | function bunyanLogFactory (level: string) { | 92 | function bunyanLogFactory (level: string) { |
122 | return function (...params: any[]) { | 93 | return function (...params: any[]) { |
123 | let meta = null | 94 | let meta = null |
@@ -141,12 +112,15 @@ const bunyanLogger = { | |||
141 | level: () => { }, | 112 | level: () => { }, |
142 | trace: bunyanLogFactory('debug'), | 113 | trace: bunyanLogFactory('debug'), |
143 | debug: bunyanLogFactory('debug'), | 114 | debug: bunyanLogFactory('debug'), |
115 | verbose: bunyanLogFactory('debug'), | ||
144 | info: bunyanLogFactory('info'), | 116 | info: bunyanLogFactory('info'), |
145 | warn: bunyanLogFactory('warn'), | 117 | warn: bunyanLogFactory('warn'), |
146 | error: bunyanLogFactory('error'), | 118 | error: bunyanLogFactory('error'), |
147 | fatal: bunyanLogFactory('error') | 119 | fatal: bunyanLogFactory('error') |
148 | } | 120 | } |
149 | 121 | ||
122 | // --------------------------------------------------------------------------- | ||
123 | |||
150 | type LoggerTagsFn = (...tags: string[]) => { tags: string[] } | 124 | type LoggerTagsFn = (...tags: string[]) => { tags: string[] } |
151 | function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { | 125 | function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { |
152 | return (...tags: string[]) => { | 126 | return (...tags: string[]) => { |
@@ -154,6 +128,8 @@ function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { | |||
154 | } | 128 | } |
155 | } | 129 | } |
156 | 130 | ||
131 | // --------------------------------------------------------------------------- | ||
132 | |||
157 | async function mtimeSortFilesDesc (files: string[], basePath: string) { | 133 | async function mtimeSortFilesDesc (files: string[], basePath: string) { |
158 | const promises = [] | 134 | const promises = [] |
159 | const out: { file: string, mtime: number }[] = [] | 135 | const out: { file: string, mtime: number }[] = [] |
@@ -189,3 +165,44 @@ export { | |||
189 | loggerTagsFactory, | 165 | loggerTagsFactory, |
190 | bunyanLogger | 166 | bunyanLogger |
191 | } | 167 | } |
168 | |||
169 | // --------------------------------------------------------------------------- | ||
170 | |||
171 | function removeCyclicValues () { | ||
172 | const seen = new WeakSet() | ||
173 | |||
174 | // Thanks: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#Examples | ||
175 | return (key: string, value: any) => { | ||
176 | if (key === 'cert') return 'Replaced by the logger to avoid large log message' | ||
177 | |||
178 | if (typeof value === 'object' && value !== null) { | ||
179 | if (seen.has(value)) return | ||
180 | |||
181 | seen.add(value) | ||
182 | } | ||
183 | |||
184 | if (value instanceof Set) { | ||
185 | return Array.from(value) | ||
186 | } | ||
187 | |||
188 | if (value instanceof Map) { | ||
189 | return Array.from(value.entries()) | ||
190 | } | ||
191 | |||
192 | if (value instanceof Error) { | ||
193 | const error = {} | ||
194 | |||
195 | Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] }) | ||
196 | |||
197 | return error | ||
198 | } | ||
199 | |||
200 | return value | ||
201 | } | ||
202 | } | ||
203 | |||
204 | function getAdditionalInfo (info: any) { | ||
205 | const toOmit = [ 'label', 'timestamp', 'level', 'message', 'sql', 'tags' ] | ||
206 | |||
207 | return omit(info, ...toOmit) | ||
208 | } | ||