Логирование тела запроса в NGINX

Возникла (и уже не первый раз) необходимость залогировать тело json-запроса, приходящего от удалённого клиента. Делать логирование на стороне приложения не хотелось, потому что во-первых, это некрасиво, во-вторых — надо приложение пересобрать и передеплоить — то есть долго. На входе у нас стоит NGINX, и вот его можно гибко настроить и, что важно, изменения применяются мгновенно.

Для начала нужно сконфигурировать сообщение, которое будем логировать. В документации к директиве log_format можно найти много всякого интересного, но там нет нужной нам переменной, хотя она есть. Я добавил вот такую записать с секцию http в nginx.conf:

log_format postdata escape=json '"$request" $status'
                                ' [REQUEST BODY]: $request_body';

Здесь postdata — это название нашего кастомного формата сообщения для логирования, escape=json нужно для того, чтобы json-данные в логах были читаемы. Далее в кавычках само сообщение, как видим, оно может быть многострочным. Переменная $request содержит сам запрос, вида "POST /route/handler HTTP/1.0", $status — HTTP-код ответа, а $request_body — та самая переменная, которая отсутствует в документации, тело запроса.

Теперь нужно добавить использование нашего кастомного формата сообщения в нужном месте в конфигурации NGINX. Для этого используется директива access_log, которую можно переопределять на уровне секций http, server, location, if in location и limit_except. Наиболее удобным выглядит location или if in location, это позволит минимизировать распухание логов.

Но после добавления директивы access_log в нужное место произошло странное — часть запросов логировалась нормально, а для части из них $request_body оказывался пустым. Дело оказалось в том, что NGINX сохраняет в переменную $request_body только запросы ограниченной длины, по умолчанию 8 или 16 кб. Для более тяжёлых запросов не сохраняется ничего. Поэтому итоговая конфигурация получилась такой:

location ~ ^/awesome-location/(\w+)$ {
  access_log /var/log/nginx/access.log postdata;
  client_body_buffer_size 64k;
  # остальные директивы
}

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *