Skip to content

Commit bc8a66f

Browse files
Merge pull request #2 from telexintegrations/feat/zeromq-error-handling
feat: enhance ZeroMQ error handling and documentation
2 parents bde8436 + c9627dc commit bc8a66f

File tree

6 files changed

+641
-67
lines changed

6 files changed

+641
-67
lines changed

README.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Code Error Microservice
2+
3+
A real-time error monitoring and reporting service that integrates with Telex channels, providing prioritized error classification and automated notifications.
4+
5+
## 🎯 Overview
6+
7+
This microservice monitors your codebase for errors, processes them through a message queue system, and delivers prioritized notifications to your Telex channels. It supports real-time monitoring and configurable error thresholds.
8+
9+
10+
### Core Components
11+
12+
- **Error Controller**: Entry point for error processing
13+
- **Categorization Service**: Analyzes and classifies errors
14+
- **ZeroMQ Service**: Handles message queuing and distribution
15+
- **Webhook Service**: Manages Telex channel communication
16+
17+
18+
## 🎯 Features
19+
20+
- **Error Detection**
21+
- Real-time monitoring
22+
- Static code analysis (ESLint)
23+
- Stack trace processing
24+
25+
- **Error Processing**
26+
- Automatic categorization
27+
- Priority classification
28+
- Error enrichment
29+
30+
- **Notification System**
31+
- Real-time Telex updates
32+
- Configurable webhooks
33+
34+
## 🚀 Getting Started
35+
36+
### Prerequisites
37+
38+
- Node.js 20.x
39+
- npm 9.x
40+
- ZeroMQ library
41+
42+
### Quick Start
43+
44+
```bash
45+
# Clone repository
46+
git clone https://github.com/telexintegrations/code-error-microservice
47+
48+
# Install dependencies
49+
npm install
50+
51+
# Setup environment
52+
cp .env.example .env
53+
54+
# Start development server
55+
npm run dev
56+
```
57+
58+
## 🏷️ Error Classification
59+
60+
| Severity | Description | Example |
61+
|----------|-------------|---------|
62+
| 🚨 High | System critical | Service crash, DB connection failure |
63+
| 🔔 Medium | Functional issues | API timeout, validation errors |
64+
| ℹ️ Low | Minor problems | Deprecation warnings, style issues |
65+
66+
## 🛠️ Project Structure
67+
68+
```
69+
src/
70+
├── controllers/ # Request handlers
71+
├── services/ # Business logic
72+
├── middlewares/ # HTTP middlewares
73+
├── routes/ # API routes
74+
├── utils/ # Helper functions
75+
└── app.ts # Application entry
76+
```
77+
78+
79+
## 📦 Core Dependencies
80+
81+
| Package | Version | Purpose |
82+
|---------|---------|---------|
83+
| express | ^4.21.2 | Web framework |
84+
| zeromq | ^6.3.0 | Message queue |
85+
| axios | ^1.8.3 | HTTP client |
86+
| typescript | ^5.8.2 | Type support |
87+
| pm2 | latest | Process management |

src/controllers/errorController.ts

Lines changed: 200 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,140 @@
1+
/**
2+
* local version
3+
* */
4+
5+
// import { Request, Response, NextFunction } from "express";
6+
// import { categorizeError } from "../services/categorizationService";
7+
8+
// export interface ProcessedError {
9+
// channelId: string;
10+
// type: string;
11+
// errors: ErrorItem[];
12+
// timestamp: string;
13+
// priority?: string;
14+
// }
15+
16+
// export interface ErrorItem {
17+
// message: string;
18+
// stack: string;
19+
// // A simplified, user-friendly description of the error.
20+
// readableMessage?: string;
21+
// }
22+
23+
// let lastProcessedError: ProcessedError | null = null;
24+
25+
// /**
26+
// * Handles incoming error reports by:
27+
// * - Validating the payload.
28+
// * - Categorizing each error using the updated categorization service.
29+
// * - Enriching errors with a user-friendly message that omits the verbose stack trace.
30+
// * - Constructing a neat summary report with emojis and essential details.
31+
// *
32+
// * If the payload is invalid (missing channelId, type, or errors array),
33+
// * responds with a 400 status and an explanatory message.
34+
// */
35+
// export const handleIncomingError = (
36+
// req: Request,
37+
// res: Response,
38+
// next: NextFunction
39+
// ): void => {
40+
// try {
41+
// const { channelId, type, errors, timestamp } = req.body;
42+
43+
// if (!channelId || !type || !Array.isArray(errors) || errors.length === 0) {
44+
// res.status(400).json({
45+
// error:
46+
// "🚫 Invalid error report format. Ensure that 'channelId', 'type', and a non-empty 'errors' array are provided.",
47+
// });
48+
// return;
49+
// }
50+
51+
// // Enrich each error with a more friendly message (removing detailed stack traces).
52+
// const enrichedErrors: ErrorItem[] = errors.map((err: ErrorItem) => {
53+
// const severity = categorizeError(err.message);
54+
// let emoji: string;
55+
// switch (severity) {
56+
// case "High":
57+
// emoji = "🚨";
58+
// break;
59+
// case "Medium":
60+
// emoji = "🔔";
61+
// break;
62+
// default:
63+
// emoji = "ℹ️";
64+
// break;
65+
// }
66+
// return {
67+
// ...err,
68+
69+
// readableMessage: `${emoji} ${severity} severity error: ${err.message}`,
70+
// };
71+
// });
72+
73+
// // Determine the highest severity among reported errors.
74+
// const highestSeverity = enrichedErrors
75+
// .map((err) => categorizeError(err.message))
76+
// .reduce(
77+
// (prev, current) =>
78+
// current === "High"
79+
// ? current
80+
// : prev === "High"
81+
// ? prev
82+
// : current === "Medium"
83+
// ? current
84+
// : prev,
85+
// "Low"
86+
// );
87+
88+
// // Format timestamp to a more readable local date and time string.
89+
// const formattedTimestamp = timestamp
90+
// ? new Date(timestamp).toLocaleString()
91+
// : new Date().toLocaleString();
92+
93+
// lastProcessedError = {
94+
// channelId,
95+
// type,
96+
// errors: enrichedErrors,
97+
// timestamp: formattedTimestamp,
98+
// priority: highestSeverity,
99+
// };
100+
101+
// // Build a simplified user-friendly error report message.
102+
// let reportMessage = `✅ Error Report Accepted:
103+
// Channel: ${channelId}
104+
// Type: ${type}
105+
// Time: ${formattedTimestamp}
106+
// Overall Severity: ${highestSeverity}
107+
108+
// Detailed Errors:
109+
// `;
110+
// enrichedErrors.forEach((err, idx) => {
111+
// reportMessage += `Error ${idx + 1}: ${err.readableMessage}\n`;
112+
// });
113+
114+
// res.status(202).json({
115+
// status: "accepted",
116+
// message: reportMessage,
117+
// });
118+
// } catch (error) {
119+
// next(error);
120+
// }
121+
// };
122+
123+
// /**
124+
// * Returns the last processed error report.
125+
// */
126+
// export const getLastProcessedError = (): ProcessedError | null => {
127+
// return lastProcessedError;
128+
// };
129+
130+
131+
/**
132+
* live version
133+
*/
1134
import { Request, Response, NextFunction } from "express";
2135
import { categorizeError } from "../services/categorizationService";
3136

4137
export interface ProcessedError {
5-
channelId: string;
6138
type: string;
7139
errors: ErrorItem[];
8140
timestamp: string;
@@ -12,47 +144,97 @@ export interface ProcessedError {
12144
export interface ErrorItem {
13145
message: string;
14146
stack: string;
147+
// A simplified, user-friendly description of the error.
148+
readableMessage?: string;
15149
}
16150

17151
let lastProcessedError: ProcessedError | null = null;
18152

153+
/**
154+
* Handles incoming error reports by:
155+
* - Validating the payload.
156+
* - Categorizing each error using the updated categorization service.
157+
* - Enriching errors with a user-friendly message that omits the verbose stack trace.
158+
*
159+
* If the payload is invalid (missing type or errors array),
160+
* responds with a 400 status and an explanatory message.
161+
*/
19162
export const handleIncomingError = (
20163
req: Request,
21164
res: Response,
22165
next: NextFunction
23166
): void => {
24167
try {
25-
const { channelId, type, errors, timestamp } = req.body;
168+
const { type, errors, timestamp } = req.body;
26169

27-
if (!channelId || !type || !Array.isArray(errors) || errors.length === 0) {
28-
res.status(400).json({ error: "Invalid error report format." });
170+
if (!type || !Array.isArray(errors) || errors.length === 0) {
171+
res.status(400).json({
172+
error:
173+
"🚫 Invalid error report format. Ensure that 'type' and a non-empty 'errors' array are provided.",
174+
});
29175
return;
30176
}
31177

32-
const highestSeverity = errors
33-
.map(err => categorizeError(err.message))
34-
.reduce((prev, current) =>
35-
current === "High" ? current :
36-
(prev === "High" ? prev :
37-
(current === "Medium" ? current : prev)),
38-
"Low"
39-
);
178+
// Enrich each error with a more friendly message (removing detailed stack traces).
179+
const enrichedErrors: ErrorItem[] = errors.map((err: ErrorItem) => {
180+
const severity = categorizeError(err.message);
181+
let emoji: string;
182+
switch (severity) {
183+
case "High":
184+
emoji = "🚨";
185+
break;
186+
case "Medium":
187+
emoji = "🔔";
188+
break;
189+
default:
190+
emoji = "ℹ️";
191+
break;
192+
}
193+
return {
194+
...err,
195+
readableMessage: `${emoji} ${severity} severity error: ${err.message}`,
196+
};
197+
});
40198

199+
// Determine the highest severity among reported errors.
200+
const highestSeverity = enrichedErrors
201+
.map((err) => categorizeError(err.message))
202+
.reduce(
203+
(prev, current) =>
204+
current === "High"
205+
? current
206+
: prev === "High"
207+
? prev
208+
: current === "Medium"
209+
? current
210+
: prev,
211+
"Low"
212+
);
213+
214+
// Format timestamp to a more readable local date and time string.
215+
const formattedTimestamp = timestamp
216+
? new Date(timestamp).toLocaleString()
217+
: new Date().toLocaleString();
218+
41219
lastProcessedError = {
42-
channelId,
43220
type,
44-
errors,
45-
timestamp: timestamp || new Date().toISOString(),
46-
priority: highestSeverity
221+
errors: enrichedErrors,
222+
timestamp: formattedTimestamp,
223+
priority: highestSeverity,
47224
};
48225

49-
res.status(202).json({ status: "accepted" });
50-
226+
res.status(202).json({
227+
status: "accepted",
228+
severity: highestSeverity
229+
});
51230
} catch (error) {
52231
next(error);
53232
}
54233
};
55234

235+
/**
236+
* Returns the last processed error report.
237+
*/
56238
export const getLastProcessedError = (): ProcessedError | null => {
57239
return lastProcessedError;
58240
};

src/middlewares/requestLogger.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
import { Request, Response, NextFunction } from "express";
22

3-
const requestLogger = (req: Request, _res: Response, next: NextFunction) => {
4-
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
5-
console.log("Body:", JSON.stringify(req.body, null, 2));
3+
/**
4+
* Logs incoming HTTP requests with timestamp, method, URL, query parameters, and body.
5+
* This middleware helps with debugging incoming requests.
6+
*/
7+
const requestLogger = (req: Request, _res: Response, next: NextFunction): void => {
8+
const timestamp = new Date().toISOString();
9+
console.log(`[${timestamp}] ${req.method} ${req.url}`);
10+
11+
// Log query parameters if present
12+
if (Object.keys(req.query).length > 0) {
13+
console.log("Query:", JSON.stringify(req.query, null, 2));
14+
}
15+
16+
// Log request body if present
17+
if (req.body && Object.keys(req.body).length > 0) {
18+
console.log("Body:", JSON.stringify(req.body, null, 2));
19+
}
20+
621
next();
722
};
823

9-
export default requestLogger;
24+
export default requestLogger;

src/routes/tick.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ router.post("/tick", async (req: Request, res: Response) => {
4242
"event_name": "Code Error Monitor Agent",
4343
"message": message,
4444
"status": "success",
45-
"username": "Agent Sapa"
45+
"username": "Code Error Agent"
4646
};
4747

4848
console.log(telexPayload.message);

0 commit comments

Comments
 (0)