Log collection is essential for monitoring, debugging, and analyzing application behavior in modern distributed systems. Traditional logging methods that rely on HTTP-based log shipping can introduce overhead, tight coupling, and reliability issues.
A more efficient approach is to use AMQP (e.g., RabbitMQ) to collect logs asynchronously, ensuring scalability, fault tolerance, and real-time processing.
This article demonstrates how to implement a centralized logging system using AMQP in Spring Boot (Java) and NestJS (TypeScript) while also enabling log retrieval via a query service.
🚀 Why Use AMQP for Log Collection?
Traditional HTTP-based logging suffers from:
❌ High overhead: Frequent HTTP requests increase system load.
❌ Potential data loss: If the log collector service is down, logs may be lost.
❌ Tight coupling: Backend services need direct integration with the log storage system.
✅ Advantages of AMQP (RabbitMQ) for Logging:
✔ Asynchronous & Decoupled → Logs are sent to a queue instead of an HTTP endpoint.
✔ Reliable → If the log processor is down, messages remain in the queue until processed.
✔ Scalable → Multiple log processors can consume messages in parallel for high throughput.
🏗 Architecture Overview
Below is the high-level architecture of the logging system:
🔍 How It Works
1️⃣ Backend Services (Spring Boot, NestJS, and other microservices) send log messages to RabbitMQ asynchronously.
2️⃣ RabbitMQ (AMQP Message Queue) temporarily holds the logs, ensuring reliable message delivery.
3️⃣ A Log Processor Service consumes log messages and stores them in PostgreSQL or MongoDB.
4️⃣ A Log Query Service (Log API) allows users or external dashboards to retrieve logs for analysis.
5️⃣ Users can access logs via a dashboard, API, or external monitoring tools like Kibana or Grafana.
🏗 Implementing AMQP-based Logging in Spring Boot
1️⃣ Add RabbitMQ Dependencies:
Add the following RabbitMQ dependency to your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2️⃣ Configure RabbitMQ:
Add the following RabbitMQ configuration in application.yml:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
virtual-host: /
3️⃣ Implement a Log Producer (Send Logs to RabbitMQ)
Create a service to send logs to RabbitMQ:
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
@Service
public class LogProducer {
private final RabbitTemplate rabbitTemplate;
private static final String EXCHANGE_NAME = "log-exchange";
private static final String ROUTING_KEY = "log.info";
public LogProducer(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
public void sendLog(String logMessage) {
rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING_KEY, logMessage);
}
}
Now, you can send logs like this:
logProducer.sendLog("User logged in: user@example.com");
4️⃣ Implement a Log Consumer (Process Logs from RabbitMQ)
Create a listener to process log messages:
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
public class LogConsumer {
@RabbitListener(queues = "log-queue")
public void receiveLog(String logMessage) {
System.out.println("Received log: " + logMessage);
// Store in a database
}
}
🏗 Implementing AMQP-based Logging in NestJS
1️⃣ Install RabbitMQ Dependencies:
npm install @nestjs/microservices amqplib
2️⃣ Configure RabbitMQ in NestJS
Modify main.ts to enable AMQP transport:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.RMQ,
options: {
urls: ['amqp://guest:guest@localhost:5672'],
queue: 'log-queue',
queueOptions: { durable: true },
},
});
await app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();
3️⃣ Implement a Log Producer (Send Logs to RabbitMQ):
import { Injectable } from '@nestjs/common';
import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices';
@Injectable()
export class LogProducerService {
private client: ClientProxy;
constructor() {
this.client = ClientProxyFactory.create({
transport: Transport.RMQ,
options: {
urls: ['amqp://guest:guest@localhost:5672'],
queue: 'log-queue',
queueOptions: { durable: true },
},
});
}
sendLog(logMessage: string) {
this.client.emit('log-event', logMessage);
}
}
🔎 Implementing a Log Query API
To retrieve logs from the database, we need a Log API service. Here's a simple implementation using Spring Boot and PostgreSQL:
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/logs")
public class LogQueryController {
private final LogRepository logRepository;
public LogQueryController(LogRepository logRepository) {
this.logRepository = logRepository;
}
@GetMapping
public List<LogEntry> getLogs() {
return logRepository.findAll();
}
}
🏁 Conclusion
By using AMQP (RabbitMQ) for logging, we achieve the following:
✅ Asynchronous log collection → Services send logs without blocking execution.
✅ Reliable processing → Logs are queued even if the consumer is down.
✅ Scalability → Multiple consumers can process logs in parallel.
✅ Decoupled log retrieval → A separate query API provides access to stored logs.
Would you like to see an integration with Grafana or Kibana for visualization? Let me know in the comments! 🚀
Top comments (0)