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 ...