npm i morgan winston
npm i -D @types/morgan
import morgan, { StreamOptions } from 'morgan';
import winston from 'winston';
import { currentENV } from '../utils';
// This parameter returns 'deveopment' or other NODE_ENV
// export const currentENV = process.env.NODE_ENV || 'development';
const levels: { [key: string]: number } = {
error: 0,
warn: 1,
info: 2,
http: 3,
debug: 4
};
// write only error log in development mode, write all logs in other mode
const level = () => {
const env = currentENV;
const isDevelopment = env === 'development';
return isDevelopment ? 'error' : 'debug';
};
type IWinstonColors = 'red' | 'yellow' | 'green' | 'magenta' | 'white';
const colors: { [key: string]: IWinstonColors } = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'white'
};
winston.addColors(colors);
const format = winston.format.combine(
winston.format.timestamp({ format: 'DD-MM-YYYY HH:mm:ss' }),
winston.format.printf(
msg => `${[msg.timestamp]} ${msg.level}: ${msg.message}`
)
);
// only add color in console
// because if we add color in log file, the format will be unreadable
const transports = [
new winston.transports.Console({
format: winston.format.combine(winston.format.colorize({ all: true })),
level: 'debug'
}),
new winston.transports.File({
filename: 'error.log',
level: 'error'
}),
new winston.transports.File({ filename: 'all.log' })
];
export const logger = winston.createLogger({
level: level(),
levels,
format,
transports
});
const stream: StreamOptions = {
write: message => logger.http(message) // morgan will log the http request
// so we use 'http' level for logging
};
export const morganMiddleware = morgan(
':method :url :status :res[content-length] - :response-time ms - :remote-addr',
// Options: in this case, I overwrote the stream and the skip logic.
// See the methods above.
{ stream }
);
...
import { logger, morganMiddleware } from './config/winston';
....
const app = express();
app.use(cors());
app.use(morganMiddleware); // use log middleware before other route settings
// otherwise it will behave strangely
// only routes which aren't defined will trigger the log
// Body parser middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use('/api', routes);
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.enable('trust proxy');
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
const message = `server running on port ${PORT}`;
logger.info(message); // log info
});
(async () => {
try {
const options = await getConnectionOptions(currentENV);
await createConnection({
...options,
name: 'default',
synchronize: false
});
const message = 'database ok';
logger.info(message); // log info
} catch (e) {
console.log(e);
const message = 'database connection failed!';
logger.error(message); // log error
}
})();
import { response, Response } from 'express';
import { ICustomResponse, IResponseFormat } from '../../@types';
import { logger } from '../../config/winston';
response.customResponse = function (
httpStatusCode: ICustomResponse['httpStatusCode'],
message: ICustomResponse['message'],
data: ICustomResponse['data'] = null,
errors: ICustomResponse['error'] = null
): Response {
const res: IResponseFormat = {
status: httpStatusCode,
code: httpStatusCode === 200 ? 1 : 0,
message
};
const validData = data && (typeof data === 'object' || Array.isArray(data));
if (validData) {
res.data = data;
}
if (errors) {
res.error = errors;
}
if (res.code === 0) {
let errorMessage = message;
if (errors && typeof errors === 'object') {
errorMessage = `${errorMessage} - ${JSON.stringify(errors)}`;
}
logger.error(errorMessage); // Use here to log the error message
}
return this.status(httpStatusCode).json(res);
};