aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/logger.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/helpers/logger.ts')
-rw-r--r--server/helpers/logger.ts101
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/
2import { stat } from 'fs-extra' 1import { stat } from 'fs-extra'
3import { omit } from 'lodash' 2import { omit } from 'lodash'
4import { join } from 'path' 3import { join } from 'path'
5import { format as sqlFormat } from 'sql-formatter' 4import { format as sqlFormat } from 'sql-formatter'
6import { createLogger, format, transports } from 'winston' 5import { createLogger, format, transports } from 'winston'
7import { FileTransportOptions } from 'winston/lib/winston/transports' 6import { FileTransportOptions } from 'winston/lib/winston/transports'
7import { context } from '@opentelemetry/api'
8import { getSpanContext } from '@opentelemetry/api/build/src/trace/context-utils'
8import { CONFIG } from '../initializers/config' 9import { CONFIG } from '../initializers/config'
9import { LOG_FILENAME } from '../initializers/constants' 10import { LOG_FILENAME } from '../initializers/constants'
10 11
11const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT 12const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
12 13
13function 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
46const consoleLoggerFormat = format.printf(info => { 14const 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
70const jsonLoggerFormat = format.printf(info => { 34const jsonLoggerFormat = format.printf(info => {
71 return JSON.stringify(info, getLoggerReplacer()) 35 return JSON.stringify(info, removeCyclicValues())
72}) 36})
73 37
74const timestampFormatter = format.timestamp({ 38const 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
97const logger = buildLogger()
98
99function buildLogger (labelSuffix?: string) { 61function 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
88const logger = buildLogger()
89
90// ---------------------------------------------------------------------------
91
121function bunyanLogFactory (level: string) { 92function 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
150type LoggerTagsFn = (...tags: string[]) => { tags: string[] } 124type LoggerTagsFn = (...tags: string[]) => { tags: string[] }
151function loggerTagsFactory (...defaultTags: string[]): LoggerTagsFn { 125function 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
157async function mtimeSortFilesDesc (files: string[], basePath: string) { 133async 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
171function 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
204function getAdditionalInfo (info: any) {
205 const toOmit = [ 'label', 'timestamp', 'level', 'message', 'sql', 'tags' ]
206
207 return omit(info, ...toOmit)
208}