Setup Logging & Monitoring Aplikasi Self-Hosted

by Marcus Chen
Setup Logging & Monitoring Aplikasi Self-Hosted

Setup Logging & Monitoring Aplikasi Self-Hosted

Aplikasi kamu down jam 2 pagi. Kamu buka server, cek prosesnya sudah mati, tapi log-nya kosong. Atau lebih parah: log ada, tapi tersebar di lima file berbeda, formatnya campur aduk, dan kamu harus grep manual satu per satu sambil setengah sadar.

Ini bukan skenario hipotetikal. Ini yang terjadi kalau kamu deploy aplikasi self-hosted tanpa setup logging monitoring yang proper dari awal.

Artikel ini akan kasih kamu stack logging monitoring yang bisa jalan di VPS murah sekalipun — pakai Loki + Grafana + Promtail (stack PLG). Ringan, open-source, dan nggak butuh Elasticsearch yang rakus RAM.


Kenapa Stack Default Nggak Cukup

stdout dan file log mentah itu cukup untuk development. Tapi begitu aplikasi kamu di-deploy dan mulai ada traffic nyata, kamu butuh:

  • Aggregasi — semua log dari semua service di satu tempat
  • Search — bisa query log berdasarkan waktu, label, atau pattern
  • Alerting — dikabarin sebelum user yang lapor duluan
  • Retention — log lama dibuang otomatis biar disk nggak penuh

ELK Stack (Elasticsearch + Logstash + Kibana) bisa semua itu, tapi minimum RAM-nya 4GB hanya untuk Elasticsearch. Untuk VPS DigitalOcean atau Hetzner kelas $6–$10/bulan, itu terlalu boros.

Loki jauh lebih hemat karena dia cuma index label, bukan full-text index seperti Elasticsearch. Trade-off-nya: search harus pakai LogQL, bukan full-text search bebas. Untuk kebanyakan use case self-hosted, ini lebih dari cukup.


Arsitektur Stack yang Akan Kita Bangun

[Aplikasi kamu] --> stdout/file
       |
   [Promtail]  <-- scrape log
       |
    [Loki]     <-- store & index
       |
   [Grafana]   <-- visualize & alert

Semua akan jalan via Docker Compose di satu VPS. Kalau kamu punya multiple server, Promtail bisa di-deploy di tiap server dan push ke satu Loki sentral.


Persiapan: Struktur Direktori

Buat struktur folder dulu sebelum nulis config apapun:

mkdir -p ~/monitoring/{loki,promtail,grafana}
cd ~/monitoring

Nanti akan ada tiga file config utama:

  • loki/loki-config.yml
  • promtail/promtail-config.yml
  • docker-compose.yml

Step 1: Config Loki

Buat file loki/loki-config.yml:

auth_enabled: false

server:
  http_listen_port: 3100
  grpc_listen_port: 9096

common:
  instance_addr: 127.0.0.1
  path_prefix: /tmp/loki
  storage:
    filesystem:
      chunks_directory: /tmp/loki/chunks
      rules_directory: /tmp/loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

query_range:
  results_cache:
    cache:
      embedded_cache:
        enabled: true
        max_size_mb: 100

schema_config:
  configs:
    - from: 2020-10-24
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

ruler:
  alertmanager_url: http://localhost:9093

limits_config:
  retention_period: 7d

compactor:
  working_directory: /tmp/loki/retention
  compaction_interval: 10m
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150
  delete_request_store: filesystem

Satu hal penting: retention_period: 7d artinya log lebih dari 7 hari otomatis dihapus. Sesuaikan dengan kebutuhan dan kapasitas disk kamu. Kalau disk kamu kecil, turunkan ke 3d.


Step 2: Config Promtail

Promtail adalah agent yang scrape log dan kirim ke Loki. Buat promtail/promtail-config.yml:

server:
  http_listen_port: 9080
  grpc_listen_port: 0

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  - job_name: docker-containers
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: ['__meta_docker_container_name']
        regex: '/?(.*)'
        target_label: 'container'
      - source_labels: ['__meta_docker_container_log_stream']
        target_label: 'logstream'
      - source_labels: ['__meta_docker_container_label_com_docker_compose_service']
        target_label: 'service'

  - job_name: system
    static_configs:
      - targets:
          - localhost
        labels:
          job: varlogs
          __path__: /var/log/*.log

Config ini akan scrape semua container Docker secara otomatis plus system log di /var/log/. Kamu nggak perlu tambah config setiap kali deploy service baru.


Step 3: Docker Compose

Buat docker-compose.yml di root folder ~/monitoring/:

version: '3.8'

networks:
  monitoring:
    driver: bridge

volumes:
  grafana-data:

services:
  loki:
    image: grafana/loki:2.9.0
    ports:
      - "3100:3100"
    volumes:
      - ./loki:/etc/loki
    command: -config.file=/etc/loki/loki-config.yml
    networks:
      - monitoring
    restart: unless-stopped

  promtail:
    image: grafana/promtail:2.9.0
    volumes:
      - /var/log:/var/log:ro
      - /var/run/docker.sock:/var/run/docker.sock
      - ./promtail:/etc/promtail
    command: -config.file=/etc/promtail/promtail-config.yml
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - loki

  grafana:
    image: grafana/grafana:10.2.0
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=gantipasswordini
      - GF_USERS_ALLOW_SIGN_UP=false
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - monitoring
    restart: unless-stopped
    depends_on:
      - loki

Ganti gantipasswordini dengan password yang kuat. Jangan skip ini — Grafana yang exposed ke internet dengan password default adalah undangan buat dibobol.

Jalankan:

docker compose up -d

Cek semua container running:

docker compose ps

Step 4: Hubungkan Grafana ke Loki

  1. Buka browser, akses http://IP-VPS-KAMU:3000
  2. Login dengan credential yang kamu set di compose file
  3. Pergi ke Connections → Data Sources → Add data source
  4. Pilih Loki
  5. Di field URL, isi: http://loki:3100
  6. Klik Save & Test

Kalau muncul pesan "Data source connected and labels found", berarti Loki sudah terima log dari Promtail.

Sekarang coba query pertama kamu di Explore:

{job="varlogs"} |= "error"

Atau kalau mau lihat log dari container spesifik:

{container="nama-container-kamu"} | json | line_format "{{.message}}"

Gotcha yang Sering Bikin Stuck

1. Promtail nggak bisa akses Docker socket

Kalau log container nggak muncul, cek permission:

ls -la /var/run/docker.sock
# output: srw-rw---- 1 root docker ...

# Tambah user ke group docker, atau jalankan promtail sebagai root
docker compose logs promtail | grep -i error

Solusi paling cepat: tambah user: root di service promtail di compose file.

2. Loki reject log karena "out of order"

Ini terjadi kalau timestamp log kamu nggak monotonically increasing. Biasanya karena aplikasi nulis log dengan timestamp yang nggak konsisten. Tambahkan config ini di limits_config pada loki-config.yml:

limits_config:
  ingestion_rate_mb: 16
  ingestion_burst_size_mb: 32
  allow_structured_metadata: false
  reject_old_samples: true
  reject_old_samples_max_age: 168h

3. Port 3000 dan 3100 exposed ke publik

Jangan biarkan port ini terbuka langsung ke internet tanpa auth. Pakai Nginx sebagai reverse proxy dengan basic auth minimal, atau gunakan Tailscale/Wireguard supaya hanya bisa diakses dari network kamu.

Contoh blok Nginx cepat:

server {
    listen 80;
    server_name monitoring.domainmu.com;

    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

4. Disk penuh karena log explosion

Kalau ada bug yang bikin aplikasi kamu nulis ribuan baris log per detik, Loki bisa isi disk dalam hitungan jam. Pasang rate limit di Promtail:

# tambah di scrape_configs job kamu
pipeline_stages:
  - limit:
      rate: 1000
      burst: 2000
      drop: true

Bikin Alert Sederhana di Grafana

Setup monitoring tanpa alerting itu setengah-setengah. Buat alert basic untuk error rate:

  1. Buka Alerting → Alert Rules → New alert rule
  2. Di query, pakai LogQL:
sum(rate({container="aplikasi-kamu"} |= "ERROR" [5m])) > 10

Ini akan trigger kalau ada lebih dari 10 error per menit dalam 5 menit terakhir.

  1. Set Contact Point ke email atau Telegram (Grafana support keduanya native)
  2. Assign ke Notification Policy

Untuk Telegram, kamu butuh bot token dan chat ID. Masuk ke Alerting → Contact Points → Add contact point → Telegram, isi token dan chat ID, done.


Langkah Lanjutan

Setup logging monitoring aplikasi self-hosted dengan stack PLG ini sudah cukup untuk handle traffic ribuan request per hari di VPS kelas menengah. Ini yang gue lakuin setelah setup ini running:

  1. Tambah metrics dengan Prometheus + Node Exporter — kalau kamu juga mau monitor CPU, RAM, dan disk usage (bukan hanya log), tambah Prometheus ke stack ini. Grafana sudah support keduanya sekaligus.

  2. Buat dashboard custom — import dashboard ID 13639 di Grafana untuk Docker monitoring siap pakai, lalu modifikasi sesuai kebutuhan.

  3. Backup Grafana dashboard — export dashboard kamu sebagai JSON dan simpan di Git. Jangan sampai konfigurasi dashboard hilang kalau VPS-nya harus di-rebuild.

  4. Pisahkan monitoring stack ke VPS berbeda — kalau aplikasi utama kamu down, monitoring kamu juga ikut down kalau di server yang sama. Untuk production yang lebih serius, taruh stack PLG ini di VPS terpisah dan arahkan Promtail dari semua server ke sana.

Kalau kamu stuck di langkah mana pun, cek log Promtail dulu:

docker compose logs -f promtail

Hampir semua masalah koneksi dan permission akan kelihatan di sana.