472 lines
16 KiB
Django/Jinja
Executable file
472 lines
16 KiB
Django/Jinja
Executable file
filebeat.inputs:
|
||
|
||
- type: log
|
||
enabled: true
|
||
id: nginx-access
|
||
paths:
|
||
- /var/log/ptaf_nginx/*/*/*access.log
|
||
- /var/log/ptaf_nginx/*/*/*access.log.1
|
||
json:
|
||
keys_under_root: true
|
||
overwrite_keys: true
|
||
add_error_key: true
|
||
fields:
|
||
log_type: access
|
||
fields_under_root: true
|
||
|
||
- type: filestream
|
||
id: nginx-error
|
||
enabled: true
|
||
paths:
|
||
- /var/log/ptaf_nginx/*/*/*error.log
|
||
- /var/log/ptaf_nginx/*/*/*error.log.1
|
||
fields:
|
||
log_type: "error"
|
||
fields_under_root: true
|
||
|
||
- type: filestream
|
||
id: angie-error
|
||
enabled: true
|
||
paths:
|
||
- /var/log/angie/*error.log
|
||
- /var/log/angie/*error.log.1
|
||
|
||
fields:
|
||
log_type: "angie-error"
|
||
fields_under_root: true
|
||
|
||
setup.ilm.enabled: false
|
||
|
||
setup.template.enabled: false
|
||
setup.template.name: "ptaf"
|
||
setup.template.pattern: "ptaf-*"
|
||
|
||
# Отключаем автоматическое создание индексов
|
||
setup.dashboards.enabled: false
|
||
|
||
output.elasticsearch:
|
||
enabled: true
|
||
# Array of hosts to connect to.
|
||
hosts: ["https://10.10.10.23:9200", "https://10.10.10.24:9200"]
|
||
username: "vector"
|
||
password: {{ filebeat_install_filebeat_password }}
|
||
#Вывод в консоль
|
||
#output.console:
|
||
# codec.json:
|
||
# pretty: true
|
||
|
||
#SSL configuration for HTTPS
|
||
ssl:
|
||
verification_mode: none # Отключаем проверку сертификата
|
||
|
||
# Индексы для разных типов логов
|
||
indices:
|
||
- index: "ptaf-access-%{+yyyy.MM.dd}"
|
||
when:
|
||
equals:
|
||
log_type: "access"
|
||
|
||
- index: "ptaf-error-%{+yyyy.MM.dd}"
|
||
when:
|
||
equals:
|
||
log_type: "error"
|
||
|
||
- index: "ptaf-angie-error-%{+yyyy.MM.dd}"
|
||
when:
|
||
equals:
|
||
log_type: "angie-error"
|
||
|
||
seccomp:
|
||
enabled: false
|
||
|
||
processors:
|
||
- script:
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
// Обработка upstream_response_time
|
||
var responseTime = event.Get("upstream_response_time");
|
||
if (responseTime && typeof responseTime === 'string') {
|
||
var values = responseTime.split(",");
|
||
if (values.length > 0) {
|
||
var lastValue = values[values.length - 1].trim();
|
||
var numValue = parseFloat(lastValue);
|
||
if (!isNaN(numValue)) {
|
||
event.Put("upstream_response_time_digital", numValue);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Обработка upstream_status
|
||
var upstreamStatus = event.Get("upstream_status");
|
||
if (upstreamStatus && typeof upstreamStatus === 'string') {
|
||
var statusValues = upstreamStatus.split(",");
|
||
if (statusValues.length > 0) {
|
||
var lastStatus = statusValues[statusValues.length - 1].trim();
|
||
var intStatus = parseInt(lastStatus, 10);
|
||
if (!isNaN(intStatus)) {
|
||
event.Put("upstream_status_digital", intStatus);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Обработка upstream_response_length
|
||
var responseLength = event.Get("upstream_response_length");
|
||
if (responseLength && typeof responseLength === 'string') {
|
||
var lengthValues = responseLength.split(",");
|
||
if (lengthValues.length > 0) {
|
||
var lastLength = lengthValues[lengthValues.length - 1].trim();
|
||
var intLength = parseInt(lastLength, 10);
|
||
if (!isNaN(intLength)) {
|
||
event.Put("upstream_response_length_digital", intLength);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Обработка upstream_bytes_sent (ДОБАВЛЕНО)
|
||
var bytesSent = event.Get("upstream_bytes_sent");
|
||
if (bytesSent && typeof bytesSent === 'string') {
|
||
var bytesValues = bytesSent.split(",");
|
||
if (bytesValues.length > 0) {
|
||
var lastBytes = bytesValues[bytesValues.length - 1].trim();
|
||
var intBytes = parseInt(lastBytes, 10);
|
||
if (!isNaN(intBytes)) {
|
||
event.Put("upstream_bytes_sent_digital", intBytes);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
- drop_fields:
|
||
fields: ["agent","ecs.version","log.offset","input.type","type"]
|
||
ignore_missing: true
|
||
|
||
|
||
- dissect:
|
||
tokenizer: "/var/log/ptaf_nginx/%{TENANT}/%{}"
|
||
field: "log.file.path"
|
||
target_prefix: ""
|
||
|
||
- rename:
|
||
fields:
|
||
- from: "host.name"
|
||
to: "node_name"
|
||
ignore_missing: true
|
||
fail_on_error: false
|
||
|
||
# Процессор для составной переменной agent_name
|
||
|
||
- script:
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
var tenant = event.Get("TENANT");
|
||
var nodeName = event.Get("node_name");
|
||
var logPath = event.Get("log.file.path");
|
||
|
||
if (!tenant || !nodeName || !logPath) return;
|
||
|
||
// Извлекаем AZ часть
|
||
var azMatch = nodeName.match(/(AZ\d+)/);
|
||
var azPart = azMatch ? azMatch[1] : "AZ0";
|
||
|
||
// Извлекаем имя хоста (последняя часть после дефиса)
|
||
var parts = nodeName.split('-');
|
||
var nodePart = parts[parts.length - 1];
|
||
|
||
// Извлекаем agent (ищем "ptaf-agent001")
|
||
var agentName = "agent000";
|
||
var agentMatch = logPath.match(/ptaf-(agent\d+)/);
|
||
if (agentMatch) {
|
||
agentName = agentMatch[1];
|
||
}
|
||
|
||
// Преобразуем nodePart: test01 → t01
|
||
var shortNodePart = "t00";
|
||
if (nodePart) {
|
||
// Ищем первую букву и цифры
|
||
var nodeMatch = nodePart.match(/^([a-zA-Z])[a-zA-Z]*(\d+)$/);
|
||
if (nodeMatch) {
|
||
var letter = nodeMatch[1]; // первая буква
|
||
var digits = nodeMatch[2]; // цифры
|
||
shortNodePart = letter.toLowerCase() + digits;
|
||
} else {
|
||
// Если не нашли буквы, берем только цифры
|
||
var digitsOnly = nodePart.match(/(\d+)/);
|
||
if (digitsOnly) {
|
||
shortNodePart = "t" + digitsOnly[1];
|
||
}
|
||
}
|
||
}
|
||
|
||
// Преобразуем agentName: agent001 → a001
|
||
var shortAgentName = "a000";
|
||
if (agentName) {
|
||
// Ищем первую букву и цифры
|
||
var agentMatch = agentName.match(/^([a-zA-Z])[a-zA-Z]*(\d+)$/);
|
||
if (agentMatch) {
|
||
var letter = agentMatch[1]; // первая буква
|
||
var digits = agentMatch[2]; // цифры
|
||
shortAgentName = letter.toLowerCase() + digits;
|
||
} else {
|
||
// Если не нашли буквы, берем только цифры
|
||
var digitsOnly = agentName.match(/(\d+)/);
|
||
if (digitsOnly) {
|
||
shortAgentName = "a" + digitsOnly[1];
|
||
}
|
||
}
|
||
}
|
||
|
||
// Создаем composite_id в новом формате
|
||
event.Put("agent_name", tenant + "_" + azPart + "_" + shortNodePart + "_" + shortAgentName);
|
||
}
|
||
ignore_failure: true
|
||
|
||
# Процессор для nginx-error: парсинг timestamp из сообщения
|
||
- dissect:
|
||
tokenizer: "%{error_timestamp} [%{log_level}] %{rest}"
|
||
field: "message"
|
||
target_prefix: ""
|
||
ignore_failure: true
|
||
when:
|
||
equals:
|
||
log_type: "error"
|
||
|
||
# Преобразование timestamp для nginx-error
|
||
- timestamp:
|
||
field: "error_timestamp"
|
||
layouts:
|
||
- "2006-01-02 15:04:05.000"
|
||
timezone: "Europe/Moscow"
|
||
target_field: "@timestamp"
|
||
ignore_failure: true
|
||
when:
|
||
equals:
|
||
log_type: "error"
|
||
# Удаление временных полей для nginx-error
|
||
- drop_fields:
|
||
fields: ["error_timestamp", "log_level", "rest"]
|
||
ignore_missing: true
|
||
when:
|
||
equals:
|
||
log_type: "error"
|
||
|
||
- drop_fields:
|
||
fields: ["agent","ecs.version","log.offset","input.type","type"]
|
||
ignore_missing: true
|
||
|
||
|
||
- dissect:
|
||
field: "log.file.path"
|
||
tokenizer: '/var/log/angie/%{extracted_sid}_%{rest}'
|
||
target_prefix: ""
|
||
overwrite_keys: false # Не перезаписывать существующие поля
|
||
ignore_failure: true
|
||
|
||
# 2. Копируем extracted_sid в SID, если SID не существует
|
||
- script:
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
var extractedSid = event.Get("extracted_sid");
|
||
var existingSid = event.Get("SID");
|
||
|
||
// Если SID уже существует, оставляем его
|
||
if (existingSid !== null && existingSid !== undefined && existingSid !== "") {
|
||
// Удаляем временное поле extracted_sid
|
||
event.Delete("extracted_sid");
|
||
return;
|
||
}
|
||
|
||
// Если извлекли SID из пути и SID еще не существует
|
||
if (extractedSid && !existingSid) {
|
||
event.Put("SID", extractedSid);
|
||
// Удаляем временное поле
|
||
event.Delete("extracted_sid");
|
||
}
|
||
}
|
||
ignore_failure: true
|
||
|
||
# 3. Удаляем временное поле rest
|
||
- drop_fields:
|
||
fields: ["rest"]
|
||
ignore_missing: true
|
||
# Процессор для angie-error: парсинг timestamp из сообщения
|
||
|
||
# 1. Первичный разбор error-лога с помощью dissect
|
||
- dissect:
|
||
when:
|
||
and:
|
||
- equals:
|
||
log_type: "angie-error"
|
||
- contains:
|
||
message: "client:"
|
||
field: "message"
|
||
tokenizer: '%{error_date} %{error_time} [%{error_level}] %{pid_worker}: *%{connection_id} %{rest}'
|
||
target_prefix: "error"
|
||
overwrite_keys: true
|
||
ignore_failure: true
|
||
# 2. Расширенная обработка оставшейся части в script
|
||
- script:
|
||
when:
|
||
and:
|
||
- equals:
|
||
log_type: "angie-error"
|
||
- has_fields: ['error.rest']
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
var rest = event.Get("error.rest");
|
||
if (!rest) return;
|
||
|
||
// Разделяем текст ошибки и дополнительные поля
|
||
// Ищем первое вхождение ", client:" чтобы отделить текст ошибки
|
||
var clientIndex = rest.indexOf(", client:");
|
||
if (clientIndex === -1) {
|
||
// Если нет client:, значит это простое сообщение
|
||
event.Put("error.message_text", rest);
|
||
return;
|
||
}
|
||
|
||
// Извлекаем текст ошибки (все до ", client:")
|
||
var errorMessage = rest.substring(0, clientIndex);
|
||
event.Put("error.message_text", errorMessage.trim());
|
||
|
||
// Извлекаем ключ-значение пары
|
||
var kvString = rest.substring(clientIndex + 2); // +2 чтобы пропустить ", "
|
||
|
||
// Разбираем пары ключ:значение
|
||
parseKeyValuePairs(kvString, event);
|
||
|
||
// Дополнительно разбираем PID#worker
|
||
var pidWorker = event.Get("error.pid_worker");
|
||
if (pidWorker && pidWorker.includes("#")) {
|
||
var parts = pidWorker.split("#");
|
||
event.Put("error.pid", parts[0]);
|
||
event.Put("error.worker", parts[1]);
|
||
}
|
||
}
|
||
|
||
// Функция для разбора строки с key:value парами
|
||
function parseKeyValuePairs(kvString, event) {
|
||
var regex = /(\w+):\s*(?:"([^"]*)"|([^,]*))/g;
|
||
var match;
|
||
var fields = {};
|
||
|
||
while ((match = regex.exec(kvString)) !== null) {
|
||
var key = match[1];
|
||
var value = match[2] || match[3];
|
||
|
||
// Очищаем значение от лишних пробелов
|
||
if (value) {
|
||
value = value.trim();
|
||
}
|
||
|
||
// Сохраняем поле с соответствующим префиксом
|
||
var fieldName = "error." + key;
|
||
event.Put(fieldName, value);
|
||
}
|
||
}
|
||
ignore_failure: true
|
||
|
||
# 3. Для notice логов (без полей client, server и т.д.)
|
||
- dissect:
|
||
when:
|
||
and:
|
||
- equals:
|
||
log_type: "angie-error"
|
||
- not:
|
||
contains:
|
||
message: "client:"
|
||
field: "message"
|
||
tokenizer: '%{error_date} %{error_time} [%{error_level}] %{pid_worker}: %{error_message}'
|
||
target_prefix: "error"
|
||
overwrite_keys: true
|
||
ignore_failure: true
|
||
|
||
# 4. Создание единого поля datetime
|
||
- script:
|
||
when:
|
||
equals:
|
||
log_type: "angie-error"
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
var date = event.Get("error.error_date");
|
||
var time = event.Get("error.error_time");
|
||
|
||
if (date && time) {
|
||
// Преобразуем дату из 2026/01/29 в 2026-01-29
|
||
var isoDate = date.replace(/\//g, "-");
|
||
// Создаем строку в формате ISO 8601
|
||
var dateTime = isoDate + "T" + time;
|
||
event.Put("error.datetime", dateTime);
|
||
}
|
||
}
|
||
ignore_failure: true
|
||
|
||
# 5. Установка timestamp из лога
|
||
- timestamp:
|
||
when:
|
||
equals:
|
||
log_type: "angie-error"
|
||
field: "error.datetime"
|
||
layouts:
|
||
- "2006-01-02T15:04:05"
|
||
timezone: "Europe/Moscow"
|
||
ignore_failure: true
|
||
|
||
# 6. Дополнительная обработка специальных полей
|
||
- script:
|
||
when:
|
||
and:
|
||
- equals:
|
||
log_type: "angie-error"
|
||
- has_fields: ['error.request']
|
||
lang: javascript
|
||
source: |
|
||
function process(event) {
|
||
// Разбираем поле request на метод, путь и версию
|
||
var request = event.Get("error.request");
|
||
if (request) {
|
||
var parts = request.split(" ");
|
||
if (parts.length >= 3) {
|
||
event.Put("error.http_method", parts[0]);
|
||
event.Put("error.http_path", parts[1]);
|
||
event.Put("error.http_version", parts[2].replace("HTTP/", ""));
|
||
}
|
||
}
|
||
|
||
// Разбираем upstream URL
|
||
var upstream = event.Get("error.upstream");
|
||
if (upstream && upstream.startsWith("http")) {
|
||
try {
|
||
var url = upstream.match(/^(\w+):\/\/([^:/]+)(?::(\d+))?(\/.*)?$/);
|
||
if (url) {
|
||
event.Put("error.upstream_protocol", url[1]);
|
||
event.Put("error.upstream_host", url[2]);
|
||
event.Put("error.upstream_port", url[3] || "80");
|
||
event.Put("error.upstream_path", url[4] || "/");
|
||
}
|
||
} catch(e) {
|
||
// Игнорируем ошибки парсинга
|
||
}
|
||
}
|
||
}
|
||
ignore_failure: true
|
||
|
||
# 7. Удаление промежуточных полей
|
||
- drop_fields:
|
||
when:
|
||
equals:
|
||
log_type: "angie-error"
|
||
fields: [
|
||
"error_date", "error_time", "error.error_date", "error.error_time",
|
||
"error.datetime", "error.rest", "error.pid_worker",
|
||
"error.request", "error.message", "tags"
|
||
]
|
||
ignore_missing: true
|
||
# Общий drop_fields для всех логов
|
||
- drop_fields:
|
||
fields: ["agent","ecs.version","log.offset","input.type","type","log.flags"]
|
||
ignore_missing: true
|
||
...
|