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
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Code Error Microservice

A real-time error monitoring and reporting service that integrates with Telex channels, providing prioritized error classification and automated notifications.

## 🎯 Overview

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.


### Core Components

- **Error Controller**: Entry point for error processing
- **Categorization Service**: Analyzes and classifies errors
- **ZeroMQ Service**: Handles message queuing and distribution
- **Webhook Service**: Manages Telex channel communication


## 🎯 Features

- **Error Detection**
- Real-time monitoring
- Static code analysis (ESLint)
- Stack trace processing

- **Error Processing**
- Automatic categorization
- Priority classification
- Error enrichment

- **Notification System**
- Real-time Telex updates
- Configurable webhooks

## 🚀 Getting Started

### Prerequisites

- Node.js 20.x
- npm 9.x
- ZeroMQ library

### Quick Start

```bash
# Clone repository
git clone https://github.com/telexintegrations/code-error-microservice

# Install dependencies
npm install

# Setup environment
cp .env.example .env

# Start development server
npm run dev
```

## 🏷️ Error Classification

| Severity | Description | Example |
|----------|-------------|---------|
| 🚨 High | System critical | Service crash, DB connection failure |
| 🔔 Medium | Functional issues | API timeout, validation errors |
| ℹ️ Low | Minor problems | Deprecation warnings, style issues |

## 🛠️ Project Structure

```
src/
├── controllers/ # Request handlers
├── services/ # Business logic
├── middlewares/ # HTTP middlewares
├── routes/ # API routes
├── utils/ # Helper functions
└── app.ts # Application entry
```


## 📦 Core Dependencies

| Package | Version | Purpose |
|---------|---------|---------|
| express | ^4.21.2 | Web framework |
| zeromq | ^6.3.0 | Message queue |
| axios | ^1.8.3 | HTTP client |
| typescript | ^5.8.2 | Type support |
| pm2 | latest | Process management |
218 changes: 200 additions & 18 deletions src/controllers/errorController.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,140 @@
/**
* local version
* */

// import { Request, Response, NextFunction } from "express";
// import { categorizeError } from "../services/categorizationService";

// export interface ProcessedError {
// channelId: string;
// type: string;
// errors: ErrorItem[];
// timestamp: string;
// priority?: string;
// }

// export interface ErrorItem {
// message: string;
// stack: string;
// // A simplified, user-friendly description of the error.
// readableMessage?: string;
// }

// let lastProcessedError: ProcessedError | null = null;

// /**
// * Handles incoming error reports by:
// * - Validating the payload.
// * - Categorizing each error using the updated categorization service.
// * - Enriching errors with a user-friendly message that omits the verbose stack trace.
// * - Constructing a neat summary report with emojis and essential details.
// *
// * If the payload is invalid (missing channelId, type, or errors array),
// * responds with a 400 status and an explanatory message.
// */
// export const handleIncomingError = (
// req: Request,
// res: Response,
// next: NextFunction
// ): void => {
// try {
// const { channelId, type, errors, timestamp } = req.body;

// if (!channelId || !type || !Array.isArray(errors) || errors.length === 0) {
// res.status(400).json({
// error:
// "🚫 Invalid error report format. Ensure that 'channelId', 'type', and a non-empty 'errors' array are provided.",
// });
// return;
// }

// // Enrich each error with a more friendly message (removing detailed stack traces).
// const enrichedErrors: ErrorItem[] = errors.map((err: ErrorItem) => {
// const severity = categorizeError(err.message);
// let emoji: string;
// switch (severity) {
// case "High":
// emoji = "🚨";
// break;
// case "Medium":
// emoji = "🔔";
// break;
// default:
// emoji = "ℹ️";
// break;
// }
// return {
// ...err,

// readableMessage: `${emoji} ${severity} severity error: ${err.message}`,
// };
// });

// // Determine the highest severity among reported errors.
// const highestSeverity = enrichedErrors
// .map((err) => categorizeError(err.message))
// .reduce(
// (prev, current) =>
// current === "High"
// ? current
// : prev === "High"
// ? prev
// : current === "Medium"
// ? current
// : prev,
// "Low"
// );

// // Format timestamp to a more readable local date and time string.
// const formattedTimestamp = timestamp
// ? new Date(timestamp).toLocaleString()
// : new Date().toLocaleString();

// lastProcessedError = {
// channelId,
// type,
// errors: enrichedErrors,
// timestamp: formattedTimestamp,
// priority: highestSeverity,
// };

// // Build a simplified user-friendly error report message.
// let reportMessage = `✅ Error Report Accepted:
// Channel: ${channelId}
// Type: ${type}
// Time: ${formattedTimestamp}
// Overall Severity: ${highestSeverity}

