Skip to content

sudovishal/vortexlog

Repository files navigation

VortexLog: Time-Series Log Ingestion Engine

A high-performance log ingestion engine built in Go and PostgreSQL, designed to handle high-volume log writes concurrently using a worker pool and batch processing. Logs are ingested via a REST API, buffered in-memory, and flushed to a time-partitioned Postgres table — then visualized in real time through a pre-configured Grafana dashboard.

For in-depth architectural explanations and design decisions, see Notes.md.

Architecture

Worker Pool and Graceful Shutdown Workflow

HTTP Client ──▶ POST /api/logs ──▶ Buffered Channel ──▶ Worker Pool (3 goroutines)
                                                              │
                                              Batch Insert (100 items or 10s timer)
                                                              │
                                                              ▼
                                                   PostgreSQL (partitioned)
                                                              │
                                                              ▼
                                                     Grafana Dashboard
  • Decoupled ingestion: HTTP handlers push logs into a buffered Go channel; background workers drain it in batches.
  • Batch writes: Each worker accumulates up to 100 logs or flushes every 10 seconds — whichever comes first.
  • Weekly partitioning: pg_partman auto-creates weekly range partitions on ingested_at, keeping indexes small and fast.
  • Advanced indexing: Composite B-Tree (service + level + time), GIN with jsonb_path_ops (metadata), and Trigram (message text search).
  • Graceful shutdown: On SIGINT/SIGTERM, the app stops accepting requests, flushes all in-memory buffers to Postgres, then exits cleanly.

Tech Stack

Layer Technology
Application Go 1.25, net/http, pgx/v5 connection pool
Database PostgreSQL 18, pg_partman, pg_cron
Code Generation sqlc (type-safe SQL → Go)
Migrations goose
Observability Grafana
Dev Tooling pgcli — interactive Postgres CLI with auto-completion and syntax highlighting
Containerization Docker & Docker Compose

Project Structure

.
├── main.go                  # Entrypoint — server, signal handling, worker pool init
├── cmd/
│   └── generator/           # Load-test log spammer — fires thousands of logs to stress-test ingestion
├── internal/
│   ├── api/                 # HTTP handlers and JSON payload types
│   ├── database/            # sqlc-generated query code
│   └── worker/              # Batch worker pool (channel consumer)
├── sql/
│   ├── schema/              # Goose migration files
│   └── queries/             # sqlc query definitions
├── Dockerfile               # Custom Postgres 18 image with pg_partman + pg_cron
├── docker-compose.yml       # Postgres + Adminer + Grafana
├── sqlc.yaml                # sqlc configuration
└── .env.example             # Environment variable template

🚀 Quick Start

1. Prerequisites

Ensure you have the following installed:

  • Docker & Docker Compose
  • Go (v1.22+)
  • goose — database migration tool
  • Git

Optional (recommended for development):

  • pgcli — a better Postgres CLI with auto-completion, syntax highlighting, and multi-line query editing (used throughout this guide instead of psql)
  • sqlc — SQL code generator (only needed if modifying queries)

2. Clone and Configure

git clone https://github.com/sudovishal/vortexlog.git
cd vortexlog

Copy the example environment file and fill in your values:

cp .env.example .env

Edit .env with your credentials:

DB_URL="postgres://<username>:<password>@localhost:5432/<db_name>?search_path=logsingest"
DB_USER="<username>"
DB_PASSWORD="<password>"
DB_NAME="logsdb"

GRAFANA_ADMIN_PASSWORD="<your_grafana_password>"

3. Launch the Infrastructure

This starts Postgres (custom image with pg_partman + pg_cron), Adminer, and Grafana:

docker compose up --build -d

Verify the containers are running:

docker compose ps

4. Run Database Migrations

cd sql/schema/
goose postgres "postgres://<username>:<password>@localhost:5432/<db_name>" up

Check migration status:

goose postgres "postgres://<username>:<password>@localhost:5432/<db_name>" status

5. Configure pg_cron (One-Time Setup)

Connect to your database using psql or pgcli:

pgcli -h localhost -p 5432 -U <db_user> -d <db_name>

Then run these SQL commands:

-- Activate pg_cron in your logs database
CREATE EXTENSION pg_cron;

-- Schedule pg_partman maintenance to run every night at midnight
SELECT cron.schedule(
    'partman-weekly-maintenance',
    '0 0 * * *',
    $$SELECT partman.run_maintenance();$$
);

-- Set the schema search path
SET search_path TO logsingest, public;

Why is this manual? pg_cron stores schedules in the system-level postgres database as cluster-wide background workers, so they can't be managed through application-level goose migrations. See Notes.md for details.

6. Start the Go Application

The Go app runs outside Docker (the compose file only manages the infrastructure services):

go run .

The API server will start on port 3001.

📡 API Reference

POST /api/logs

Ingest a log entry. The log is buffered and batch-inserted asynchronously.

Request:

curl -X POST http://localhost:3001/api/logs \
  -H "Content-Type: application/json" \
  -d '{
    "service_name": "auth-service",
    "log_level": "ERROR",
    "message": "Failed to validate JWT token",
    "created_at": "2026-06-02T10:30:00Z",
    "trace_id": "abc-123-def-456",
    "metadata": {
      "user_id": "u_9281",
      "endpoint": "/api/login",
      "status_code": 401
    }
  }'

Response: 201 Created

{ "status": "success" }

Payload Fields:

Field Type Required Description
service_name string Name of the originating service
log_level string Log severity (INFO, WARN, ERROR, etc.)
message string Log message text
created_at string (ISO 8601) Timestamp when the event occurred
trace_id string Distributed tracing correlation ID
metadata object (JSONB) Arbitrary key-value metadata

📊 Dashboards & Tools

Tool URL Description
Grafana http://localhost:3000 Log dashboard with live streaming, expression filters, and JSON unpacking
Adminer http://localhost:8080 Lightweight database management UI

Grafana credentials: Username: admin / Password: <your GRAFANA_ADMIN_PASSWORD from .env>

🛑 Stopping the App

# Stop the Go app with Ctrl+C (sends SIGINT)
# It will flush all remaining memory buffers to Postgres before exiting

# Tear down the infrastructure
docker compose down -v

The app utilizes a graceful shutdown pattern to flush all remaining memory buffers to Postgres before closing down, preventing data loss.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors