A lightweight, high-performance microservice for forwarding browser-side logs to server-side log aggregation systems (ELK, Loki, Splunk, etc.).
The Logs microservice provides a REST API that accepts JSON log objects from browser applications and outputs them to stdout. When combined with Docker logging drivers, this enables seamless integration with enterprise log aggregation systems without requiring client-side configuration of log collectors.
- Universal Integration: Works with any log aggregation system via Docker logging drivers
- Zero Client Dependencies: No need for browser-side log shipping libraries
- Production Ready: Built with NestJS for reliability and performance
- Observability: Built-in Prometheus metrics for monitoring
- Flexible Log Levels: Support for debug, verbose, info, warn, and error levels
- Lightweight: Minimal resource footprint, optimized for containerized environments
- Quick Start
- Installation
- API Reference
- Configuration
- Client Integration
- Monitoring
- Architecture
- Contributing
- License
Run the microservice locally for testing:
docker run -it --rm -p 3000:3000 mtsrus/logsTest the endpoint:
curl -d '{"message": "Sample log", "timestamp": "2025-12-23T10:30:00Z"}' \
-H "Content-Type: application/json" \
-X POST http://localhost:3000/logs/logThe JSON payload will be written to stdout, ready to be consumed by your logging driver.
For production deployment:
docker run -d \
--name logs \
--restart always \
-p 3000:3000 \
--log-driver=fluentd \
--log-opt fluentd-address=localhost:24224 \
mtsrus/logsversion: '3.8'
services:
logs:
image: mtsrus/logs:latest
ports:
- "3000:3000"
restart: always
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: browser.logsapiVersion: apps/v1
kind: Deployment
metadata:
name: logs-microservice
spec:
replicas: 2
selector:
matchLabels:
app: logs
template:
metadata:
labels:
app: logs
spec:
containers:
- name: logs
image: mtsrus/logs:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"All endpoints accept POST requests with JSON payloads.
| Endpoint | Log Level | Description |
|---|---|---|
/logs/debug |
DEBUG | Detailed debugging information |
/logs/verbose |
VERBOSE | Verbose/trace level logs |
/logs/log |
INFO | General informational messages |
/logs/warn |
WARNING | Warning messages for potential issues |
/logs/error |
ERROR | Error messages for failures |
{
"message": "User action completed",
"timestamp": "2025-12-23T10:30:00Z",
"userId": "user-123",
"userAgent": "Mozilla/5.0...",
"location": "https://example.com/dashboard",
"context": {
"feature": "checkout",
"sessionId": "session-abc"
}
}All fields are optional. The microservice accepts any valid JSON structure.
Success (200 OK):
{
"status": "logged"
}Error (400 Bad Request):
{
"statusCode": 400,
"message": "Invalid JSON"
}The microservice can be configured using the following environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP server port |
NODE_ENV |
production |
Node.js environment |
Configure your Docker logging driver to forward stdout to your log aggregation system. Examples:
docker run -d \
--log-driver=fluentd \
--log-opt fluentd-address=localhost:24224 \
--log-opt tag="browser.{{.Name}}" \
mtsrus/logsdocker run -d \
--log-driver=syslog \
--log-opt syslog-address=tcp://192.168.0.1:514 \
mtsrus/logsdocker run -d \
--log-driver=awslogs \
--log-opt awslogs-region=us-east-1 \
--log-opt awslogs-group=browser-logs \
mtsrus/logsclass LogService {
private readonly endpoint: string;
constructor(endpoint: string = '/api/logs') {
this.endpoint = endpoint;
}
async log(level: 'debug' | 'verbose' | 'log' | 'warn' | 'error', data: Record<string, any>) {
try {
await fetch(`${this.endpoint}/${level}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
location: window.location.href,
...data
})
});
} catch (error) {
console.error('Failed to send log:', error);
}
}
info(message: string, context?: Record<string, any>) {
return this.log('log', { message, ...context });
}
warn(message: string, context?: Record<string, any>) {
return this.log('warn', { message, ...context });
}
error(message: string, error?: Error, context?: Record<string, any>) {
return this.log('error', {
message,
error: error?.message,
stack: error?.stack,
...context
});
}
}
// Usage
const logger = new LogService('http://localhost:3000/logs');
logger.info('User logged in', { userId: '123' });
logger.error('Payment failed', new Error('Timeout'), { orderId: 'order-456' });import { useEffect } from 'react';
export function useErrorLogger(logEndpoint: string) {
useEffect(() => {
const errorHandler = (event: ErrorEvent) => {
fetch(`${logEndpoint}/error`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
error: event.error?.stack,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
location: window.location.href
})
});
};
window.addEventListener('error', errorHandler);
return () => window.removeEventListener('error', errorHandler);
}, [logEndpoint]);
}The microservice exposes Prometheus-compatible metrics at the /metrics endpoint.
Available Metrics:
- HTTP request duration
- Request count by status code
- Request count by endpoint
- Active connections
Example Prometheus Configuration:
scrape_configs:
- job_name: 'logs-microservice'
static_configs:
- targets: ['logs:3000']
metrics_path: '/metrics'Security Note: Restrict access to /metrics via your reverse proxy if exposing the service publicly.
┌─────────────┐ HTTP POST ┌──────────────┐ stdout ┌─────────────┐
│ Browser │ ─────────────────> │ Logs Service │ ───────────────> │ Docker │
│ Application │ JSON Payload │ (NestJS) │ Structured Log │ Logging │
└─────────────┘ └──────────────┘ │ Driver │
└──────┬──────┘
│
▼
┌──────────────────┐
│ Log Aggregation │
│ (ELK/Loki/etc) │
└──────────────────┘
- Simplicity: Single responsibility - accept logs and output to stdout
- Scalability: Stateless design enables horizontal scaling
- Reliability: Built on NestJS with production-grade error handling
- Flexibility: Agnostic to log aggregation backend
- Framework: NestJS - Enterprise-grade Node.js framework
- Runtime: Node.js 20+
- Containerization: Docker
- Monitoring: Prometheus metrics
- Input Validation: All incoming JSON is validated
- No Data Storage: Logs are immediately output to stdout, no persistence
- CORS: Configure CORS headers in your reverse proxy
- Rate Limiting: Implement rate limiting at proxy/ingress level
- Authentication: Add authentication via reverse proxy if needed
- Throughput: Handles 1000+ requests/second on modest hardware
- Latency: < 10ms average response time
- Memory: ~50MB baseline, scales linearly with concurrent requests
- CPU: Minimal CPU usage, I/O bound workload
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Issues: GitHub Issues
- Security: See SECURITY.md for reporting vulnerabilities
See CHANGELOG.md for a list of changes.
Maintained by: MobileTeleSystems Contributors: LabEG