Access Log

Access Log #

Access log adalah catatan setiap request HTTP yang diterima Caddy — siapa yang meminta apa, kapan, dengan status apa, dan berapa lama. Ini adalah data paling fundamental untuk monitoring, debugging, security audit, dan analisis traffic.

Caddy memiliki sistem logging yang sangat fleksibel. Secara default Caddy tidak menulis access log sama sekali (hanya error log ke stderr). Kamu perlu mengkonfigurasi access log secara eksplisit untuk setiap site atau secara global.

Mengaktifkan Access Log #

example.com {
    # Aktifkan access log untuk site ini
    log
    
    file_server { root /var/www/html }
}

Tanpa konfigurasi tambahan, log ditulis ke stderr dalam format console — cocok untuk development tapi tidak ideal untuk production.


Output ke File #

example.com {
    log {
        # Tulis ke file
        output file /var/log/caddy/access.log
        
        # Format JSON (direkomendasikan untuk production)
        format json
    }
    
    file_server { root /var/www/html }
}

Semua Opsi Direktif log #

example.com {
    log {
        # ── Output Destination ────────────────────────────────────
        # Tulis ke file dengan rotation otomatis
        output file /var/log/caddy/access.log {
            roll_size     50mb    # Rotate saat file mencapai 50MB
            roll_keep     10      # Simpan 10 file rotasi terakhir
            roll_keep_days 30     # Hapus file lebih dari 30 hari
            roll_uncompressed     # Jangan kompres file rotasi (default: kompres)
        }
        
        # ── Format ────────────────────────────────────────────────
        # Pilihan: json, console, single_field
        format json
        
        # ── Level ─────────────────────────────────────────────────
        # Pilihan: DEBUG, INFO, WARN, ERROR
        # Default: INFO
        level INFO
        
        # ── Sampling ──────────────────────────────────────────────
        # Untuk traffic sangat tinggi: log hanya sebagian request
        # first 10: log 10 request pertama per interval
        # thereafter 100: setelah itu, log 1 dari setiap 100
        # sampling {
        #     interval 1s
        #     first    10
        #     thereafter 100
        # }
    }
    
    file_server { root /var/www/html }
}

Format JSON — Struktur Log Entry #

Setiap baris log JSON berisi informasi lengkap tentang request:

{
  "ts": 1704067200.123456,
  "logger": "http.log.access.log0",
  "msg": "handled request",
  "request": {
    "remote_ip": "203.0.113.1",
    "remote_port": "54321",
    "proto": "HTTP/2.0",
    "method": "GET",
    "host": "example.com",
    "uri": "/api/users?page=1",
    "headers": {
      "User-Agent": ["Mozilla/5.0 ..."],
      "Accept": ["application/json"],
      "Referer": ["https://example.com/dashboard"]
    },
    "tls": {
      "resumed": false,
      "version": 772,
      "cipher_suite": 4865,
      "proto": "h2",
      "server_name": "example.com"
    }
  },
  "bytes_read": 0,
  "user_id": "",
  "duration": 0.023456,
  "size": 1234,
  "status": 200,
  "resp_headers": {
    "Content-Type": ["application/json"],
    "Content-Encoding": ["gzip"]
  }
}

Format Console #

Format console lebih mudah dibaca oleh manusia, ideal untuk development:

# Development configuration
localhost:3001 {
    log {
        output stderr
        format console
        level DEBUG
    }
    
    reverse_proxy localhost:3000
}

Output konsol terlihat seperti:

2024/01/15 14:30:45.123 INFO    http.log.access handled request    {"request": {"remote_ip": "127.0.0.1", "method": "GET", "uri": "/api/users", "proto": "HTTP/1.1"}, "status": 200, "duration": 0.023456}

Per-Site Logging dengan Nama Logger Berbeda #

Untuk deployment multi-site, konfigurasi log terpisah per site:

example.com {
    log {
        output file /var/log/caddy/example-access.log {
            roll_size 100mb
            roll_keep 7
            roll_keep_days 30
        }
        format json
    }
    
    reverse_proxy backend-1:3000
}

api.example.com {
    log {
        output file /var/log/caddy/api-access.log {
            roll_size 200mb   # API lebih banyak traffic
            roll_keep 14
            roll_keep_days 90  # Simpan lebih lama untuk audit
        }
        format json
    }
    
    reverse_proxy api-backend:8080
}

admin.example.com {
    log {
        output file /var/log/caddy/admin-access.log {
            roll_size 10mb
            roll_keep 30      # Simpan lebih banyak file untuk audit keamanan
            roll_keep_days 365 # Satu tahun untuk compliance
        }
        format json
    }
    
    basicauth { ... }
    reverse_proxy admin:9000
}

Menyaring Request dari Log #

Tidak semua request perlu dicatat — request health check yang sangat sering bisa memenuhi log:

example.com {
    log {
        output file /var/log/caddy/access.log
        format json
        
        # Jangan log request ke /health dan /ping
        # (format: exclude path *)
        exclude_status 200    # Atau jangan log sukses (hanya log error)
    }
    
    # Alternatif: gunakan handler terpisah sebelum log
    @health path /health /ping /metrics
    handle @health {
        respond "ok" 200
        # Request ini tidak melewati 'log' directive di bawah
    }
    
    # Sisanya: log normal
    log {
        output file /var/log/caddy/access.log
        format json
    }
    
    reverse_proxy backend:3000
}

Global Access Log #

Konfigurasi log yang berlaku untuk semua site:

{
    # Log global — berlaku untuk semua request
    log default {
        output file /var/log/caddy/global-access.log {
            roll_size 500mb
            roll_keep 5
            roll_keep_days 30
        }
        format json
        level INFO
    }
}

example.com {
    # Site ini menggunakan global log
    # Tidak perlu direktif log terpisah
    file_server { root /var/www/html }
}

api.example.com {
    # Site ini override dengan log khusus
    log {
        output file /var/log/caddy/api-access.log
        format json
    }
    
    reverse_proxy api:8080
}

Analisis Access Log #

# Top 10 IP yang paling banyak request
cat /var/log/caddy/access.log | \
    jq -r '.request.remote_ip' | \
    sort | uniq -c | sort -rn | head -10

# Top 10 URL yang paling sering diakses
cat /var/log/caddy/access.log | \
    jq -r '.request.uri' | \
    sort | uniq -c | sort -rn | head -10

# Status code distribution
cat /var/log/caddy/access.log | \
    jq '.status' | \
    sort | uniq -c | sort -rn

# Request yang memakan waktu lebih dari 1 detik
cat /var/log/caddy/access.log | \
    jq 'select(.duration > 1) | {uri: .request.uri, duration: .duration, status: .status}' | \
    head -20

# Traffic per jam (hari ini)
cat /var/log/caddy/access.log | \
    jq -r 'select(.ts > (now - 86400)) | (.ts | strftime("%H"))' | \
    sort | uniq -c

# Error requests (4xx dan 5xx)
cat /var/log/caddy/access.log | \
    jq 'select(.status >= 400) | {
        time: (.ts | todate),
        ip: .request.remote_ip,
        method: .request.method,
        uri: .request.uri,
        status: .status
    }'

Integrasi dengan Log Aggregation #

# Kirim log ke Elasticsearch (via Filebeat)
# /etc/filebeat/filebeat.yml
# filebeat.inputs:
# - type: log
#   paths:
#     - /var/log/caddy/*.log
#   json.keys_under_root: true
#   json.add_error_key: true
# 
# output.elasticsearch:
#   hosts: ["elasticsearch:9200"]
#   index: "caddy-logs-%{+yyyy.MM.dd}"

# Atau kirim ke Loki (via Promtail)
# scrape_configs:
#   - job_name: caddy
#     static_configs:
#       - targets: [localhost]
#         labels:
#           job: caddy
#           __path__: /var/log/caddy/*.log

# Real-time monitoring
tail -f /var/log/caddy/access.log | \
    jq -r '"\(.ts | todate) \(.status) \(.request.method) \(.request.uri) \(.duration | (. * 1000 | round))ms"'

Ringkasan #

  • Caddy tidak menulis access log secara default — tambahkan direktif log { ... } di dalam setiap site block untuk mengaktifkannya.
  • Gunakan format json di production untuk kemudahan parsing dan integrasi dengan tools seperti Elasticsearch, Loki, atau Datadog.
  • Konfigurasi roll_size, roll_keep, dan roll_keep_days untuk mencegah log menghabiskan disk space.
  • Gunakan handler terpisah (tanpa log) untuk endpoint health check yang sangat sering dipanggil agar log tidak dibanjiri request tidak penting.
  • Setiap entry log JSON berisi request, status, duration, dan size — gunakan jq untuk analisis cepat langsung dari command line.
  • Untuk multi-site, konfigurasikan log per-site dengan file terpisah agar mudah dimonitor dan di-audit per domain.

← Sebelumnya: Map   Berikutnya: Error Log →


Access Log untuk Security Audit #

# Deteksi scanning/probing dari access log
cat /var/log/caddy/access.log | jq '
  select(.status == 404) |
  .request.remote_ip
' | sort | uniq -c | sort -rn | head -10

# IP yang terus mencoba path /admin, /.env, /wp-login
cat /var/log/caddy/access.log | jq -r '
  select(.request.uri | test("/(admin|wp-login|\\.env|config\\.php|shell)")) |
  "\(.request.remote_ip) \(.request.uri)"
' | sort | uniq -c | sort -rn

Analisis ini membantu mengidentifikasi IP yang melakukan scanning otomatis — yang bisa langsung di-blokir menggunakan remote_ip matcher atau fail2ban.

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact