Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ pnpm test

---

linked PR 5,4
linked PR 5,4,6

## 📄 License

Expand Down
10 changes: 3 additions & 7 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import helmet from 'helmet';
import compression from 'compression';
import rateLimit from 'express-rate-limit';
import { connectDatabase } from './config/database';
import logger from './config/logger';
import { corsOptionsDelegate, helmetOptions } from './config/security';
import errorHandler from './middleware/errorHandler';
import requestLogger from './middleware/requestLogger';
import routes from './routes';
import env from './config/env';

Expand Down Expand Up @@ -40,11 +39,8 @@ app.use(express.urlencoded({ extended: true, limit: '10mb' }));
// Compression
app.use(compression());

// Logging middleware
app.use((req, res, next) => {
logger.info(`${req.method} ${req.url}`);
next();
});
// Request logging middleware
app.use(requestLogger);

// Health check endpoint
app.get('/health', (req, res) => {
Expand Down
9 changes: 4 additions & 5 deletions src/config/env.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { z } from 'zod';
import dotenv from 'dotenv';
import logger from './logger';

dotenv.config();

Expand All @@ -23,12 +22,12 @@ try {
env = envSchema.parse(process.env);
} catch (error) {
if (error instanceof z.ZodError) {
logger.error('❌ Invalid environment variables:');
error.issues.forEach((err) => {
logger.error(` - ${err.path.join('.')}: ${err.message}`);
console.error('❌ Invalid environment variables:');
error.issues.forEach((issue) => {
console.error(` - ${issue.path.join('.')}: ${issue.message}`);
});
} else {
logger.error('❌ Failed to parse environment variables:', error);
console.error('❌ Failed to parse environment variables:', error);
}
process.exit(1);
}
Expand Down
25 changes: 14 additions & 11 deletions src/config/logger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import winston from 'winston';
import env from './env';

const levels = {
error: 0,
Expand All @@ -8,12 +9,6 @@ const levels = {
debug: 4,
};

const level = (): string => {
const env = process.env.NODE_ENV || 'development';
const isDevelopment = env === 'development';
return process.env.LOG_LEVEL || (isDevelopment ? 'debug' : 'warn');
};

const colors = {
error: 'red',
warn: 'yellow',
Expand All @@ -24,25 +19,33 @@ const colors = {

winston.addColors(colors);

const format = winston.format.combine(
const devFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss:ms' }),
winston.format.colorize({ all: true }),
winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`),
);

const prodFormat = winston.format.combine(winston.format.timestamp(), winston.format.json());

const transports = [
new winston.transports.Console(),
new winston.transports.Console({
format: env.NODE_ENV === 'development' ? devFormat : prodFormat,
}),
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
format: prodFormat,
}),
new winston.transports.File({
filename: 'logs/all.log',
format: prodFormat,
}),
new winston.transports.File({ filename: 'logs/all.log' }),
];

const logger = winston.createLogger({
level: level(),
level: env.LOG_LEVEL,
levels,
format,
format: env.NODE_ENV === 'development' ? devFormat : prodFormat,
transports,
});

Expand Down
29 changes: 29 additions & 0 deletions src/middleware/requestLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Request, Response, NextFunction } from 'express';
import logger from '../config/logger';

const requestLogger = (req: Request, res: Response, next: NextFunction): void => {
const start = Date.now();

res.on('finish', () => {
const duration = Date.now() - start;
const logMessage = `${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`;
const logData = {
method: req.method,
url: req.originalUrl,
statusCode: res.statusCode,
duration,
ip: req.ip,
userAgent: req.get('user-agent'),
};

if (res.statusCode >= 400) {
logger.warn(logMessage, logData);
} else {
logger.info(logMessage, logData);
}
});

next();
};

export default requestLogger;
Loading