// Detailed Errors:
// `;
// enrichedErrors.forEach((err, idx) => {
// reportMessage += `Error ${idx + 1}: ${err.readableMessage}\n`;
// });

// res.status(202).json({
// status: "accepted",
// message: reportMessage,
// });
// } catch (error) {
// next(error);
// }
// };

// /**
// * Returns the last processed error report.
// */
// export const getLastProcessedError = (): ProcessedError | null => {
// return lastProcessedError;
// };


/**
* live version
*/
import { Request, Response, NextFunction } from "express";
import { categorizeError } from "../services/categorizationService";

export interface ProcessedError {
channelId: string;
type: string;
errors: ErrorItem[];
timestamp: string;
Expand All @@ -12,47 +144,97 @@ export interface ProcessedError {
export interface ErrorItem {
message: string;
stack: string;
// A simplified, user-friendly description of the error.
readableMessage?: string;
}

let lastProcessedError: ProcessedError | null = null;

/**
* Handles incoming error reports by:
* - Validating the payload.
* - Categorizing each error using the updated categorization service.
* - Enriching errors with a user-friendly message that omits the verbose stack trace.
*
* If the payload is invalid (missing type or errors array),
* responds with a 400 status and an explanatory message.
*/
export const handleIncomingError = (
req: Request,
res: Response,
next: NextFunction
): void => {
try {
const { channelId, type, errors, timestamp } = req.body;
const { type, errors, timestamp } = req.body;

if (!channelId || !type || !Array.isArray(errors) || errors.length === 0) {
res.status(400).json({ error: "Invalid error report format." });
if (!type || !Array.isArray(errors) || errors.length === 0) {
res.status(400).json({
error:
"🚫 Invalid error report format. Ensure that 'type' and a non-empty 'errors' array are provided.",
});
return;
}

const highestSeverity = errors
.map(err => categorizeError(err.message))
.reduce((prev, current) =>
current === "High" ? current :
(prev === "High" ? prev :
(current === "Medium" ? current : prev)),
"Low"
);
// Enrich each error with a more friendly message (removing detailed stack traces).
const enrichedErrors: ErrorItem[] = errors.map((err: ErrorItem) => {
const severity = categorizeError(err.message);
let emoji: string;
switch (severity) {
case "High":
emoji = "🚨";
break;
case "Medium":
emoji = "🔔";
break;
default:
emoji = "ℹ️";
break;
}
return {
...err,
readableMessage: `${emoji} ${severity} severity error: ${err.message}`,
};
});

// Determine the highest severity among reported errors.
const highestSeverity = enrichedErrors
.map((err) => categorizeError(err.message))
.reduce(
(prev, current) =>
current === "High"
? current
: prev === "High"
? prev
: current === "Medium"
? current
: prev,
"Low"
);

// Format timestamp to a more readable local date and time string.
const formattedTimestamp = timestamp
? new Date(timestamp).toLocaleString()
: new Date().toLocaleString();

lastProcessedError = {
channelId,
type,
errors,
timestamp: timestamp || new Date().toISOString(),
priority: highestSeverity
errors: enrichedErrors,
timestamp: formattedTimestamp,
priority: highestSeverity,
};

res.status(202).json({ status: "accepted" });

res.status(202).json({
status: "accepted",
severity: highestSeverity
});
} catch (error) {
next(error);
}
};

/**
* Returns the last processed error report.
*/
export const getLastProcessedError = (): ProcessedError | null => {
return lastProcessedError;
};
23 changes: 19 additions & 4 deletions src/middlewares/requestLogger.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
import { Request, Response, NextFunction } from "express";

const requestLogger = (req: Request, _res: Response, next: NextFunction) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
console.log("Body:", JSON.stringify(req.body, null, 2));
/**
* Logs incoming HTTP requests with timestamp, method, URL, query parameters, and body.
* This middleware helps with debugging incoming requests.
*/
const requestLogger = (req: Request, _res: Response, next: NextFunction): void => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.url}`);

// Log query parameters if present
if (Object.keys(req.query).length > 0) {
console.log("Query:", JSON.stringify(req.query, null, 2));
}

// Log request body if present
if (req.body && Object.keys(req.body).length > 0) {
console.log("Body:", JSON.stringify(req.body, null, 2));
}

next();
};

export default requestLogger;
export default requestLogger;
2 changes: 1 addition & 1 deletion src/routes/tick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ router.post("/tick", async (req: Request, res: Response) => {
"event_name": "Code Error Monitor Agent",
"message": message,
"status": "success",
"username": "Agent Sapa"
"username": "Code Error Agent"
};

console.log(telexPayload.message);
Expand Down
Loading