throttling, abuse-protection & logging/monitoring pass

This commit is contained in:
2026-08-23 23:44:00 +03:30
parent cb18c93a3e
commit 80298b47b1
33 changed files with 4202 additions and 23 deletions

56
docker/alloy/config.alloy Normal file
View File

@@ -0,0 +1,56 @@
// پیکربندیِ حداقلیِ Grafana Alloy — جایگزینِ Promtail (که EOL شده،
// مارچ ۲۰۲۶). دقیقاً همون کاری که promtail-config.yml قبلی می‌کرد:
// فایلِ لاگِ JSON اپِ Django رو tail می‌کنه، level/logger رو به‌عنوانِ
// لیبل استخراج می‌کنه، و به Loki push می‌کنه. هیچ لاگِ جدیدی تولید
// نمی‌کنه و پیامِ لاگ‌ها دست‌نخورده می‌مونه.
// مرحله‌ی ۱: پیدا کردنِ فایل‌های لاگ روی ولومِ مشترک (django_logs)
local.file_match "django_logs" {
path_targets = [{
__path__ = "/var/log/django/*.log",
job = "django",
app = "tadrisino",
}]
}
// مرحله‌ی ۲: خوندنِ همون فایل‌ها
loki.source.file "django_logs" {
targets = local.file_match.django_logs.targets
forward_to = [loki.process.django_logs.receiver]
}
// مرحله‌ی ۳: پردازش — استخراجِ level/logger/timestamp از JSONِ هر خط
// (فرمتِ config.log_formatters.JSONLogFormatter) و ست‌کردنِ اونا به‌عنوانِ
// لیبل، دقیقاً هم‌ارزِ stage.json + stage.labels + stage.timestamp در
// promtail-config.yml قبلی.
loki.process "django_logs" {
stage.json {
expressions = {
level = "",
logger = "",
timestamp = "",
}
}
stage.labels {
values = {
level = "",
logger = "",
}
}
// استفاده از timestampِ خودِ لاگ (نه زمانِ رسیدنِ خط به Alloy)
stage.timestamp {
source = "timestamp"
format = "RFC3339"
}
forward_to = [loki.write.local.receiver]
}
// مرحله‌ی ۴: ارسال به Loki
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}

View File

@@ -0,0 +1,63 @@
{
"title": "Tadrisino Django Logs",
"uid": "tadrisino-django-logs",
"timezone": "browser",
"schemaVersion": 39,
"version": 1,
"time": { "from": "now-6h", "to": "now" },
"refresh": "10s",
"templating": {
"list": [
{
"name": "level",
"type": "query",
"datasource": { "type": "loki", "uid": "Loki" },
"query": "label_values({job=\"django\"}, level)",
"refresh": 2,
"includeAll": true,
"multi": true,
"current": { "text": "All", "value": "$__all" }
},
{
"name": "search",
"type": "textbox",
"label": "Search text",
"current": { "text": "", "value": "" }
}
]
},
"panels": [
{
"id": 1,
"title": "Log volume by level",
"type": "timeseries",
"datasource": { "type": "loki", "uid": "Loki" },
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 0 },
"targets": [
{
"expr": "sum by (level) (count_over_time({job=\"django\", level=~\"$level\"} |= `$search` [1m]))",
"refId": "A"
}
]
},
{
"id": 2,
"title": "Django application logs",
"type": "logs",
"datasource": { "type": "loki", "uid": "Loki" },
"gridPos": { "h": 16, "w": 24, "x": 0, "y": 8 },
"options": {
"showTime": true,
"showLabels": true,
"wrapLogMessage": true,
"sortOrder": "Descending"
},
"targets": [
{
"expr": "{job=\"django\", level=~\"$level\"} |= `$search`",
"refId": "A"
}
]
}
]
}

View File

@@ -0,0 +1,13 @@
# به Grafana می‌گه داشبوردهای JSON رو از /var/lib/grafana/dashboards
# (که در docker-compose.yml به docker/grafana/dashboards mount شده) بخونه.
apiVersion: 1
providers:
- name: Django Logs
orgId: 1
folder: ""
type: file
disableDeletion: false
updateIntervalSeconds: 30
options:
path: /var/lib/grafana/dashboards

View File

@@ -0,0 +1,15 @@
# Loki رو به‌صورتِ خودکار به‌عنوانِ دیتاسورسِ پیش‌فرضِ Grafana اضافه می‌کنه
# (نیازی به تنظیمِ دستی در UI نیست).
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
# UID رو صریح ست می‌کنیم چون داشبورد (django-logs.json) با همین UID
# (uid: "Loki") به این دیتاسورس ارجاع می‌ده؛ بدونِ این، Grafana خودش
# یک UID تصادفی تولید می‌کرد و ارجاعِ داشبورد resolve نمی‌شد.
uid: Loki
isDefault: true
editable: true

View File

@@ -0,0 +1,35 @@
# پیکربندیِ حداقلیِ Loki برای dev/local — تک‌نود، بدونِ auth، ذخیره‌سازیِ
# لاگ روی فایل‌سیستمِ محلیِ خودِ کانتینر (ولومِ loki_data). برای بارِ
# پروداکشنِ واقعی این تنظیمات کافی نیست (replication، retention، object
# storage و ...)، ولی برای دیدنِ لاگ‌های محلی در Grafana کاملاً کافیه.
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
# جلوگیری از رد شدنِ silent لاگ‌های خیلی قدیمی/خیلی جدید در محیطِ dev
reject_old_samples: true
reject_old_samples_max_age: 168h