Re: map для вывод записей в error.log по условию

Maxim Dounin mdounin на mdounin.ru
Пн Май 30 14:28:59 UTC 2016


Hello!

On Fri, May 27, 2016 at 03:09:37PM -0400, dim1 wrote:

> Проблема: 
> В логе есть множество не нужных 404 ошибок. Например, c перебором всего URL.
> 
> Пример с юзерагентом WhatsApp:
> http://domen.com/category/subcategory/subsubcategory/page
> http://domen.com/category/subcategory/subsubcategory/pag
> http://domen.com/category/subcategory/subsubcategory/pa
> ...
> http://domen.com/c
> 
> Кстати, зачем он так делает?!
> 
> 
> ЗАДАЧА:
> Писать в отдельный error.log только записи с реферером, исключая некоторые
> UA.
> 
> 
> РЕАЛИЗАЦИЯ:
> map "$http_user_agent" $iswa {
>   default        1;
>   ~WhatsApp  0;
> }
> map "$http_referer:$iswa:$status" $log404 {
>   default                       0;
>   ~^http.+:1:4[0-9][0-9]  1;
>   ~^http.+:1:50[0-9]       1;
> }
> ...
> sever {
>   access_log /path/domen.error.log combined if=$log404;
>   ... 
> }
> 
> Записываем в отдельный лог все UA кроме WhatsApp, и только 4хх и 50х ошибки
> (4[0-9][0-9] и 50[0-9]), при НЕ пустом реферере (^http.+).
> 
> Если вместо ^http.+ поставить .+  заметно увеличивается нагрузка.
> ~.+ - заметно грузит nginx (вместо 2% на процесс - получается 7%)
> ~^http.+ - не так сильно грузит процессор.
> 
> 
> ВОПРОСЫ:
> 1. Верна ли реализация, в общем? 

Реализация - в целом да.  Тут, скорее, неверна задача, но это 
отдельный вопрос.  Ну и регулярное выражение - неправильное.

> 2. Какой регуляркой лучше указывать - НЕ пустая строка? Странно, что так
> сильно грузит ~.+. 

В мире регулярный выражений есть такое понятие, как backtracking 
(aka "поиск с возвратом"): когда встречается символ, не 
удовлетворяющий выбранному выражению, сопоставление выражения 
откатывается на предыдущую точку, где было возможно более одного 
варианта совпадения, и поиск возможных совпадений продолжается.

Так, для регулярного выражения "^a.+b" и строки "a0b0" поиск будет 
происходить так:

1. в первой позиции будет найден символ "a"
2. следом под шаблон ".+" попадут символы "0b0"
3. дальше встречен конец строки, в то время как нам нужен символ "b",
   так что случится backtracking и мы вернёмся на этап 
   сопоставления с шаблоном ".+"
4. на этот раз шаблону ".+" сопоставят только символы "0b"
5. следующий символ "0" опять не совпадает с тем, что нужно 
   ("b"), так что снова возврат к шаблону ".+"
6. в этот раз шаблону ".+" сопоставят только символ "0"
7. следующий символ "b", и совпадение найдено

Очевидно, что такой метод поиска совпадений - приводит к множеству 
лишней работы в случае, если совпадения нет.  В особо запущенных 
случаях росто сложности получается экспоненциальный, и это 
называется "catastrophic backtracking", подробнее где-то тут:

http://www.regular-expressions.info/catastrophic.html

В вашем случае выражение "http.+:1:..." приводит к просто 
backtracking'у на всю длину Referer'а, что не так плохо, как могло 
бы быть, но всё равно - плохо.

Кроме того, волшебная строка ":1:404:" в Referer'е просто отключит 
логгирование запроса с таким конфигом.

Проще всего для решения всех этих проблем проверяемую строку 
перевернуть, поставив известные фиксированные переменные в начало:

    map $iswa:$status:$http_referer $log404 {
        default                     0;
        ~^1:4[0-9][0-9]:http.+      1;
        ~^1:50[0-9]:http.+          1;
    }

> 3. Имеет ли значение порядок переменных в map? Возможно простые условия (без
> регулярок) лучше ставить первыми?

См. выше.

> 4. Есть ли возможность указать ! (NOT) для условия в map?

В случае точного сопоставления строк - нет, используйте вместо 
этого значение по умолчанию.

В случае регулярных выражений - man pcresyntax в руки.  Если нужно 
построить отрицание, то обычно это проще всего сделать с помощью 
negative lookahead assertion, "(?!...)".  Ну или опять же 
использовать значение по умолчанию.

-- 
Maxim Dounin
http://nginx.org/



Подробная информация о списке рассылки nginx-ru