Imagine running a bustling coffee shop. Every day, hundreds of customers come in, order their favorite brews, and go about their day. Now, imagine if you had no way of keeping track of who ordered what, when supplies were running low, or if the espresso machine decided to go on strike. Chaos, right?
This, my friends, is what a server without logs looks like. Pure, unadulterated chaos. Logs are the unsung heroes of the tech world, and today, we’re going to explore them with a touch of humor. Because who said server management can’t be funny?
What Are Logs?
Logs are like the diary your server secretly keeps. They record every little event, error, and action that takes place. If servers had personalities, their logs would be their inner monologues.
Think of logs as your server’s tweets:
- Status Update: “Server started at 9:00 AM. Feeling fresh and ready to handle requests!”
- Complaint: “Error 404! Someone tried to reach a page that doesn’t exist. Humans, am I right?”
- Achievement: “Successfully processed 10,000 requests today! #Winning”
Types of Logs: The Server’s Diary Entries
- Access Logs: These are like the guestbook at a fancy hotel. Every time someone (or something) accesses the server, it makes an entry.
- Guestbook Entry: “IP 192.168.1.1 accessed /home at 10:00 AM.”
- Guestbook Entry: “IP 192.168.1.1 accessed /home at 10:00 AM.”
- Error Logs: The diary’s venting section. This is where the server complains about everything that’s going wrong.
- Diary Vent: “Error 500: Internal server error. I think I need a coffee break.”
- Diary Vent: “Error 500: Internal server error. I think I need a coffee break.”
- Event Logs: The server’s timeline. This records significant events like system restarts, updates, or anything noteworthy.
- Timeline Post: “System update at 2:00 PM. New features installed. Feeling fabulous!”
- Timeline Post: “System update at 2:00 PM. New features installed. Feeling fabulous!”
The Importance of Logs: Why Your Server Needs a Diary
Imagine Sherlock Holmes solving a mystery without his trusty notebook. Logs are like that notebook. They help us:
- Solve Mysteries: When something goes wrong, logs are our clues to figure out what happened.
- Clue: “Hmm, looks like the error occurred right after a large spike in traffic. Elementary, my dear Watson!”
- Clue: “Hmm, looks like the error occurred right after a large spike in traffic. Elementary, my dear Watson!”
- Monitor Health: Just like you keep an eye on your fitness tracker, logs help monitor server performance.
- Fitness Tracker: “CPU usage spikes to 90% at 3:00 PM. Time to investigate!”
- Fitness Tracker: “CPU usage spikes to 90% at 3:00 PM. Time to investigate!”
- Security: Logs are your server’s security camera footage. They help spot suspicious activity.
- Security Footage: “Multiple failed login attempts from the same IP? Someone’s trying to break in!”
Building a Logging System for Your Server with a Decorator
In this blog post, we’ll delve into how to build a robust logging system for your server using a decorator pattern. We’ll cover the key concepts, including the entity, the service, and how to use the decorator in your controller. Let’s get started!
The Logs Entity
First, we need to define the Logs entity. This entity will represent our log records in the database. Each log entry will store various details such as the summary, source path, user information, and any errors encountered.
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from "typeorm";
export enum notificationType {
SERVER_LOGS = "SERVER_LOGS",
AUDIT_LOG = "AUDIT_LOG",
NOTIFICATION = "NOTIFICATION",
}
@Entity("logs")
export class Logs {
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ nullable: true, type: String })
summary: string;
@Column({ nullable: true, type: String })
sourcePath: string;
@Column({ type: String, nullable: true })
ipAddress: string;
@Column({ type: String, nullable: true })
body: string;
@Column({ type: String, nullable: true })
profileImage: string;
@Column({ type: String, nullable: true })
deviceType: string;
@Column({ type: String, nullable: true })
userTag: string;
@Column({ type: String, nullable: true })
email: string;
@Column({ type: String, nullable: true })
fullName: string;
@Column({ type: String, nullable: true })
userId: string;
@Column({ type: String, nullable: true })
error: string;
@Column({ type: "enum", enum: notificationType, default: notificationType.AUDIT_LOG })
notification_type: notificationType;
@CreateDateColumn()
createdAt: Date;
}
The Logging Service
Next, we create a logging service that handles the creation of log entries. This service will be responsible for saving logs to the database and handling any exceptions that occur during this process.
export interface IServerLogsDto {
summary: string;
email?: string;
fullName?: string;
userId?: string;
error?: string;
sourcePath: string;
body?: string;
notification_type: notificationType;
}
class LogService {
async serverLogs(logsDto: IServerLogsDto) {
try {
await this.save(logsDto);
} catch (error) {
this.catchException({
context: `${LogService.name} - serverLogs`,
statusCode: error.status,
message: `serverLogs() - ${error.message}`,
});
}
}
private async save(logsDto: IServerLogsDto) {
// Code to save logsDto to the database
}
private catchException({ context, statusCode, message }: { context: string; statusCode: number; message: string }) {
console.error(`Exception in ${context}: Status Code ${statusCode}, Message: ${message}`);
}
}
Serializing Errors
To ensure that errors are properly logged, we need a function to serialize error objects. This function converts an error object into a JSON string, handling circular references.
function serializeError(error: any): any {
const seen = new WeakSet();
return JSON.stringify(error, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
});
}
The Decorator
Now, we create a decorator function processServerLog
. This decorator will be applied to controller methods to automatically log the details of the request and response.
function processServerLog(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const req: CustomRequest = args[0];
const res: CustomResponse = args[1];
try {
const result = await originalMethod.apply(this, args);
const createSuccessLog: IServerLogsDto = {
email: req?.email,
fullName: req?.fullName,
userId: req?.userId,
sourcePath: req?.originalUrl,
summary: `${req?.fullName} - ${propertyKey}() ==>> ${res?.customErrorMessage ? res?.customErrorMessage : "Successful"}`,
body: JSON.stringify(req?.body),
...(res?.errorObject && { error: serializeError(res.errorObject) }),
notification_type: notificationType.SERVER_LOGS,
};
await new LogService().serverLogs(createSuccessLog);
return result;
} catch (error) {
const context = `${target.constructor.name}.${propertyKey}`;
const statusCode = error.status || 500;
const message = `${propertyKey}() - ${error.message}`;
console.error(`Exception in ${context}: Status Code ${statusCode}, Message: ${message}`);
}
};
return descriptor;
}
Using the Decorator in a Controller
Finally, let’s see how to use this decorator in a controller method.
class MyController {
@processServerLog
async myEndpoint(req: CustomRequest, res: CustomResponse) {
// Your endpoint logic here
const data = await someService.doSomething(req.body);
res.send(data);
}
}
Key Concepts Explained
- Entity: The Logs entity represents a log record in the database. It includes various fields to store details about the log entry, such as the summary, source path, user information, and any errors.
- Service: The
LogService
handles the creation and saving of log entries. It includes methods to save logs to the database and handle exceptions. - Decorator: The
processServerLog
decorator is applied to controller methods to automatically log the details of the request and response. It wraps the original method and logs success or error details accordingly. - Serialize Error: The serializeError function converts an error object into a JSON string, handling circular references.
Why It’s Important to Clear Logs Periodically
Logs can accumulate quickly, especially on busy servers. If not managed properly, they can consume significant storage space and impact server performance. It’s essential to clear logs periodically to:
- Free Up Storage: Prevent storage from being consumed by old logs.
- Maintain Performance: Ensure the server runs smoothly without being bogged down by excessive logs.
- Improve Manageability: Keep logs manageable and easier to review.
You can use log rotation tools like logrotate or set up automated scripts to archive and delete old logs like Cron jobs.
My Final Note – Logs is Your Server’s Best Friend
Logs are more than just boring lines of text. They’re your server’s best friend, diary, and security system all rolled into one. By keeping track of what’s happening, logs help ensure your server runs smoothly, securely, and efficiently.
So, next time you dive into your server logs, remember: you’re not just reading data, you’re getting a glimpse into the daily life of your hardworking server. And with a little humor, managing logs can be a lot more fun!