Skip to content

feat: Implement production-ready logging with Winston#46

Merged
hexawulf merged 1 commit intomainfrom
feat/production-logging-winston
Jun 24, 2025
Merged

feat: Implement production-ready logging with Winston#46
hexawulf merged 1 commit intomainfrom
feat/production-logging-winston

Conversation

@hexawulf
Copy link
Copy Markdown
Owner

@hexawulf hexawulf commented Jun 24, 2025

  • Installed Winston and configured a logger module (server/src/logger/index.ts).
  • Logger includes JSON formatting, timestamps, error stack traces, and a 'service: markdownmate' meta field.
  • Configured file transports for combined logs (logs/combined.log) and error logs (logs/error.log).
  • Added conditional console transport with colorization for non-production environments.
  • Integrated the new logger into server/index.ts and server/routes/auth.ts, replacing previous console.log calls.
  • Created logs directory (user to ensure correct permissions in deployment).
  • Configured PM2 for log rotation (pm2-logrotate) with settings for retention, max size, and compression.

Summary by CodeRabbit

  • New Features

    • Introduced structured logging using Winston, providing enhanced and consistent log output across the application.
    • Log entries now include additional context such as request path, HTTP method, and client IP address.
    • Logs are saved to dedicated log files and, in non-production environments, are also displayed in a colorized, human-readable format in the console.
  • Chores

    • Updated dependencies to include the Winston logging library.

- Installed Winston and configured a logger module (`server/src/logger/index.ts`).
- Logger includes JSON formatting, timestamps, error stack traces, and a 'service: markdownmate' meta field.
- Configured file transports for combined logs (`logs/combined.log`) and error logs (`logs/error.log`).
- Added conditional console transport with colorization for non-production environments.
- Integrated the new logger into `server/index.ts` and `server/routes/auth.ts`, replacing previous `console.log` calls.
- Created `logs` directory (user to ensure correct permissions in deployment).
- Configured PM2 for log rotation (`pm2-logrotate`) with settings for retention, max size, and compression.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jun 24, 2025

Walkthrough

Logging across the server codebase was refactored to use a new Winston-based logger module, replacing all direct console logging. The logger is configured for structured, contextual logging with output to files and the console. Logging statements now include additional metadata for improved traceability.

Changes

File(s) Change Summary
package.json Added "winston" dependency version "^3.17.0".
server/index.ts, server/routes/auth.ts Replaced all console logging with Winston logger; added contextual metadata to logs; improved error logging.
server/src/logger/index.ts Introduced new Winston logger module with file and console transports, JSON formatting, and default metadata.

Poem

In the warren, logs now gleam,
With Winston’s touch, our traces beam.
Console calls, now in the past,
Structured logs are built to last.
Errors, infos, all in line,
In files and streams, our records shine.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@hexawulf hexawulf merged commit c5640de into main Jun 24, 2025
0 of 2 checks passed
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
server/routes/auth.ts (1)

96-96: Consider removing error details from client response.

While logging the full error details is good for debugging, exposing error.message in the client response might leak sensitive information in production.

Consider this approach for better security:

-      error: error.message // Keep original error message for client if needed, but logged with full details
+      error: process.env.NODE_ENV === 'development' ? error.message : 'Authentication failed'
server/index.ts (3)

68-68: Consider using 'info' level instead of 'debug' for API route tracking.

Debug level logs are typically disabled in production. If you want to track API routes in production, consider using 'info' level.

-    logger.debug(`API Route Hit: ${req.method} ${req.path}`, { path: req.path, method: req.method, ip: req.ip });
+    logger.info(`API Route Hit: ${req.method} ${req.path}`, { path: req.path, method: req.method, ip: req.ip });

152-153: Consider standardizing on Winston logger throughout.

There's mixed usage of the vite log function and winston logger. For consistency, consider whether all logging should go through winston.

If you want to standardize on winston for all logging:

-    // The 'log' function from vite.ts might still be useful for vite specific logs
-    // For general server logs, use the winston logger.
     logger.info(`Server re-attempting to listen on port ${port}`);
-    // The 'log' function from vite.ts seems specific to vite's output, let's keep it for that.
-    log(`serving on port ${port}`);
     logger.info(`Server started on port ${port}`);

