+function getLoggerReplacer () {
+ const seen = new WeakSet()
+
+ // Thanks: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value#Examples
+ return (key: string, value: any) => {
+ if (typeof value === 'object' && value !== null) {
+ if (seen.has(value)) return
+
+ seen.add(value)
+ }
+
+ if (value instanceof Error) {
+ const error = {}
+
+ Object.getOwnPropertyNames(value).forEach(key => { error[key] = value[key] })
+
+ return error
+ }
+
+ return value
+ }
+}
+
+const consoleLoggerFormat = winston.format.printf(info => {
+ const obj = omit(info, 'label', 'timestamp', 'level', 'message')
+
+ let additionalInfos = JSON.stringify(obj, getLoggerReplacer(), 2)
+
+ if (additionalInfos === undefined || additionalInfos === '{}') additionalInfos = ''
+ else additionalInfos = ' ' + additionalInfos
+
+ return `[${info.label}] ${info.timestamp} ${info.level}: ${info.message}${additionalInfos}`
+})
+
+const jsonLoggerFormat = winston.format.printf(info => {
+ return JSON.stringify(info, getLoggerReplacer())
+})
+
+const timestampFormatter = winston.format.timestamp({
+ format: 'YYYY-MM-DD HH:mm:ss.SSS'
+})
+const labelFormatter = winston.format.label({
+ label
+})
+
+const fileLoggerOptions: FileTransportOptions = {
+ filename: path.join(CONFIG.STORAGE.LOG_DIR, LOG_FILENAME),
+ handleExceptions: true,
+ format: winston.format.combine(
+ winston.format.timestamp(),
+ jsonLoggerFormat
+ )
+}
+
+if (CONFIG.LOG.ROTATION.ENABLED) {
+ fileLoggerOptions.maxsize = CONFIG.LOG.ROTATION.MAX_FILE_SIZE
+ fileLoggerOptions.maxFiles = CONFIG.LOG.ROTATION.MAX_FILES
+}
+
+const logger = winston.createLogger({
+ level: CONFIG.LOG.LEVEL,
+ format: winston.format.combine(
+ labelFormatter,
+ winston.format.splat()
+ ),