Skip to content

Commit d3158b1

Browse files
Merge pull request
Feat: added logs
2 parents d69e54b + 6ebfe03 commit d3158b1

File tree

9 files changed

+290
-24
lines changed

9 files changed

+290
-24
lines changed

backend/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
"lodash": "^4.17.21",
2424
"ondc-automation-cache-lib": "^1.0.4",
2525
"redis": "^4.7.0",
26-
"uuid": "^11.0.3"
26+
"uuid": "^11.0.3",
27+
"winston": "^3.17.0",
28+
"winston-loki": "^6.1.3",
29+
"chalk": "^5.4.1"
2730
},
2831
"devDependencies": {
32+
"@types/chalk": "^0.4.31",
2933
"@types/connect-redis": "^0.0.23",
3034
"@types/cookie-parser": "^1.4.7",
3135
"@types/cors": "^2.8.17",
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import winston from "winston";
2+
import chalk, { ChalkInstance} from "chalk";
3+
import LokiTransport from "winston-loki";
4+
import { LogParams } from "../interfaces/logger";
5+
6+
7+
const { combine, timestamp, printf, errors } = winston.format;
8+
9+
// Define colors for log levels and messages
10+
const levelColors: Record<string, ChalkInstance> = {
11+
error: chalk.bold.red, // Bright red for errors
12+
warn: chalk.hex("#FFA500"), // Orange for warnings
13+
info: chalk.blue, // Blue for information
14+
debug: chalk.green, // Green for debugging
15+
default: chalk.white, // Default color for others
16+
};
17+
18+
const messageColors: Record<string, ChalkInstance> = {
19+
error: chalk.redBright, // Highlight error messages
20+
warn: chalk.yellowBright, // Bright yellow for warnings
21+
info: chalk.cyan, // Cyan for information messages
22+
debug: chalk.magentaBright, // Bright magenta for debugging
23+
default: chalk.gray, // Default gray for fallback
24+
};
25+
26+
// Custom log format
27+
const logFormat = printf(({ level, message, timestamp, stack, transaction_id , ...meta}) => {
28+
const levelColor = levelColors[level] || levelColors.default; // Colorize level
29+
const messageColor = messageColors[level] || messageColors.default; // Colorize message
30+
31+
const coloredLevel = levelColor(`[${level.toUpperCase()}]`); // Apply color to log level
32+
const coloredTimestamp = chalk.dim(timestamp); // Dim timestamp
33+
const coloredMessage = messageColor(message); // Apply message-specific color
34+
const coloredStack = stack ? chalk.dim(stack) : ""; // Dim stack trace if present
35+
const coloredtransaction_id = transaction_id ? chalk.yellow(`[${transaction_id}] `) : ""; // Yellow for transaction ID
36+
const coloredMeta = meta && Object.keys(meta).length > 0 ? chalk.gray(JSON.stringify(meta)) : "";
37+
return `${coloredTimestamp} ${coloredtransaction_id}${coloredLevel}: ${coloredMessage} ${coloredStack} ${coloredMeta}`;
38+
});
39+
40+
// Determine log level based on environment
41+
const logLevel = process.env.NODE_ENV === "production" ? "info" : "debug";
42+
43+
// Configure Winston logger
44+
const logger = winston.createLogger({
45+
level: logLevel,
46+
format: combine(
47+
timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
48+
errors({ stack: true }), // Include stack trace in error messages
49+
logFormat
50+
),
51+
transports: [
52+
// Console transport with colorized output
53+
new winston.transports.Console(),
54+
55+
// Loki transport for sending logs to Grafana Loki
56+
// new LokiTransport({
57+
// host: process.env.LOKI_HOST || "http://localhost:3100", // Loki endpoint
58+
// labels: {
59+
// app: process.env.APP_NAME || "automation", // Custom label for filtering in Loki
60+
// env: process.env.NODE_ENV || "development",
61+
// },
62+
// json: true, // Send logs in JSON format
63+
// onConnectionError: (err) => console.error("Loki connection error:", err), // Handle connection errors
64+
// }),
65+
],
66+
});
67+
68+
69+
70+
// Logging functions
71+
const logInfo = ({ message, transaction_id, meta }: LogParams): void => {
72+
logger.info(message, { transaction_id, ...meta });
73+
};
74+
75+
const logDebug = ({ message, transaction_id, meta }: LogParams): void => {
76+
logger.debug(message, { transaction_id, ...meta });
77+
};
78+
79+
const logError = ({ message, transaction_id, error, meta }: LogParams): void => {
80+
if (error instanceof Error) {
81+
logger.error(message, { transaction_id, stack: error.stack, ...meta });
82+
} else {
83+
logger.error(message, { transaction_id, ...meta });
84+
}
85+
};
86+
87+
88+
export { logger, logInfo, logDebug, logError };
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import jwt from "jsonwebtoken";
22
import { Request, Response } from "express";
3+
import {logError, logInfo} from "../config/winstonConfig";
34

45
// Secret key for JWT
56
const JWT_SECRET = "your_secret_key"; // Replace with a secure key in production
@@ -12,23 +13,24 @@ const mockUser = {
1213

1314
// Login controller
1415
export const login = async (req: Request, res: Response) => {
16+
logInfo({message: `Entering Login Funcion`});
1517
const { username, password } = req.body;
16-
1718
// Validate input
1819
if (!username || !password) {
20+
logInfo({message: `Exiting Login Funcion`});
1921
res.status(400).json({ message: "Username and password are required." });
2022
return;
2123
}
2224

2325
try {
24-
console.log(username, mockUser.username);
2526
if (username !== mockUser.username) {
27+
logInfo({message: `Exiting Login Funcion`});
2628
res.status(401).json({ message: "Invalid username or password." });
2729
return;
2830
}
2931

30-
console.log(password, mockUser.password);
3132
if (password !== mockUser.password) {
33+
logInfo({message: `Exiting Login Funcion`});
3234
res.status(401).json({ message: "Invalid username or password." });
3335
return;
3436
}
@@ -37,11 +39,11 @@ export const login = async (req: Request, res: Response) => {
3739
const token = jwt.sign({ username: mockUser.username }, JWT_SECRET, {
3840
expiresIn: "1h", // Token expiration time
3941
});
40-
42+
logInfo({message: `Exiting Login Funcion`});
4143
// Send token in response
4244
res.status(200).json({ message: "Login successful", token });
43-
} catch (error) {
44-
console.error(error);
45+
} catch (error: any) {
46+
logError({message: "Error in Login Function",error });
4547
res.status(500).json({ message: "An error occurred during login." });
4648
}
4749
};

backend/src/controllers/sessionController.ts

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,67 @@ import {
77
getAllSessionService,
88
createSessionService,
99
} from "../services/sessionService";
10+
import { logError, logInfo } from "../config/winstonConfig";
1011

1112
const SESSION_EXPIRY = 3600; // 1 hour
1213
const COOKIE_OPTIONS = { maxAge: SESSION_EXPIRY, httpOnly: true };
1314

1415
// Helper function to set session cookie
1516
const setSessionCookie = (res: Response, sessionId: string) => {
17+
logInfo({
18+
message: 'Entering setSessionCookie Controller Function',
19+
meta: { sessionId }
20+
})
1621
res.cookie("sessionId", sessionId, COOKIE_OPTIONS);
22+
logInfo({
23+
message: 'Exiting setSessionCookie Controller Function',
24+
meta: { sessionId }
25+
})
1726
};
1827

1928
export const getSession = async (req: Request, res: Response) => {
29+
logInfo({
30+
message: 'Entering getSession Controller Function'
31+
})
2032
const subscriber_url = req.query.subscriber_url as string;
2133

2234
if (!subscriber_url) {
35+
logInfo({
36+
message: 'Exiting getSession Controller Function'
37+
})
2338
res.status(400).send({ message: "Session Key is required." });
2439
return;
2540
}
2641

2742
try {
2843
const sessionData = await getSessionService(subscriber_url);
44+
logInfo({
45+
message: 'Exiting getSession Controller Function',
46+
meta: { sessionId: subscriber_url }
47+
})
2948
res.status(200).send(sessionData);
3049
} catch (error: any) {
31-
console.error(error);
50+
logError({
51+
message: 'Error in getSession Controller Function',
52+
meta: { sessionId: subscriber_url },
53+
error
54+
})
3255
res
3356
.status(500)
3457
.send({ message: "Error fetching session", error: error.message });
3558
}
3659
};
3760

3861
export const updateSession = async (req: Request, res: Response) => {
62+
logInfo({
63+
message: 'Entering updateSession Controller Function',
64+
})
3965
const subscriber_url = req.query.subscriber_url as string;
4066

4167
if (!subscriber_url) {
68+
logInfo({
69+
message: 'Exiting updateSession Controller Function'
70+
})
4271
res.status(400).send({ message: "subscriber url is required." });
4372
return;
4473
}
@@ -47,16 +76,24 @@ export const updateSession = async (req: Request, res: Response) => {
4776
try {
4877
const response = await updateSessionService(subscriber_url, sessionData);
4978
setSessionCookie(res, subscriber_url);
79+
logInfo({
80+
message: 'Exiting updateSession Controller Function',
81+
meta: { sessionId: subscriber_url }
82+
})
5083
res.status(200).send({ message: response });
5184
} catch (error: any) {
52-
console.error(error);
85+
logError({
86+
message: 'Error in updateSession Controller Function',error,meta: { sessionId: subscriber_url }});
5387
res
5488
.status(500)
5589
.send({ message: "Error updating session", error: error.message });
5690
}
5791
};
5892

5993
export const deleteSession = async (req: Request, res: Response) => {
94+
logInfo({
95+
message: 'Entering deleteSession Controller Function'
96+
})
6097
const subscriber_url = req.query.subscriber_url as string;
6198

6299
if (!subscriber_url) {
@@ -67,34 +104,59 @@ export const deleteSession = async (req: Request, res: Response) => {
67104
try {
68105
const response = await deleteService(subscriber_url);
69106
setSessionCookie(res, subscriber_url);
107+
logInfo({
108+
message: 'Exiting deleteSession Controller Function',
109+
meta: { sessionId: subscriber_url }
110+
})
70111
res.status(200).send({ message: response });
71112
} catch (error: any) {
72-
console.error(error);
113+
logError({
114+
message: 'Error in deleteSession Controller Function',
115+
error,
116+
meta: { sessionId: subscriber_url }
117+
})
73118
res
74119
.status(500)
75120
.send({ message: "Error deleting session", error: error.message });
76121
}
77122
};
78123

79124
export const updateCacheDb = async (req: Request, res: Response) => {
125+
logInfo({
126+
message: 'Entering updateCacheDb Controller Function'
127+
})
80128
const db_id = parseInt(req.query.db_id as string);
81-
console.log("Switching db", db_id);
82129

83130
if (!db_id && db_id !== 0) {
131+
logInfo({
132+
message: 'Exiting updateCacheDb Controller Function'
133+
})
84134
res.status(400).send({ message: "db_id is required." });
85135
return;
86136
}
87-
88137
switchCacheDb(db_id);
138+
logInfo({
139+
message: 'Exiting updateCacheDb Controller Function',
140+
meta: { db_id }
141+
})
89142
res.send({ message: "Cache DB swithced" });
90143
};
91144

92145
export const getAllSession = async (req: Request, res: Response) => {
146+
logInfo({
147+
message: 'Entering getAllSession Controller Function'
148+
})
93149
try {
94150
const sessionData = await getAllSessionService();
151+
logInfo({
152+
message: 'Exiting getAllSession Controller Function'
153+
})
95154
res.status(200).send(sessionData);
96155
} catch (error: any) {
97-
console.error(error);
156+
logError({
157+
message: 'Error in getAllSession Controller Function',
158+
error
159+
});
98160
res
99161
.status(500)
100162
.send({ message: "Error fetching session", error: error.message });
@@ -103,14 +165,22 @@ export const getAllSession = async (req: Request, res: Response) => {
103165

104166
export const createSession = async (req: Request, res: Response) => {
105167
const { sessionID, payload } = req.body;
106-
168+
logInfo({
169+
message: 'Entering createSession Controller Function'
170+
})
107171
try {
108172
const response = await createSessionService(sessionID, payload);
173+
logInfo({
174+
message: 'Exiting createSession Controller Function'
175+
})
109176
res.status(200).send({ message: response });
110177
} catch (error: any) {
111-
console.error(error);
178+
logError({
179+
message: 'Error in getAllSession Controller Function',
180+
error
181+
});
112182
res
113183
.status(500)
114184
.send({ message: "Error creating session", error: error.message });
115185
}
116-
};
186+
};

backend/src/interfaces/logger.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type LogParams = {
2+
message: string;
3+
transaction_id?: string;
4+
meta?: Record<string, any>;
5+
error?: any;
6+
};

backend/src/middleware/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Request, Response, NextFunction } from "express";
22
import jwt from "jsonwebtoken";
3+
import { logInfo, logError, logDebug } from "../config/winstonConfig";
34

45
// Secret key for JWT
56
const JWT_SECRET = process.env.JWT_SECRET || "your_secret_key"; // Use environment variable in production
@@ -11,8 +12,10 @@ const validateToken = (
1112
next: NextFunction
1213
): void => {
1314
// Get the token from the Authorization header
15+
logInfo({ message: `Entering Token Validation Middleware` });
1416
const authHeader = req.headers.authorization;
1517
if (!authHeader || !authHeader.startsWith("Bearer ")) {
18+
logInfo({ message: `Exiting Token Validation Middleware` });
1619
res
1720
.status(401)
1821
.json({ message: "Unauthorized: Token is missing or invalid." });
@@ -26,10 +29,10 @@ const validateToken = (
2629
const decoded = jwt.verify(token, JWT_SECRET);
2730
// Attach the decoded payload to the request object for further use
2831
// req.user = decoded;
29-
32+
logInfo({ message: `Exiting Token Validation Middleware`});
3033
next(); // Proceed to the next middleware or route handler
3134
} catch (error) {
32-
console.error("Token validation error:", error);
35+
logError({ message: "Token validation error", error });
3336
res.status(403).json({ message: "Forbidden: Invalid or expired token." });
3437
}
3538
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Request, Response, NextFunction } from "express";
2+
import { logDebug } from "../config/winstonConfig";
3+
4+
export default (req: Request, _res: Response, next: NextFunction) => {
5+
const transaction_id = req.body?.transaction_id;
6+
logDebug({message: `Request Log`, transaction_id, meta: {
7+
method: req.method,
8+
url: req.url,
9+
body: req.body
10+
}});
11+
next();
12+
};
13+

0 commit comments

Comments
 (0)