However, keeping vite-specific logs separate might be intentional for development tooling.

Also applies to: 167-168


46-63: Consider migrating request logging to Winston.

The current middleware uses the vite log function for API request logging. For consistency with the new logging system, consider using winston here too.

   res.on("finish", () => {
     const duration = Date.now() - start;
     if (path.startsWith("/api") || path.startsWith("/user")) {
       let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
       if (capturedJsonResponse) {
         logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
       }

-      if (logLine.length > 80) {
-        logLine = logLine.slice(0, 79) + "…";
-      }
-
-      log(logLine);
+      logger.info(`API Request`, {
+        method: req.method,
+        path: path,
+        statusCode: res.statusCode,
+        duration: `${duration}ms`,
+        response: capturedJsonResponse,
+        ip: req.ip
+      });
     }
   });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2027f37 and 28072d4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json (1 hunks)
  • server/index.ts (8 hunks)
  • server/routes/auth.ts (7 hunks)
  • server/src/logger/index.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-and-deploy
🔇 Additional comments (7)
package.json (1)

99-99: Verify Winston version is current and secure.

The Winston version 3.17.0 looks reasonable, but let's confirm it's the latest stable version and check for any known security vulnerabilities.

What is the latest stable version of Winston npm package and are there any known security vulnerabilities in version 3.17.0?
server/routes/auth.ts (2)

3-3: Logger import looks correct.

The import path is appropriate for the new logger module structure.


9-9: Excellent structured logging implementation.

The logging statements provide excellent traceability with contextual metadata including request details and user IDs. The log levels are appropriate (info for successful operations, error for failures).

Also applies to: 26-26, 38-38, 60-60, 73-73, 93-93, 114-114

server/src/logger/index.ts (1)

32-48: Console transport configuration is well implemented.

The conditional console transport with colorization and custom formatting for non-production environments is excellent for development experience.

server/index.ts (3)

7-7: Logger import looks correct.

The import path matches the new logger module structure.


78-78: Excellent structured logging implementation.

The winston logger usage provides good structured logging with appropriate metadata and context throughout the server lifecycle.

Also applies to: 95-95, 104-104, 119-119, 128-128, 130-130, 136-136, 138-138, 145-145, 154-154, 158-158, 169-171


106-111: Good decision to comment out error re-throwing.

The explanation is clear and the decision to prevent potential unhandled promise rejections is sound. This prevents the server from crashing on errors while still logging them properly.

Comment on lines +6 to +8
// Ensure the logs directory exists (though typically this should be handled outside the logger)
// For this exercise, we'll assume it's handled by the setup script or deployment process.
const logsDir = path.join(process.cwd(), 'logs'); // Adjusted to be relative to project root, assuming server is run from project root
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure logs directory exists and handle path assumptions.

The comment acknowledges that directory creation should be handled elsewhere, but this creates a potential runtime failure if the directory doesn't exist.

Consider adding directory creation and more robust path handling:

+import fs from 'fs';
+
 // Ensure the logs directory exists (though typically this should be handled outside the logger)
 // For this exercise, we'll assume it's handled by the setup script or deployment process.
