177 lines
4.2 KiB
TypeScript

/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import chalk from 'chalk';
import winston from 'winston';
import { getLoggerFormat } from './config';
import { LoggerOptions } from './logger';
import { isEmpty } from 'lodash';
const DEFAULT_DELIMITER = '|';
const colorize = {};
/**
* @internal
*/
export const getFormat = (format?: LoggerOptions['format']) => {
const configFormat = format || getLoggerFormat();
let logFormat: winston.Logform.Format;
switch (configFormat) {
case 'console':
logFormat = winston.format.combine(consoleFormat);
break;
case 'logfmt':
logFormat = logfmtFormat;
break;
case 'delimiter':
logFormat = winston.format.combine(escapeFormat, delimiterFormat);
break;
case 'json':
logFormat = winston.format.combine(winston.format.json({ deterministic: false }));
break;
default:
return winston.format.combine(format as winston.Logform.Format);
}
return winston.format.combine(sortFormat, logFormat);
};
/**
* @internal
*/
export const colorFormat: winston.Logform.Format = winston.format((info) => {
Object.entries(info).forEach(([k, v]) => {
const level = info['level'];
if (colorize[k]) {
info[k] = colorize[k](v);
return;
}
if (colorize[level]?.[k]) {
info[k] = colorize[level][k](v);
return;
}
});
return info;
})();
/**
* @internal
*/
export const stripColorFormat: winston.Logform.Format = winston.format((info) => {
Object.entries(info).forEach(([k, v]) => {
if (typeof v !== 'string') {
return;
}
const regex = new RegExp(`\\x1b\\[\\d+m`, 'g');
info[k] = v.replace(regex, '');
});
return info;
})();
/**
* @internal
*https://brandur.org/logfmt
*/
export const logfmtFormat: winston.Logform.Format = winston.format.printf((info) =>
Object.entries(info)
.map(([k, v]) => {
if (typeof v === 'object') {
try {
v = JSON.stringify(v);
} catch (error) {
v = String(v);
}
}
if (v === undefined || v === null) {
v = '';
}
return `${k}=${v}`;
})
.join(' '),
);
/**
* @internal
*/
export const consoleFormat: winston.Logform.Format = winston.format.printf((info) => {
const keys = ['level', 'timestamp', 'message'];
Object.entries(info).forEach(([k, v]) => {
if (typeof v === 'object') {
if (isEmpty(v)) {
info[k] = '';
return;
}
try {
info[k] = JSON.stringify(v);
} catch (error) {
info[k] = String(v);
}
}
if (v === undefined || v === null) {
info[k] = '';
}
});
const tags = Object.entries(info)
.filter(([k, v]) => !keys.includes(k) && v)
.map(([k, v]) => `${k}=${v}`)
.join(' ');
const level = `[${info.level}]`.padEnd(7, ' ');
const message = info.message.padEnd(44, ' ');
const color =
{
error: chalk.red,
warn: chalk.yellow,
info: chalk.green,
debug: chalk.blue,
trace: chalk.cyan,
}[info.level] || chalk.white;
const colorized = message.startsWith('Executing')
? color(`${info.timestamp} ${level}`) + ` ${message}`
: color(`${info.timestamp} ${level} ${message}`);
return `${colorized} ${tags}`;
});
/**
* @internal
*/
export const delimiterFormat = winston.format.printf((info) =>
Object.entries(info)
.map(([, v]) => {
if (typeof v === 'object') {
try {
return JSON.stringify(v);
} catch (error) {
return String(v);
}
}
return v;
})
.join(DEFAULT_DELIMITER),
);
/**
* @internal
*/
export const escapeFormat: winston.Logform.Format = winston.format((info) => {
let { message } = info;
if (typeof message === 'string' && message.includes(DEFAULT_DELIMITER)) {
message = message.replace(/"/g, '\\"');
message = `"${message}"`;
}
return { ...info, message };
})();
/**
* @internal
*/
export const sortFormat = winston.format((info) => ({ level: info.level, ...info }))();