logger.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. const winston = require('winston');
  2. const path = require('path');
  3. // Define log levels
  4. const logLevels = {
  5. error: 0,
  6. warn: 1,
  7. info: 2,
  8. http: 3,
  9. debug: 4,
  10. };
  11. // Define log colors
  12. const logColors = {
  13. error: 'red',
  14. warn: 'yellow',
  15. info: 'green',
  16. http: 'magenta',
  17. debug: 'white',
  18. };
  19. winston.addColors(logColors);
  20. // Create logs directory if it doesn't exist
  21. const fs = require('fs');
  22. const logDir = path.join(__dirname, '../logs');
  23. if (!fs.existsSync(logDir)) {
  24. fs.mkdirSync(logDir, { recursive: true });
  25. }
  26. // Define log format
  27. const logFormat = winston.format.combine(
  28. winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
  29. winston.format.errors({ stack: true }),
  30. winston.format.json(),
  31. winston.format.printf(({ timestamp, level, message, stack, ...meta }) => {
  32. let log = `${timestamp} [${level.toUpperCase()}]: ${message}`;
  33. if (Object.keys(meta).length > 0) {
  34. log += ` | ${JSON.stringify(meta)}`;
  35. }
  36. if (stack) {
  37. log += `\n${stack}`;
  38. }
  39. return log;
  40. })
  41. );
  42. // Create console format for development
  43. const consoleFormat = winston.format.combine(
  44. winston.format.colorize({ all: true }),
  45. winston.format.timestamp({ format: 'HH:mm:ss' }),
  46. winston.format.printf(({ timestamp, level, message, ...meta }) => {
  47. let log = `${timestamp} [${level}]: ${message}`;
  48. if (Object.keys(meta).length > 0) {
  49. log += ` | ${JSON.stringify(meta)}`;
  50. }
  51. return log;
  52. })
  53. );
  54. // Create the logger instance
  55. const logger = winston.createLogger({
  56. level: process.env.LOG_LEVEL || 'info',
  57. levels: logLevels,
  58. format: logFormat,
  59. defaultMeta: { service: 'financial-data-server' },
  60. transports: [
  61. // Write all logs with importance level of `error` or less to `error.log`
  62. new winston.transports.File({
  63. filename: path.join(logDir, 'error.log'),
  64. level: 'error',
  65. maxsize: 5242880, // 5MB
  66. maxFiles: 5,
  67. }),
  68. // Write all logs with importance level of `info` or less to `combined.log`
  69. new winston.transports.File({
  70. filename: path.join(logDir, 'combined.log'),
  71. maxsize: 5242880, // 5MB
  72. maxFiles: 5,
  73. }),
  74. ],
  75. });
  76. // If we're not in production, log to the console as well
  77. if (process.env.NODE_ENV !== 'production') {
  78. logger.add(new winston.transports.Console({
  79. format: consoleFormat,
  80. level: process.env.LOG_LEVEL || 'debug',
  81. }));
  82. }
  83. // Create a stream object for Morgan HTTP logger
  84. const stream = {
  85. write: (message) => {
  86. logger.http(message.trim());
  87. },
  88. };
  89. // Handle uncaught exceptions and unhandled rejections
  90. logger.exceptions.handle(
  91. new winston.transports.File({
  92. filename: path.join(logDir, 'exceptions.log')
  93. })
  94. );
  95. logger.rejections.handle(
  96. new winston.transports.File({
  97. filename: path.join(logDir, 'rejections.log')
  98. })
  99. );
  100. // Graceful shutdown
  101. process.on('SIGTERM', () => {
  102. logger.info('SIGTERM received, shutting down gracefully');
  103. logger.end(() => {
  104. process.exit(0);
  105. });
  106. });
  107. process.on('SIGINT', () => {
  108. logger.info('SIGINT received, shutting down gracefully');
  109. logger.end(() => {
  110. process.exit(0);
  111. });
  112. });
  113. module.exports = {
  114. logger,
  115. stream
  116. };