Оптимизация кода
В данном разделе рассматриваются методы оптимизации кода VRL, направленные на повышение производительности системы и упрощение работы с кодом.
Оптимизация VRL-кода включает минимизацию избыточных операций, таких как лишнее копирование объектов, неоптимальные вычисления и использование устаревших или неэффективных функций.
Снижение вычислительных затрат
Копирование исходного объекта
Копирование исходного объекта в VRL-коде может привести к избыточным затратам ресурсов, особенно если оно выполняется для каждого события. Для повышения производительности такие операции следует минимизировать.
При написании логики обработки может встречаться лишнее копирование объекта, как в данном примере:
... on_correlate: !vrl | state = . %state.check = false for_each(["управляемые устройства/задача обновления"]) -> |_index, value| { if contains(downcase(to_string!(.msg) ?? ""), value) { %state.check = true %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} было зафиксировано изменение параметров критичной политики пользователем {{ to_string(.duser) ?? '-' }}" } } %state = state ...
Для устранения проблемы рекомендуется минимизировать операции копирования, сразу работая с переменной %state
:
... on_correlate: !vrl | %state = . %state.check = false for_each(["управляемые устройства/задача обновления"]) -> |_index, value| { if contains(downcase(to_string!(.msg) ?? ""), value) { %state.check = true %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} было зафиксировано изменение параметров критичной политики пользователем {{ to_string(.duser) ?? '-' }}" } } ...
Дублирование логики
Другой пример избыточного копирования можно найти в следующем коде, где объект state
дублируется на каждом событии:
... on_correlate: !vrl | state = . count_detections = int(%state.count_detections) ?? 0 count_detections = count_detections + 1 %state.count_detections = count_detections if %state.count_detections == 2 { %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} у пользователя {{ to_string(.duser) ?? '-' }} было обнаружено повторное заражение ВПО" } %state = state ...
Код можно оптимизировать, убрав лишнее копирование объекта state
, что предотвратит ненужные срабатывания логики:
... on_correlate: !vrl | if %state.check != true { %state.count_detections = (int(%state.count_detections) ?? 0) + 1 if %state.count_detections == 2 { %state.check = true %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} у пользователя {{ to_string(.duser) ?? '-' }} было обнаружено повторное заражение ВПО" } } ...
Оптимизация вычислений внутри циклов
При обработке событий в секции on_correlate все вычисления, не зависящие от цикла, следует выносить за его пределы. Это позволяет избежать повторных затрат ресурсов на каждой итерации цикла и повышает производительность. Например, выражение downcase(to_string(.msg) ?? "-")
не изменяется в ходе выполнения цикла, но будет вычисляться на каждой итерации, если его не вынести в отдельную переменную.
... on_correlate: !vrl | # Список критичных объектов для мониторинга critical_objects = ["управляемые устройства/задача обновления"] %state = . %state.check = false for_each(critical_objects) -> |_index, value| { if contains(downcase(to_string(.msg) ?? "-"), value) { %state.check = true %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} было зафиксировано изменение параметров критичной политики пользователем {{ to_string(.duser) ?? '-' }}" } } ...
В данном варианте downcase(to_string(.msg) ?? "-")
выполняется на каждой итерации, что приводит к ненужным вычислительным затратам.
on_correlate: !vrl | # Список критичных объектов для мониторинга critical_objects = ["управляемые устройства/задача обновления"] # Оптимизация: предварительное вычисление значения downcase_msg = downcase(to_string(.msg) ?? "-") %state = . %state.check = false for_each(critical_objects) -> |_index, value| { if contains(downcase_msg, value) { %state.check = true %state.cs8 = .msg %state.cs8Label = "Antivirus Message" %state.msg = "На хосте {{ to_string(.dvc) ?? '-' }} было зафиксировано изменение параметров критичной политики пользователем {{ to_string(.duser) ?? '-' }}" } }
В данном примере выражение downcase(to_string(.msg) ?? "-")
вычисляется один раз до начала цикла и сохраняется в переменной downcase_msg
. Это устраняет необходимость повторного выполнения вычисления на каждой итерации. В результате оптимизации количество вызовов функций сокращается, что снижает общий объем вычислений, особенно при большом количестве элементов в массиве critical_objects
.
Избыточные вычисления при повторяющихся проверках
Когда в логике обработки данных необходимо многократно выполнять одну и ту же проверку, это может привести к избыточным вычислениям и увеличению нагрузки на систему. В VRL отсутствует механизм для прерывания цикла, но избыточные вычисления можно исключить, используя функции, такие как rv_contains_any или rv_ends_with_any, которые выполняют поиск за один проход по строке.
rv_contains_any
... aliases: event: filter: !vrl | filePath = to_string!(.filePath) ?? "" rv_contains_any(filePath, [ "/var/ossec/logs", "/var/run/utmp", "/var/log/messages", "/var/log/audit", "/var/log/secure", "/var/log/auth.log", "/var/log/kern.log", "/var/log/syslog", "/var/log/cron.log" ]) ...
rv_ends_with_any
... aliases: event: filter: !vrl | filePath = to_string!(.filePath) ?? "" rv_ends_with_any(filePath, [ ".bash_history", ".log", ".audit" ]) ...
Для обработки условий на наличие подстрок или совпадений строк можно также использовать функции contains или match-any. Выбор функции зависит от количества условий и требований к производительности.
Функция contains
работает эффективно при небольшом количестве условий, например, 2–3 условия.
contains
... aliases: event: filter: !vrl | filePath = to_string!(.filePath) ?? "" contains(filePath, "/var/log/audit") || ends_with(filePath, ".bash_history") ...
При большом числе условий (5 и более) предпочтительнее использовать match_any
, поскольку это упрощает код и снижает вероятность ошибок при добавлении новых условий.
match_any
... aliases: event: filter: !vrl | filePath = to_string!(.filePath) ?? "" match_any(filePath, [ "/var/ossec/logs", "/var/run/utmp", "/var/log/messages", "/var/log/audit", "/var/log/secure", "/var/log/auth.log", "/var/log/kern.log", "/var/log/syslog", "/var/log/cron.log", ".bash_history" ]) ...
Рекомендации по выбору функций
-
contains
: используйте для небольшого количества условий (2–3 условия), где нужно проверить наличие конкретных подстрок. -
match_any
: предпочтительнее при большом количестве условий (5 и более условий), так как она упрощает код и повышает производительность. -
rv_contains_any
илиrv_ends_with_any
: используйте для проверки наличия хотя бы одной подстроки или суффикса, так как эти функции выполняют поиск за один проход, обеспечивая лучшую производительность.
Учитывайте чувствительность к регистру, если это необходимо, и используйте дополнительный параметр case_sensitive: false .
|
Использование регулярных выражений
Регулярные выражения требуют больше ресурсов и их стоит использовать только там, где другие функции VRL не могут быть использованы.
... on_correlate: !vrl | message = parse_regex_all!(.raw, r'(?P<key_value>(\S*))') ...
... on_correlate: !vrl | parsed_message = parse_key_value(.raw) ?? {} ...
Контроль структуры кода
Приоритеты операторов
При использовании нескольких логических операторов важно явно задавать приоритет операций с помощью скобок, чтобы избежать двусмысленных условий.
Если написать код без скобок:
.dvender == "Microsoft" && .externalId == "4688" || .externalId == "4688"
Этот код будет интерпретирован как два отдельных блока:
-
.dvender == "Microsoft"
И.externalId == "4688"
-
.externalId == "4688"
Если цель заключается в том, чтобы условие .dvender == "Microsoft"
было связано с одним из значений .externalId
, необходимо использовать скобки для группировки операторов.
.dvender == "Microsoft" && (.externalId == "4688" || .externalId == "1234")
Для сложных условий с несколькими процессами также требуется группировка:
.dvender == "Microsoft" && (.dproc == "ipconfig.exe" || .dproc == "nltest.exe" || .dproc == "ping.exe")
Приведение полей к нужному типу
В блоке on_correlate
рекомендуется приводить значения полей к ожидаемому типу данных. Это помогает избежать ошибок при обработке данных и обеспечивает корректное выполнение правил корреляции.
Рекомендуемые значения для различных типов данных:
Тип данных | Значение по умолчанию |
---|---|
|
пустая строка ( |
|
|
|
|
|
|
|
|
|
|
|
пустая строка ( |
to_string(.dproc) ?? "" # Преобразование к строке LCString to_int(dvcpid) ?? 0 # Преобразование к UInt ip_to_ipv6(agent.ip) ?? "::" # Преобразование к IPv6
Шаблонизация строк
При написании RObject-конфигураций рекомендуется использовать строковую шаблонизацию для формирования сообщений. Это позволяет улучшить читаемость кода и избежать ошибок, связанных с объединением строк вручную.
Вместо использования функции join
для формирования строки, можно применить шаблонизированные строки, которые поддерживаются в VRL. В шаблонизированной строке переменные значения заключаются в фигурные скобки {}
, и их значения автоматически подставляются в строку.
join
:... on_correlate: !vrl | %state = . %state.msg = join(["На хосте", (to_string(.dvc) ?? "-"), "пользователем", (to_string(.duser) ?? "-"), "было зафиксировано изменение параметров критичной политики"], separator: " ") ?? "-" ...
В данном примере функция join
используется для объединения строк и переменных. Этот подход требует явного указания разделителей и может приводить к сложностям при добавлении или изменении текста.
... on_correlate: !vrl | %state = . %state.msg = "На хосте {to_string(.dvc) ?? '-'} пользователем {to_string(.duser) ?? '-'} было зафиксировано изменение параметров критичной политики" ...
Использование шаблонизации позволяет подставлять значения переменных прямо в строку, что упрощает структуру кода и снижает вероятность ошибок.
Контроль дублирования строк
Дублирование строк в правилах может затруднить их поддержку и привести к ошибкам при изменении кода. Повторяющиеся строки следует избегать или объединять.
... on_correlate: !vrl | . |= compact({ "name": %state.name, "dhost": %state.dhost, "dntdom": %state.dntdom, # Дублирование "dvc": %state.dvc, "dntdom": %state.dntdom, # Дублирование ... }) ...
... on_correlate: !vrl | . |= compact({ "name": %state.name, "dhost": %state.dhost, "dntdom": %state.dntdom, "dvc": %state.dvc, ... }) ...