-const logsDir = path.join(process.cwd(), 'logs'); // Adjusted to be relative to project root, assuming server is run from project root
+const logsDir = path.join(process.cwd(), 'logs');
+
+// Create logs directory if it doesn't exist
+if (!fs.existsSync(logsDir)) {
+  fs.mkdirSync(logsDir, { recursive: true });
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Ensure the logs directory exists (though typically this should be handled outside the logger)
// For this exercise, we'll assume it's handled by the setup script or deployment process.
const logsDir = path.join(process.cwd(), 'logs'); // Adjusted to be relative to project root, assuming server is run from project root
import fs from 'fs';
// Ensure the logs directory exists (though typically this should be handled outside the logger)
// For this exercise, we'll assume it's handled by the setup script or deployment process.
const logsDir = path.join(process.cwd(), 'logs');
// Create logs directory if it doesn't exist
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
🤖 Prompt for AI Agents
In server/src/logger/index.ts around lines 6 to 8, the code assumes the 'logs'
directory exists but does not ensure its creation, risking runtime errors.
Modify the code to check if the 'logs' directory exists and create it if it does
not before using it. Use appropriate Node.js filesystem methods to synchronously
or asynchronously verify and create the directory, ensuring robust path handling
relative to the project root.

Comment on lines +10 to +29
const logger = winston.createLogger({
level: 'info',
format: combine(
timestamp(),
errors({ stack: true }), // Log stack traces for errors
json() // Output logs in JSON format
),
defaultMeta: { service: 'markdownmate' }, // Add a meta field for service name
transports: [
// - Write all logs with level `error` and below to `error.log`
new winston.transports.File({
filename: path.join(logsDir, 'error.log'),
level: 'error',
}),
// - Write all logs with level `info` and below to `combined.log`
new winston.transports.File({
filename: path.join(logsDir, 'combined.log'),
}),
],
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding log rotation configuration.

The current setup will create log files that grow indefinitely. For production environments, log rotation is essential to prevent disk space issues.

Consider adding the winston-daily-rotate-file transport:

+import DailyRotateFile from 'winston-daily-rotate-file';
+
 const logger = winston.createLogger({
   level: 'info',
   format: combine(
     timestamp(),
     errors({ stack: true }),
     json()
   ),
   defaultMeta: { service: 'markdownmate' },
   transports: [
-    new winston.transports.File({
-      filename: path.join(logsDir, 'error.log'),
-      level: 'error',
-    }),
-    new winston.transports.File({
-      filename: path.join(logsDir, 'combined.log'),
-    }),
+    new DailyRotateFile({
+      filename: path.join(logsDir, 'error-%DATE%.log'),
+      datePattern: 'YYYY-MM-DD',
+      level: 'error',
+      maxFiles: '14d',
+      maxSize: '20m'
+    }),
+    new DailyRotateFile({
+      filename: path.join(logsDir, 'combined-%DATE%.log'),
+      datePattern: 'YYYY-MM-DD',
+      maxFiles: '30d',
+      maxSize: '20m'
+    }),
   ],
 });

This would require adding winston-daily-rotate-file to dependencies.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const logger = winston.createLogger({
level: 'info',
format: combine(
timestamp(),
errors({ stack: true }), // Log stack traces for errors
json() // Output logs in JSON format
),
defaultMeta: { service: 'markdownmate' }, // Add a meta field for service name
transports: [
// - Write all logs with level `error` and below to `error.log`
new winston.transports.File({
filename: path.join(logsDir, 'error.log'),
level: 'error',
}),
// - Write all logs with level `info` and below to `combined.log`
new winston.transports.File({
filename: path.join(logsDir, 'combined.log'),
}),
],
});
+import DailyRotateFile from 'winston-daily-rotate-file';
+
const logger = winston.createLogger({
level: 'info',
format: combine(
timestamp(),
errors({ stack: true }),
json()
),
defaultMeta: { service: 'markdownmate' },
transports: [
- new winston.transports.File({
- filename: path.join(logsDir, 'error.log'),
- level: 'error',
- }),
- new winston.transports.File({
- filename: path.join(logsDir, 'combined.log'),
- }),
+ new DailyRotateFile({
+ filename: path.join(logsDir, 'error-%DATE%.log'),
+ datePattern: 'YYYY-MM-DD',
+ level: 'error',
+ maxFiles: '14d',
+ maxSize: '20m'
+ }),
+ new DailyRotateFile({
+ filename: path.join(logsDir, 'combined-%DATE%.log'),
+ datePattern: 'YYYY-MM-DD',
+ maxFiles: '30d',
+ maxSize: '20m'
+ }),
],
});
🤖 Prompt for AI Agents
In server/src/logger/index.ts around lines 10 to 29, the current Winston logger
configuration writes logs to files without rotation, which can cause log files
to grow indefinitely. To fix this, add the winston-daily-rotate-file transport
to enable log rotation by installing the winston-daily-rotate-file package and
configuring it to rotate logs daily or based on size limits. Replace or
supplement the existing file transports with daily rotate file transports,
specifying options like filename pattern, date pattern, max size, and max files
to manage disk usage effectively.

hexawulf added a commit that referenced this pull request Jun 24, 2025
Merge pull request #46 from hexawulf/feat/production-logging-winston
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant