fluent-bit.conf
[SERVICE]
flush 1
log_level info
daemon off
storage.path /var/log/flb-storage/
storage.sync normal
storage.checksum off
storage.max_chunks_up 128
storage.backlog.mem_limit 10M
parsers_file parsers.conf
http_server on
http_listen 0.0.0.0
http_port 2020
[INPUT]
Name tail
Path /var/lib/docker/containers/*/*.log
Parser docker
Refresh_Interval 10
Ignore_Older 1h
Docker_Mode On
Tag docker.<file_name>
Tag_Regex (?<file_name>[a-f0-9]*)-json.log
Mem_Buf_Limit 50MB
Skip_Long_Lines On
DB /var/log/flb-storage/flb_db.db
DB.sync normal
Storage.Type filesystem
Read_from_Head false
# Только для отладки
# [INPUT]
# Name tail
# Path /var/lib/docker/containers/*/*.log
# Parser docker
# Refresh_Interval 10
# Docker_Mode On
# Tag docker.<file_name>
# Tag_Regex (?<file_name>[a-f0-9]*)-json.log
# Mem_Buf_Limit 50MB
# Skip_Long_Lines On
# DB /var/log/flb-storage/flb_db.db
# DB.sync normal
# Storage.Type filesystem
# Read_from_Head true
# Фильтруем пустые логи
[FILTER]
name grep
match docker.*
Exclude log ^$
Exclude log ^\s*$
Exclude log ^==>.+<==$
# Извлекаем сырой лог
[FILTER]
name modify
match docker.*
copy log raw_log
copy exception.backtrace exception_backtrace
copy exception.class exception_class
copy exception.message exception_message
# Очищаем docker.* - оставляем ТОЛЬКО нужные поля
[FILTER]
name record_modifier
match docker.*
whitelist_key date
whitelist_key log
whitelist_key raw_log
whitelist_key exception_backtrace
whitelist_key exception_class
whitelist_key exception_message
whitelist_key method
whitelist_key path
whitelist_key action
whitelist_key status
whitelist_key remote_ip
whitelist_key controller
whitelist_key line_id
#
# FILTERS
#
[FILTER]
name parser
match docker.*
key_name raw_log
parser nginx_access
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser gitlab_json
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser gitlab_registry
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser sidekiq_json
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser mysql_error
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser mysql_slow
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser postgresql
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser postgresql_detailed
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser nextcloud_access
reserve_data true
Preserve_Key true
[FILTER]
name parser
match docker.*
key_name raw_log
parser apache_access
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser php_fpm
reserve_data true
[FILTER]
name parser
match docker.*
key_name raw_log
parser grafana_regex
reserve_data true
# Структуризация распарсенных полей В raw_log
[FILTER]
name nest
match docker.*
operation nest
wildcard remote_*
wildcard method
wildcard path
wildcard status
wildcard body_bytes
wildcard user_agent
wildcard referrer
wildcard logger
wildcard endpoint
wildcard pluginId
wildcard dsName
wildcard dsUID
wildcard uname
wildcard level
wildcard msg
wildcard statusCode
wildcard resourcePath
wildcard exception
nest_under parsed_data
#
# FILTERS
#
# Метаданные Docker
[FILTER]
name lua
match docker.*
script /fluent-bit/bin/docker-metadata.lua
call enrich_with_docker_metadata
# Копируем метаданные
[FILTER]
name modify
match docker.*
copy docker.hostname hostname
copy docker.container_started started
copy docker.container_name container_name
copy docker.container_name service_name
copy docker.container_id container_id
copy docker.state state
copy docker.stream stream
copy docker.line_id line_id
copy log _raw
copy parsed_data _parsed
# copy exception_class _class
# copy exception_message _message
# copy exception_backtrace _backtrace
copy docker.label_project project
copy docker.label_service service
copy docker.label_logging logging
copy docker.label_logging_jobname logging_jobname
# Структурируем через nest
[FILTER]
name nest
match docker.*
operation nest
wildcard _*
nest_under log
remove_prefix _
# Добавляем host metadata
[FILTER]
name modify
match docker.*
set node_id ${NODE_ID}
set node_name ${NODE_NAME}
set host_name ${NODE_NAME}
# Перетагиваем только логи с enabled logging
[FILTER]
name rewrite_tag
match docker.*
rule $logging ^enabled$ data.$container_id true
[FILTER]
name record_modifier
match data.*
whitelist_key date
whitelist_key log
whitelist_key exception_class
whitelist_key exception_message
whitelist_key exception_backtrace
whitelist_key method
whitelist_key path
whitelist_key action
whitelist_key status
whitelist_key remote_ip
whitelist_key controller
whitelist_key line_id
whitelist_key node_id
whitelist_key node_name
whitelist_key host_name
whitelist_key hostname
whitelist_key started
whitelist_key container_name
whitelist_key service_name
whitelist_key container_id
whitelist_key stream
whitelist_key project
whitelist_key service
whitelist_key logging_jobname
[OUTPUT]
name loki
match data.*
host loki
port 3100
labels job=$logging_jobname, node_name=$node_name, container_id=$container_id, container_name=$container_name, service_name=$service_name, project=$project, service=$service, level=$stream
label_keys $node_name,$container_id,$container_name,$service_name,$project,$service
line_format json
auto_kubernetes_labels off
# Только для отладки
# [OUTPUT]
# name stdout
# match data.*
# format json
parsers.conf
# Базовый парсер для Docker JSON логов
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
# Nginx access логи
[PARSER]
Name nginx_access
Format regex
Regex ^(?<remote_ip>[^ ]*) - - \[(?<timestamp>[^\]]*)\] "(?<method>\w+) (?<path>[^ ]*) HTTP/[0-9.]+" (?<status>\d+) (?<body_bytes>\d+) "(?<referrer>[^"]*)" "(?<user_agent>[^"]*)"
Time_Key timestamp
Time_Format %d/%b/%Y:%H:%M:%S %z
# Gitlab JSON логи (application logs)
[PARSER]
Name gitlab_json
Format json
# Time_Key time # Используем время из Docker
Time_Keep On
# GitLab Registry логи
[PARSER]
Name gitlab_registry
Format regex
Regex ^\d{4}-\d{2}-\d{2}_\d{2}:\d{2}:\d{2}\.\d+ time="(?<time>[^"]*)" level=(?<level>\w+) msg="(?<msg>[^"]*)"(?<rest>.*)
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
# Sidekiq JSON логи
[PARSER]
Name sidekiq_json
Format json
# Time_Key time # Используем время из Docker
Time_Keep On
# Nextcloud Apache-style логи
[PARSER]
Name nextcloud_access
Format regex
Regex ^(?<remote_ip>[^ ]*) (?<user_ident>[^ ]*) (?<user_id>[^ ]*) \[(?<timestamp>[^\]]*)\] "(?<method>\w+) (?<path>[^ ]*) HTTP/[0-9.]+" (?<status>\d+) (?<body_bytes>\d+) "(?<referrer>[^"]*)" "(?<user_agent>[^"]*)"
Time_Key timestamp
Time_Format %d/%b/%Y:%H:%M:%S %z # ← Apache/Nginx формат!
Time_Keep On
# Apache access логи
[PARSER]
Name apache_access
Format regex
Regex ^(?<remote_ip>[^ ]*) - - \[(?<timestamp>[^\]]*)\] "(?<method>\w+) (?<path>[^ ]*) HTTP/[0-9.]+" (?<status>\d+) (?<body_bytes>\d+) "(?<referrer>[^"]*)" "(?<user_agent>[^"]*)"
Time_Key timestamp
Time_Format %d/%b/%Y:%H:%M:%S %z
# MySQL error логи
[PARSER]
Name mysql_error
Format regex
Regex ^(?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z) (?<level>\w+) (?<message>.*)
Time_Key timestamp
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
# MySQL slow query логи
[PARSER]
Name mysql_slow
Format regex
Regex ^# Time: (?<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z).*# User@Host: (?<user>[^\[]*)\[(?<database>[^\]]*)\] @ (?<host>[\w\.]*)\s*\[(?<ip>[\d\.]*)\].*# Query_time: (?<query_time>[\d\.]*) Lock_time: (?<lock_time>[\d\.]*) Rows_sent: (?<rows_sent>\d*) Rows_examined: (?<rows_examined>\d*).*use (?<used_database>\w*);.*SET timestamp=(?<timestamp_unix>\d*);(?<query>.*)
Time_Key timestamp
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
# PostgreSQL логи
[PARSER]
Name postgresql
Format regex
Regex ^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d+ [A-Z]{3}) \[(?<pid>\d+)\] (?<level>\w+):\s*(?<message>.*)
Time_Key timestamp
Time_Format %Y-%m-%d %H:%M:%S.%L %Z
Time_Keep On
# PostgreSQL extended логи (с деталями запросов)
[PARSER]
Name postgresql_detailed
Format regex
Regex ^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d+ [A-Z]{3}) \[(?<pid>\d+)\] (?<level>\w+):\s*duration: (?<duration>[\d\.]*) ms\s*(?<message>.*)
Time_Key timestamp
Time_Format %Y-%m-%d %H:%M:%S.%L %Z
Time_Keep On
# PHP-FPM логи
[PARSER]
Name php_fpm
Format regex
Regex ^\[(?<timestamp>[^\]]+)\] (?<level>\w+): (?<message>.*)
Time_Key timestamp
Time_Format %d-%b-%Y %H:%M:%S
Time_Keep On
# Стандартный syslog
[PARSER]
Name syslog_rfc3164
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<timestamp>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? (?<message>.*)$
Time_Key timestamp
Time_Format %b %d %H:%M:%S
Time_Keep On
# Grafana лог формата key=value
[PARSER]
Name grafana_logfmt
Format logfmt
Time_Key t
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
[PARSER]
Name grafana_regex
Format regex
Regex logger=(?<logger>[^ ]*) endpoint=(?<endpoint>[^ ]*) pluginId=(?<pluginId>[^ ]*) dsName=(?<dsName>[^ ]*) dsUID=(?<dsUID>[^ ]*) uname=(?<uname>[^ ]*) t=(?<t>[^ ]*) level=(?<level>[^ ]*) msg="(?<msg>[^"]*)" error=(?<error>[^ ]*) statusCode=(?<statusCode>[^ ]*) resourcePath="(?<resourcePath>[^"]*)"
Time_Key t
Time_Format %Y-%m-%dT%H:%M:%S.%LZ
Time_Keep On
Установка
loki_config
auth_enabled: false
server:
http_listen_port: 3100
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
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: 720h
reject_old_samples: true
reject_old_samples_max_age: 720h
allow_structured_metadata: true
max_query_length: 721h
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
replication_factor: 1
final_sleep: 0s
chunk_idle_period: 1h
max_chunk_age: 1h
chunk_target_size: 1048576
chunk_retain_period: 30s
table_manager:
retention_deletes_enabled: true
retention_period: 720h
Развертывание
Проверка работы
# Проверяем конфигурацию
$ docker exec -it <fluentbit_container> /fluent-bit/bin/fluent-bit -c /fluent-bit/etc/fluent-bit.conf --dry-run
# Смотрим логи
$ docker service logs logging_fluent-bit
# Проверяем метрики
$ curl http://localhost:2020/api/v1/metrics | jq
# Тестируем парсеры
$ echo '2025-11-21T02:13:34.366Z {"method":"PUT","path":"/projects","status":500}' | \
$ docker exec -i <fluentbit_container> /fluent-bit/bin/fluent-bit -c /fluent-bit/etc/fluent-bit.conf -i stdin -o stdout
{job="fluent-bit"} |= "gitlab"
{container_name="gitlab"}
{job="fluent-bit"} |~ "(?i)error|exception|fail"
{node_name="node-1"}
# Все логи GitLab
{container_name=~".*gitlab.*"}
# Логи по компонентам
{container_name=~".*gitlab.*"} | json | component="gitaly.UnaryServerInterceptor"
# Ошибки
{container_name=~".*gitlab.*"} | json | level="error"
# Запросы с определенным correlation_id
{container_name=~".*gitlab.*"} | json | correlation_id="01KAJ30DCE4BW6JSAT7KHGZ9PX"
# Логи Sidekiq
{container_name=~".*gitlab.*"} | json | severity="INFO"
# Все логи GitLab с parsed_data
{container_name=~".*gitlab.*"} | json
# Логи с ошибками
{environment="production"} | json | level="error"
# Медленные PostgreSQL запросы
{service_name=~".*postgres.*"} | json | duration > 1000
# Nginx 5xx ошибки
{container_name=~".*nginx.*"} | json | status >= 500
# Sidekiq логи
{container_name=~".*sidekiq.*"} | json | severity="INFO"