Merkezi Loglama ile Hangi Soruları Yanıtlayabilirsiniz?
Bir hata sırasında Nginx, uygulama ve sistem günlüklerini ayrı sunucularda aramak olayın zaman çizelgesini çıkarmayı zorlaştırır. Merkezi loglama, izin verilen kayıtları ortak bir yerde sorgulamanızı sağlar. Dağıtım sonrası 5xx artışı, uygulama hatası ve veri akışının kesilmesi birlikte incelenebilir. Bunun için doğru dosyaların gerçekten toplandığını, saatlerin tutarlı olduğunu ve sorgunun gerçek log biçimini işlediğini doğrulamak gerekir; boş bir panel tek başına sistemin sağlıklı olduğunu göstermez.
Bu rehber, tek VPS üzerinde Loki 3.7.9, Grafana Alloy 1.21.3 ve Grafana 13.3.0 ile yeni bir Docker Compose kurulumu için örnek verir. Promtail, 2 Mart 2026’da kullanım ömrünü tamamladı; yeni kurulumun toplayıcısı Alloy’dur. Combined biçimindeki Nginx access logları, ayrı error dosyaları ile belirli biçimdeki JSON uygulama logları toplanır. Systemd journal için 12 saatlik başlangıç penceresi ve unit etiketi, worker için çok satırlı kayıt örneği ve ayrı native systemd kurulum yolu da verilir. Otomatik konteyner keşfi bu dosya/journal örneğine dahil değildir. Sürümler 4 Kasım 2026 kontrolüne göre sabittir; mevcut Loki verisinin şemasını bu yeni kurulum örneğiyle değiştirmeyin.
Loki, Alloy ve Grafana’nın Görevleri
Alloy seçilmiş kaynakları okur, gerekli zaman bilgisini işler ve Loki’ye gönderir. Loki etiketlerle akışları indeksler ve log içeriğini saklar. Grafana Loki’yi sorgular, panoları gösterir ve burada tercih edilen Grafana yönetimli alarm kurallarını değerlendirir. Loki’nin resmi sayfası mimariyi tanıtır. Tek makinede bu üç bileşenin birlikte durması yüksek erişilebilirlik sağlamaz; VPS arızasında toplama, sorgu ve alarm değerlendirmesi aynı anda etkilenebilir.
Log satırı sayısı istek sayısıyla her zaman aynı değildir: bir HTTP isteği birden fazla uygulama hatası üretebilir. Aynı şekilde saniyelik hata satırı hızı, hata yüzdesi değildir. Aşağıdaki sorgular bu ayrımı korur; performans ve kapasite kararı için ölçülen günlük hacim, aktif akışlar, disk/inode kullanımı ve sorgu yükü birlikte değerlendirilir. Belirli bir RAM miktarı veya yedi günlük saklama süresi bütün iş yüklerine yeterli sayılamaz.
Kurulum Öncesi Dizinler, İzinler ve Veri Biçimi
Örnek Linux VPS üzerinde Docker Engine ve güncel Compose eklentisi, yönetim yetkisi, yeterli disk alanı ve doğrulanmış yedekleme planı gerektirir. Var olan üretim verisini kullanmadan ayrı bir test dizininde başlayın. Compose ağındaki servis adları, host üzerinde native çalışan systemd servislerinin adresleriyle aynı değildir; aşağıdaki dosyalar aynı Compose kurulumu içindir. Sürüm etiketlerini rastgele latest ile değiştirmeyin; resmi sürüm notlarını ve uyumluluğu kontrol edip test edilen imaj digestlerini dağıtım kaydına ekleyin.
Çalışma dizininde docker-compose.yml, loki.yml, config.alloy, state/loki, state/alloy, state/grafana ve secrets/grafana_admin_password hazırlayın. Veri dizinleri sırasıyla 10001, 473 ve 472 UID’lerinin yazabileceği izinlerde olmalıdır; bunlar örnekte açıkça seçilen konteyner kimlikleridir. Gizli parola dosyasını yalnız yönetici ve Grafana UID472 okuyabilsin. Log okuma grubunun gerçek sayısal GID’sini LOG_READ_GID olarak ayarlayın; UID473 ve bu gruba gereken dizin geçişi/dosya okuma iznini verin. Her bağlanan kaynak önceden doğru türde var olmalıdır; kısa bind sözdizimi eksik dosya yerine dizin oluşturabilir. SELinux etkinse uygun, dar kapsamlı erişim politikasını da doğrulayın.
Journal için mevcut /var/log/journal veya /run/log/journal dizinini JOURNAL_DIR olarak seçin; gerçek systemd-journal ve adm grup GID değerlerini JOURNAL_GID ve ADM_GID değişkenlerine koyun. Dizin ve journal dosyaları UID473 ile ek gruplarından okunabilir, /etc/machine-id gerçek host dosyası olmalıdır. Worker örneği için uygulama mountunda worker.log bulunmalıdır. Grup isimleri dağıtıma göre farklıysa gerçek okuma politikasını uyarlayın. İmajların resmi digestleri aşağıda sabitlenmiştir. Alloy imajı varsayılan olarak root çalışır; burada UID473 açık bir geçersiz kılmadır ve upstream bu imajdaki non-root desteğini deneysel olarak işaretler. Bu seçeneğin izinlerini hedef ortamda doğrulayın; ayrı native kurulum yolu da aşağıdadır.
Docker Compose: Özel Loki, Yerel Grafana Erişimi
Aşağıdaki dosyayı docker-compose.yml olarak kaydedin. Loki ve Alloy için host portu yayımlanmaz; Grafana yalnız 127.0.0.1:3000 üzerindedir. Loki HTTP dinleyicisi konteyner içinde Alloy/Grafana’nın ulaşabilmesi için açık, gRPC dinleyicisi aynı konteynerin loopback adresindedir. Bu düzen host yöneticisini veya aynı güven alanındaki konteynerleri birbirinden ayırmaz. Gerçek Docker ağ/routing ve güvenlik duvarı ayarlarını dışarıdan kontrol edin. /var/log/nginx ve /var/www/app/storage/logs kaynaklarını yalnız gereken gerçek log dizinlerine uyarlayın; loglara parola, token veya gereksiz kişisel veri yazılmasını kaynağında engelleyin.
services:
loki:
image: grafana/loki@sha256:0db216b630ae1f348a1584c67f3cead8b4c1fc839169a39501db664744158e28
user: 10001:10001
command:
- -config.file=/etc/loki/config.yml
volumes:
- ./loki.yml:/etc/loki/config.yml:ro
- ./state/loki:/var/lib/loki
restart: unless-stopped
alloy:
image: grafana/alloy@sha256:8ab871206bd2d8f89277f53abffee5f452a293980788d52f85fa183a87a505a4
user: 473:473
group_add:
- ${LOG_READ_GID:?Set the host log-reader group GID}
- ${JOURNAL_GID:?Set actual systemd-journal group GID}
- ${ADM_GID:?Set actual adm group GID}
command:
- run
- --server.http.listen-addr=127.0.0.1:12345
- --storage.path=/var/lib/alloy
- /etc/alloy/config.alloy
volumes:
- ./config.alloy:/etc/alloy/config.alloy:ro
- ./state/alloy:/var/lib/alloy
- /var/log/nginx:/logs/nginx:ro
- /var/www/app/storage/logs:/logs/app:ro
- ${JOURNAL_DIR:?Set existing readable host journal directory}:/journal:ro
- /etc/machine-id:/etc/machine-id:ro
depends_on:
- loki
restart: unless-stopped
grafana:
image: grafana/grafana@sha256:488984b716c0862639f91a698adbe2862677a5a07bad42da5f03f3030883b205
user: 472:472
ports:
- 127.0.0.1:3000:3000
volumes:
- ./state/grafana:/var/lib/grafana
- ./secrets/grafana_admin_password:/run/secrets/grafana_admin_password:ro
environment:
GF_SECURITY_ADMIN_PASSWORD__FILE: /run/secrets/grafana_admin_password
GF_AUTH_ANONYMOUS_ENABLED: 'false'
GF_USERS_ALLOW_SIGN_UP: 'false'
depends_on:
- loki
restart: unless-stopped
Diğer iki yapılandırmayı da tamamlayın. LOG_READ_GID, JOURNAL_GID, ADM_GID ve JOURNAL_DIR ayarlı olarak docker compose config çalıştırın. Ardından docker compose run --rm --no-deps loki -config.file=/etc/loki/config.yml -verify-config=true ve docker compose run --rm --no-deps alloy validate /etc/alloy/config.alloy ile kontrol edin. Seçilmiş imajların entrypoint değerleri sırasıyla /usr/bin/loki ve /bin/alloy olduğundan bu argümanlar doğrudan ilgili programa gider; –entrypoint veya sh -c eklemek farklı davranış yaratır. Loki doğrulama bayrağı yapılandırmayı kontrol edip çıkar. Test ortamında docker compose up -d sonrasında logları ve gerçek kaynak teslimini kontrol edin; depends_on hazır olmayı beklemez. Grafana’ya güvenilir SSH tüneliyle erişin. Parola dosyası yeni yönetici hesabının başlangıcı içindir; mevcut Grafana DB parolasını değiştirmez. Yapılandırma kontrolü gerçek journal okumasını, Docker izinlerini veya bildirimi doğrulamaz.
Loki: TSDB Şeması ve Yedi Günlük Saklama
loki.yml aşağıda yeni, tek kopyalı filesystem kurulumu için verilmiştir. TSDB, v13 ve 24 saatlik indeks dönemi birlikte kullanılır. auth_enabled: false yalnız tek kiracılı modu seçer; kullanıcı kimlik doğrulaması eklemez. Eski şema dönemlerini silmek veya tarihlerini değiştirmek geçmiş logları erişilemez bırakabilir. Üretimden geçişte bütün eski schema_config dönemlerini koruyun ve yeni dönem/depo başlangıcını ilgili sürümün yükseltme belgelerine göre planlayın.
# Loki 3.7.9: fresh single-host Compose deployment, no published Loki ports.
auth_enabled: false
target: all
server:
http_listen_address: 0.0.0.0
http_listen_port: 3100
grpc_listen_address: 127.0.0.1
grpc_listen_port: 9096
common:
instance_addr: 127.0.0.1
path_prefix: /var/lib/loki
replication_factor: 1
ring:
kvstore:
store: inmemory
storage:
filesystem:
chunks_directory: /var/lib/loki/chunks
rules_directory: /var/lib/loki/rules
schema_config:
configs:
- from: "2024-01-01"
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /var/lib/loki/index
cache_location: /var/lib/loki/index-cache
ingester:
wal:
enabled: true
dir: /var/lib/loki/wal
limits_config:
retention_period: 168h
reject_old_samples: true
reject_old_samples_max_age: 168h
compactor:
working_directory: /var/lib/loki/compactor
retention_enabled: true
delete_request_store: filesystem
analytics:
reporting_enabled: false
limits_config.retention_period hedef süreyi, Compactor ise etkin temizlik sürecini belirler. delete_request_store ve kalıcı çalışma dizini gereklidir; chunk silme asenkrondur. Süreyi azaltmayı acil disk boşaltma yöntemi saymayın; resmi belgeler saklama değişikliğinin geriye dönük uygulanmadığını belirtir. reject_old_samples_max_age eski olayların kabul sınırıdır, saklama süresiyle ayrı işleve sahiptir. Yerel filesystem verisi S3 ayarı değiştirilince otomatik taşınmaz; depo erişimi, şema dönemleri, eski veriye erişim ve geri dönüş ayrıca planlanır. S3 uyumlu uzak yedekleme rehberi yedek hedefini değerlendirmeye yardımcı olur; yedek, sorgulanabilir Loki deposunun yerine geçmez.
Alloy ile Nginx, Uygulama ve Journal Kayıtlarını Toplama
config.alloy aşağıda access.log, error.log, orders.log, worker.log ve seçilen host journal dizinini toplar. Nginx access standart combined biçimindedir; error dosyası ayrı kalır. orders.log her satırda RFC3339 time, küçük harf level ve message alanlı JSON içerir. Worker kaydının ilk satırı 2026-11-04T12:00:00Z ERROR job failed biçiminde başlar; sonraki stack trace satırları bu olayla birleştirilir. Laravel veya farklı dosya biçimlerini otomatik JSON kabul etmeyin. Bütün kaynaklardaki vps-01 değerini gerçek, kalıcı VPS kimliğiyle değiştirin; konteyner HOSTNAME’i host kimliği sayılmaz.
// Alloy v1.21.3. Set the real, stable VPS identity in both sources.
loki.source.file "nginx" {
targets = [
{__path__ = "/logs/nginx/access.log", job = "nginx", log_type = "access", env = "prod", host = "vps-01"},
{__path__ = "/logs/nginx/error.log", job = "nginx", log_type = "error", env = "prod", host = "vps-01"},
]
file_match { enabled = true }
forward_to = [loki.process.logs.receiver]
}
loki.source.file "app" {
targets = [
{__path__ = "/logs/app/orders.log", job = "app", service = "webshop", env = "prod", host = "vps-01"},
]
file_match { enabled = true }
forward_to = [loki.process.logs.receiver]
}
loki.process "logs" {
// Nginx combined access format only; error logs remain untouched.
stage.match {
selector = "{job=\"nginx\", log_type=\"access\"}"
stage.regex {
expression = `^\S+ \S+ \S+ \[(?P<event_time>[^\]]+)\] "[^"]*" [0-9]{3} [0-9]+ "[^"]*" "[^"]*"$`
}
stage.timestamp {
source = "event_time"
format = "02/Jan/2006:15:04:05 -0700"
action_on_failure = "skip"
}
}
// One JSON object per line; time must be RFC3339/RFC3339Nano.
stage.match {
selector = "{job=\"app\"}"
stage.json { expressions = {event_time = "time"} }
stage.timestamp {
source = "event_time"
format = "RFC3339Nano"
action_on_failure = "skip"
}
}
stage.label_drop { values = ["filename"] }
forward_to = [loki.write.local.receiver]
}
loki.write "local" {
endpoint { url = "http://loki:3100/loki/api/v1/push" }
}
loki.relabel "journal_units" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
}
loki.source.journal "system" {
path = "/journal"
max_age = "12h"
labels = {job = "systemd", host = "vps-01", env = "prod"}
relabel_rules = loki.relabel.journal_units.rules
forward_to = [loki.write.local.receiver]
}
loki.source.file "worker" {
targets = [
{__path__ = "/logs/app/worker.log", job = "worker", service = "worker", env = "prod", host = "vps-01"},
]
file_match { enabled = true }
forward_to = [loki.process.worker.receiver]
}
loki.process "worker" {
stage.multiline {
firstline = `^[0-9]{4}-[0-9]{2}-[0-9]{2}T`
max_wait_time = "3s"
max_lines = 128
}
stage.regex {
expression = `(?s)^(?P<event_time>[^ ]+) .*`
}
stage.timestamp {
source = "event_time"
format = "RFC3339Nano"
action_on_failure = "skip"
}
stage.label_drop { values = ["filename"] }
forward_to = [loki.write.local.receiver]
}
Geçerli access/JSON zamanları olay zamanı olarak kullanılır. Parse edilemeyen zamanlarda skip davranışı kaynak okuma zamanını korur; bozuk JSON satırları sessizce atılmaz. Bu, bozuk kayıtların sorguda ayrıca aranması gerektiği anlamına gelir. filename etiketi kaldırılır; kullanıcı ID, URL yolu, mesaj ve level indeks etiketine çevrilmez. Asıl satır korunur, gerekli alanlar sorgu sırasında çıkarılır. Eski Promtail belgeleri yalnız geçiş referansıdır. Alloy dönüştürme aracının uyarılarını, dosya konumlarını ve pozisyon durumunu inceleyin; otomatik çeviri teslimi veya sıfır kayıp/tekrarı kanıtlamaz.
Grafana ve LogQL: Gerçek 5xx Yanıtlarını Bulma
Grafana’da Connections → Data sources üzerinden Loki ekleyin; bu Compose ağı için URL http://loki:3100 olmalıdır. Save & test sonrasında Explore’da Loki’yi seçin ve test kaydının zaman aralığını açın. auth_enabled: false ile bu örnekte tenant header gerekmez. HTTP erişim testi başarılı olsa bile seçilen dosyanın toplandığını ayrıca doğrulayın. Aşağıdaki sorgu yalnız combined access dosyasındaki durum kodunu ayıklar; URL veya User-Agent içinde geçen 500 ifadesini hata yanıtı saymaz.
{job="nginx", log_type="access"} |~ `^\S+ \S+ \S+ \[[^\]]+\] "[^\"]*" [0-9]{3} [0-9]+ "[^\"]*" "[^\"]*"$` | pattern `<remote_addr> <_> <_> [<_>] "<request>" <status> <_> "<_>" "<_>"` | status=~"5[0-9][0-9]"Önce ^ ve $ ile bütün combined satır biçimi doğrulanır, sonra pattern içindeki request alanı tek parça alınır; böylece isteğin “-” veya boş olması 5xx durumunu saklamaz. status filtresi yalnız 500–599 kodlarını seçer. Bu biçime uymayan bütün satırları aşağıdaki ayrı sorguyla inceleyin. Pattern bir literal/capture eşleşmediğinde durabilir ve kısmi alanlar bırakabilir; bu durumda otomatik __error__ etiketi üretildiği varsayılmaz. Sadece status=”” kontrolü, status çıkarıldıktan sonraki bozuk kuyruğu yakalamaz. Farklı log biçiminde hem tam-satır filtresi hem pattern uyarlanmalıdır.
{job="nginx", log_type="access"} !~ `^\S+ \S+ \S+ \[[^\]]+\] "[^\"]*" [0-9]{3} [0-9]+ "[^\"]*" "[^\"]*"$`Sonraki sorgu seçilen gerçek 5xx satırlarının beş dakikalık penceredeki saniyelik ortalama hızını host’a göre verir. Pencere içindeki toplam adet için rate yerine count_over_time kullanılır; bu değerler hata yüzdesi değildir.
sum by (host) (rate({job="nginx", log_type="access"} |~ `^\S+ \S+ \S+ \[[^\]]+\] "[^\"]*" [0-9]{3} [0-9]+ "[^\"]*" "[^\"]*"$` | pattern `<remote_addr> <_> <_> [<_>] "<request>" <status> <_> "<_>" "<_>"` | status=~"5[0-9][0-9]" [5m]))Hata yüzdesi için aynı kaynaktan ve zaman penceresinden bütün geçerli HTTP yanıtlarını sayan bir payda gerekir; sıfır trafik ve eksik veri ayrıca ele alınmalıdır. Yalnız 5xx satırlarını saymak yüzde üretmez. JSON uygulama sorgularında | json sonrasında __error__=”” ile parse hatalarını ayırın ve bu hatalar için ayrı bir denetim görünümü oluşturun. LogQL sorgulama belgeleri metin filtrelerini, parser’ları ve metrik fonksiyonlarını açıklar.
Grafana Yönetimli Alarm Kuralları
Grafana’nın Alerts & IRM → Alert rules akışında Loki’den sayısal sonuç veren sorguyla Grafana yönetimli kural oluşturun; sadece log satırı döndüren sorgu eşik için yeterli değildir. Değerlendirme aralığı, pending süresi, No Data ve Error davranışını açıkça seçin. Contact point ile yetkili hedefi tanımlayın. Bildirimlerde Advanced seçildiğinde notification policy etiket eşleşmesini kontrol edin; Default seçimi doğrudan contact point’e yönlendirir ve politika ağacını kullanmaz. Bu kurulumda Loki ruler kuralı veya harici Alertmanager bildirim yolu tanımlanmamıştır; konteyner içindeki localhost:9093 adresine bir alarm hedefi varsayılmaz.
Örneğin webshop için son on dakikadaki JSON error satırlarının toplamını izlemek üzere aşağıdaki sorguyu kullanın. level burada sorgu sırasında JSON’dan çıkarılır; kurulumda indeks etiketi değildir. Aynı olay birden fazla hata satırı üretirse toplam artar, bu nedenle eşiği istek sayısı veya müşteri etkisiyle karıştırmayın.
sum by (service) (count_over_time({job="app", service="webshop"} | json | __error__="" | level="error" [10m]))100 üzeri toplam ve birkaç değerlendirme boyunca devam etme koşulu ancak örnek bir başlangıçtır; gerçek trafik ve kabul edilen hata bütçesine göre belirlenir. Farklı bir kuralda rate kullanırsanız eşik birimi saniyedeki satır olur. Az sayıda kuralla başlayın; bildirimde servis, gözlenen değer, sorgu/pano bağlantısı ve ilk inceleme adımı bulunsun. Test bildiriminin ardından kontrollü bir logla gerçek sorgu → kural → politika → hedef zincirini de sınayın. Grafana’nın kapalı olması halinde bildirim verecek bağımsız izleme gerekir.
Saklama, Rotasyon ve Geri Yükleme
Yedi gün yalnız bu teknik örneğin hedefidir; işletme, gizlilik ve uygulanabilir kayıt yükümlülüklerine göre karar verin. Loki saklaması, kaynak dosyaların rotasyonu ve ayrı yedeklerin saklaması üç farklı politikadır. Kaynak dosyayı kolektör okumadan silmek kayıp doğurur; yedekte süresiz tutmak da birincil depodaki silme süresini geçersiz kılabilir. S3 uyumlu uzak yedekleme pratiğini değerlendirirken Loki indeks/chunk/WAL durumu, Compactor marker’ları, Alloy pozisyonları, Grafana DB/provisioning ve gerekli sırları tutarlı kurtarma kapsamına alın. Çalışan dizinleri rastgele kopyalamanın tutarlı yedek olduğunu varsaymayın; ayrı ortamda geri yüklemeyi doğrulayın.
Dosya yeniden adlandırma, yeni dosya oluşturma, sıkıştırma ve copytruncate davranışını gerçek uygulamayla test edin. Alloy’un –storage.path dizini kalıcıdır; pozisyon, okunmuş baytı gösterir ve Loki’de kalıcı teslimin tek başına kanıtı değildir. Kaynağın target listesinden çıkıp yeniden eklenmesi baştan okumaya yol açabilir. Kesinti ve tekrar başlatma testinde benzersiz sentetik olaylarla boşluk ve çift kayıt arayın. Örneğe dahil journal ve çok satırlı worker kaynakları için de izin, zaman ve sınırlama testlerini uygulayın.
Grafana İçin HTTPS ve Erişim Koruması
Yerel portu internete açmak yerine host üzerinde yetkili bir Nginx proxy kullanılabilir. Aşağıdaki örnek gerçek logs.example.com DNS’i, o adı kapsayan sertifika ve ayrı yetkili kullanıcılardan oluşan htpasswd dosyası gerektirir. Grafana tarafında gerçek HTTPS root_url/domain ve secure cookie ayarlarını yapın; backend yine 127.0.0.1:3000 kalsın. map ve server blokları Nginx http bağlamına yerleştirilir. Var olan üretim proxy ayarlarına doğrulamadan eklemeyin.
# Both map and server belong inside nginx's http context.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl;
server_name logs.example.com;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
auth_basic "Logs";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Authorization "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:3000;
}
}
Upgrade/Connection aktarımı Grafana Live WebSocket yolu için de gereklidir. Proxy Basic Auth kimlik bilgisi Grafana’ya Authorization header olarak iletilmez; Grafana’nın kendi oturumu ve erişim yetkileri ayrıca korunur. Parolaları ortak ekip girişi haline getirmeyin; kişiye özel hesap, erişim iptali ve denetim kaydı kullanın. Sertifika, proxy yapılandırması, oturum açma ve WebSocket davranışını test edin. Uzak Alloy ajanları için Loki’ye ayrı, şifreli ve kimliği doğrulanmış bir erişim yolu gerekir; IP izin listesi tek başına şifreleme değildir.
Etiket Kardinalitesi ve Sorgu Maliyeti
job, service, env, host, log_type ve journal unit bu örneğin sınırlı değerli etiketleridir. Her isteğin ID’sini, raw path’i veya kullanıcı adını etiket yapmak akış sayısını büyütür; tek başına daha hızlı sorgu sağlamaz. Gerekli tanımlayıcılar, veri minimizasyonu ve erişim kuralları izin verdiğinde log alanında kalabilir; token ve parolalar logda hiçbir yerde tutulmamalıdır. Query-time JSON/pattern çıkarımı alanları aramayı sağlar, fakat geniş aralıkta yüksek sayıda gruplama yine maliyetli olabilir.
Önce dar zaman aralığı ve bilinen host/service seçimiyle başlayın, sonra araştırmayı genişletin. Gecikme artışında sadece etiketleri değil disk I/O, sorgu eşzamanlılığı, ingest reddi ve bellek kullanımını da inceleyin. Debug azaltma veya örnekleme ancak gerekli olaylar belirlendikten sonra yapılmalıdır; rastgele WARN’a geçmek hata öncesi bağlamı silebilir. Örneklenen loglardan hesaplanan oran/adetlerin bütün trafiği temsil ettiği varsayılmamalıdır.
Uygulama ve systemd Kaynaklarını Genişletme
Gecikme sorgusu için Nginx’in request_time veya ilgili upstream sürelerini gerçekten loglaması gerekir; standart combined biçimi bu alanları sağlamaz. Zaman birimini doğrulayıp sayısal unwrap kullanın; metin araması bir gecikme dağılımı hesaplamaz. Node.js uygulamasında tek satır JSON ve açık time/level/message sözleşmesi toplama ve hata teşhisini kolaylaştırır. Node.js için PM2/systemd ve Nginx notları süreç yönetimi bağlamını tamamlar; dosya kaynakları uygulamanın stdout çıktısını otomatik keşfetmez; journal bileşeni yalnız seçilen host journal kaynağını okur.
Journal: loki.source.journal.system içindeki max_age=”12h”, başlangıçta okunabilecek en eski göreli zamanı belirler; journal saklama politikası değildir ve mevcut kaynakta bulunmayan kayıtları geri getirmez. Kalıcı Alloy storage.path journal cursor durumunu tutar. __journal__systemd_unit alanı unit etiketine taşınır; bu alanı olmayan kayıtlarda unit bulunmayabilir. Açık job=”systemd” etiketi korunur. Çok sayıda dinamik unit adı kardinaliteyi artırabilir; gerekiyorsa matches ile gerçekten izlenecek servisleri seçin. Journal’ın kendi olay zamanı kullanılır; eksik zaman durumunda okuma zamanı devreye girebilir.
Worker: firstline tarih önekiyle yeni olayı başlatır; eşleşmeyen satırlar önceki olayın devamıdır. max_wait_time=”3s” bekleme, max_lines=128 ise blok boyu sınırıdır; daha uzun izler bölünür. İlk satırdaki RFC3339 zaman ayrı çıkarılır, parse edilemezse kaynak zamanı korunur. Önceki olaydan bağımsız başlayabilecek stack satırlarını ve timeout/bölünme davranışını gerçek biçiminizle test edin. Journal cursor ve dosya offset’i Loki’de kalıcı teslimin tek başına kanıtı değildir.
Docker Yerine Ayrı Native Systemd Kurulumu
Bu alternatif yeni Linux amd64 kurulum içindir; aynı servisleri Compose ile eşzamanlı başlatmayın. Yukarıdaki loki.yml ve config.alloy dosyalarını çalışma dizininde hazırlayın; Compose adımını atlayın. Root Bash, curl, unzip, sha256sum ve systemd gerekir. adm/systemd-journal gruplarının varlığını ve Alloy’un gerçek Nginx/uygulama loglarını okuma iznini doğrulayın; gerekirse unit içindeki SupplementaryGroups’a yalnız gereken gerçek log okuyucu grubunu ekleyin. Aşağıdaki komutlar resmi arşiv digestlerini kontrol eder, root sahipliğinde ikili dosyalar ve ayrı servis hesapları kurar. Loki HTTP adresini loopback yapar; Alloy kaynaklarını gerçek host yollarına, Loki URL’sini 127.0.0.1’e çevirir. Journal path boş bırakıldığı için yerel /var/log/journal ve /run/log/journal aranır.
# Root Bash; new native installation. Keep the prepared loki.yml and config.alloy in the current directory.
set -eu
source_dir=$PWD
workdir=$(mktemp -d /tmp/loki-alloy.XXXXXX)
cd "$workdir"
curl --fail --location --proto =https --tlsv1.2 -o loki.zip https://github.com/grafana/loki/releases/download/v3.7.9/loki-linux-amd64.zip
printf '%s %s\n' '17a5fae28ce733b3560c27c76620920b169bd2aea9473947b733b511f92e81dd' 'loki.zip' | sha256sum --check -
unzip loki.zip
install -o root -g root -m 0755 loki-linux-amd64 /usr/local/bin/loki
curl --fail --location --proto =https --tlsv1.2 -o alloy.zip https://github.com/grafana/alloy/releases/download/v1.21.3/alloy-linux-amd64.zip
printf '%s %s\n' '50d23af2d0f98c00beac6ce9a365693e60f87fb76b6cad6fe1d161e80588af6d' 'alloy.zip' | sha256sum --check -
unzip alloy.zip
install -o root -g root -m 0755 alloy-linux-amd64 /usr/local/bin/alloy
cd "$source_dir"
useradd --system --user-group --no-create-home --shell /usr/sbin/nologin loki
useradd --system --user-group --no-create-home --shell /usr/sbin/nologin alloy
usermod -aG adm,systemd-journal alloy
install -d -o root -g loki -m 0750 /etc/loki
install -d -o root -g alloy -m 0750 /etc/alloy
install -d -o loki -g loki -m 0700 /var/lib/loki
install -d -o alloy -g alloy -m 0700 /var/lib/alloy
sed 's|^ http_listen_address: 0\.0\.0\.0$| http_listen_address: 127.0.0.1|' loki.yml > /etc/loki/config.yml
sed -e 's|http://loki:3100|http://127.0.0.1:3100|g' \
-e 's|/logs/nginx|/var/log/nginx|g' \
-e 's|/logs/app|/var/www/app/storage/logs|g' \
-e 's|path = "/journal"|path = ""|' config.alloy > /etc/alloy/config.alloy
chown root:loki /etc/loki/config.yml
chown root:alloy /etc/alloy/config.alloy
chmod 0640 /etc/loki/config.yml /etc/alloy/config.alloy
cat > /etc/systemd/system/loki.service <<'EOF'
[Unit]
Description=loki logging service
Wants=network-online.target
After=network-online.target
[Service]
User=loki
Group=loki
ExecStart=/usr/local/bin/loki -config.file=/etc/loki/config.yml
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
UMask=0027
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/alloy.service <<'EOF'
[Unit]
Description=alloy logging service
Wants=network-online.target
After=network-online.target
[Service]
User=alloy
Group=alloy
SupplementaryGroups=adm systemd-journal
ExecStart=/usr/local/bin/alloy run --server.http.listen-addr=127.0.0.1:12345 --storage.path=/var/lib/alloy /etc/alloy/config.alloy
Restart=on-failure
RestartSec=5s
LimitNOFILE=65536
UMask=0027
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
EOF
chown root:root /etc/systemd/system/loki.service /etc/systemd/system/alloy.service
chmod 0644 /etc/systemd/system/loki.service /etc/systemd/system/alloy.service
runuser -u loki -- /usr/local/bin/loki -config.file=/etc/loki/config.yml -verify-config=true
runuser -u alloy -- /usr/local/bin/alloy validate /etc/alloy/config.alloy
systemd-analyze verify /etc/systemd/system/loki.service /etc/systemd/system/alloy.service
systemctl daemon-reload
systemctl enable --now loki alloy
systemctl --no-pager --full status loki alloy
Native Grafana’yı resmi paketle ayrıca kurun ve dinleyicisini 127.0.0.1:3000 ile sınırlayın; veri kaynağı URL’si http://127.0.0.1:3100 olur. İlk erişimde yönetici parolasını değiştirin. journalctl -u loki -u alloy ve yerel http://127.0.0.1:3100/ready üzerinden durumu inceleyin; hazır yanıtı gerçek kaynak okuması değildir. Config değişikliğinde aynı Loki/Alloy kontrollerini tekrar çalıştırıp başarılıysa ilgili servisi yeniden başlatın; unit değişikliğinde önce daemon-reload gerekir. Durdurma için systemctl stop, başlangıçta otomatik çalışma için systemctl enable/disable kullanılır. LimitNOFILE=65536 örnek sınırdır, kapasiteyi garanti etmez. Compose ve native veri dizinlerini birlikte kullanmayın; mevcut verinin geçiş/yedek/geri dönüşü ayrı planlanmalıdır.
Logları Metrik ve Uptime Verisiyle Birlikte İnceleme
CPU/bellek, kuyruk derinliği ve dışarıdan erişilebilirlik log akışından kendiliğinden doğmaz. Bu sinyaller için uygun metrik kaynağı, uygulama ölçümü veya bağımsız probe gerekir. VPS izleme ve alarm kurulumu rehberi Prometheus/Grafana/Uptime Kuma bileşenlerini ele alır. Normalde sessiz bir uygulama için log yokluğu arıza kanıtı değildir; beklenen host/service için düzenli heartbeat veya gerçek ölçüm tanımlayın. Aynı VPS üzerindeki bütün gözlem araçlarının tek arıza alanını paylaştığını hesaba katın.
Sürüm Yükseltme ve Promtail’den Geçiş
Yapılandırma, sürüm ve test sonuçlarını sırlar hariç sürüm kontrolünde tutun. Alloy convert ile geçişte dönüştürme uyarıları, kaynak adları, etiketler, zamanlar ve pozisyonlar karşılaştırılmalıdır; eski ve yeni toplayıcıyı aynı kaynağa eşzamanlı bağlamak çift kayıt üretebilir. VPS için CI/CD yaklaşımı kontrollü dağıtım düzeni kurmaya yardımcı olur; Loki şema/veri değişimi yalnız dosya geri almayla tersine çevrilemeyebilir. Ürünleri zorunlu olarak aynı anda yükseltmeyin; her birinin desteklenen sürümünü, protokol uyumunu ve geri dönüş sınırını doğrulayın.
Boş Panel veya Eksik Log Nasıl İncelenir?
Önce konteynerin gerçekten okuduğu dosya yolu, izin, son okuma offset’i ve Alloy’un kendi hata/teslim sayaçlarını kontrol edin. Bir bileşenin healthy görünmesi uçtan uca teslim kanıtı değildir. Ardından Loki’ye ağ erişimini ve ingest yanıtlarını inceleyin: 429 sınır aşımı, zaman kabulü ve parser hataları farklı sorunlardır. Push endpoint’ine sıradan GET isteği göndermek ingest testi değildir; kontrollü sentetik satırın Loki’de beklenen etiket/zaman/içerikle bulunmasını doğrulayın. Kaynak hatasını maskelemek için endpoint’i rastgele localhost’a çevirmeyin; konteynerde localhost kendisini gösterir.
Explore zaman aralığını, host/service seçimini, dosya formatını, JSON parse hatalarını ve tam satır biçimine uymayan kayıtları ayrı kontrol edin. Loki’de Compactor, indeks, WAL ve disk uyarılarını inceleyin. Loki 3.7.9 WAL disk kullanım korumasının varsayılan eşiği %90’dır; dolu bir dosya sistemi yazıları kısabilir. Eşiği kapatmak yerine gerekli kapasiteyi ve veri saklama planını düzeltin. Ingest limitini düşürmek göndereni otomatik yavaşlatmaz; reddedilen/retry edilen veri ve olası kaybı artırabilir. Bellek/kapasite sorununda ölçülen darboğaza göre kaynak hacmi, sorgu yükü ve altyapıyı birlikte ele alın. Çalışan bir sorgudan sonra alarm değerlendirme ve bildirim zincirini ayrıca test edin.
Doğrulanmış Bir Merkezi Loglama Akışı İşletme
Yeni kurulumda Alloy seçilmiş dosyaları, Loki tek kiracılı özel depoyu, Grafana sorgu ve yönetimli alarm akışını sağlar. Bu örnek sürümlerle Loki ve Alloy yapılandırmalarını ayrı bir test ortamında doğrulayın ve sentetik dosya/sorgu kontrollerini uygulayın. Gerçek sunucu izinleri, Docker çalışması, TLS, bildirim teslimi ve yedi günlük temizleme döngüsü de kendi ortamınızda doğrulanmalıdır. Sabit sürüm ve kalıcı veri kullanmak tek başına işletim kalitesi garantisi değildir.
Devreye alma kontrolünüzde en az bir normal olay, gerçek 5xx durum kodu, JSON hata satırı ve veri yokluğu senaryosu bulunsun. Etiketleri, olay zamanını, alıcıya teslimi ve geri yüklemeyi kaydedin; gürültüyü azaltırken gerekli denetim/olay kayıtlarını koruyun. Node.js canlıya alma notları ile uygulamanın log çıktısını da bu veri sözleşmesine göre düzenleyebilirsiniz. Kurulumdan sonra sürüm ve kapasite gözlemi ile erişim/saklama sorumluluğunu bir kişiye veya ekibe açıkça atayın.
Bir sonraki değişiklikte aynı sentetik kontrolleri yeniden uygulayın: kaynak dosya, kolektör, Loki sorgusu ve gerçek bildirim hedefi tutarlı sonuç üretmelidir. Sorun olduğunda hangi adımda kayıp veya yanlış sınıflama başladığını bu kayıtlarla ayırabilirsiniz. Log arşivinin varlığı, kayıtların eksiksiz olduğunu ya da operasyonun kesintisiz kalacağını tek başına kanıtlamaz.





