使用 Nginx 在日志上写入响应请求

Write response requests on logs with Nginx

!更新!

非常感谢@ivan

default.conf:

log_format include_id '$remote_addr - $remote_user [$time_local] $request_id "$request" '
                      '$status $body_bytes_sent "$http_referer" "$http_user_agent"';

server {
    listen 443;
    listen [::]:443;
    ssl_certificate /etc/nginx/conf.d/cert.pem;
    ssl_certificate_key /etc/nginx/conf.d/key.pem;
    location / {
        proxy_pass http://sns-mock:9911;
        proxy_store /var/log/nginx/requests/mohsin.json;
    }
}

docker-compose.yml:

version: '3.7'

services:

  sns-mock:
    image: s12v/sns
    container_name: sns-mock
    ports:
      - "9911:9911"
    networks:
      - default-nw
    volumes:
      - ./config/db.json:/etc/sns/db.json

  sns-mock-proxy:
    image: nginx:latest
    container_name: sns-mock-proxy
    ports:
      - "8443:443"
    networks:
      - default-nw
    volumes:
      - ./conf.d:/etc/nginx/conf.d
      - ./log/nginx:/var/log/nginx
      - ./log/nginx/requests:/var/log/nginx/requests
    depends_on:
      - sns-mock

networks:
  default-nw:
    external: true

Php 测试文件:

<?php

use Aws\Result;
use Aws\Sns\SnsClient;
use Illuminate\Http\JsonResponse;
use PHPUnit\Framework\TestCase;

class FooTest extends TestCase
{
    public function testFoo()
    {
        $snsClient = new SnsClient([
            'endpoint' => 'localhost:8443',
            'region' => 'eu-west-2',
            'version' => 'latest'
        ]);

        $result = $snsClient->publish([
            'Message' => 'foo',
            'TopicArn' => 'arn:aws:sns:eu-west-2:123450000001:test-topic'
        ]);

        dd($result, 'ok');
    }
}

测试结果:

我的项目:

在我的 access.log 文件中,我可以像这里一样捕获来自 phpunit 测试的响应:

172.28.0.1 - - [23/May/2022:06:54:04 +0000] "POST / HTTP/1.1" 200 270 "-" "aws-sdk-php/3.222.17 OS/Linux/5.13.0-41-generic lang/php/8.1.6 GuzzleHttp/7" "-"

============================================= ================================================ ====================================

您问题的一般答案是“否”。然而,某种解决方法是可能的。

nginx 处理的每个请求都会通过 $request_id 变量(16 个随机字节,十六进制)接收一些可用的内部请求 ID。您可以将该 ID 添加到您的访问日志中,定义您自己的自定义日志格式:

log_format include_id '$remote_addr - $remote_user [$time_local] $request_id "$request" '
                      '$status $body_bytes_sent "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log include_id;

接下来,在您的 proxy_pass 指令所在的相同位置添加 proxy_store 一个:

proxy_store /var/log/nginx/requests/$request_id.json;

我在这里使用单独的 /var/log/nginx/requests 目录是为了不让您的 /var/log/nginx 变得一团糟。当然,它应该在启动 docker 容器之前手动创建,并且它应该像 /var/log/nginx 本身一样可写(如果在主机系统上使用,包括 SELinux 上下文这样的东西) .但是出于测试目的,您可以从 proxy_store /var/log/nginx/$request_id.json;.

开始

整个 nginx 配置应该如下所示

log_format include_id '$remote_addr - $remote_user [$time_local] $request_id "$request" '
                      '$status $body_bytes_sent "$http_referer" "$http_user_agent"';

server {
    listen 443;
    listen [::]:443;
    access_log /var/log/nginx/access.log include_id;
    ssl_certificate /etc/nginx/conf.d/cert.pem;
    ssl_certificate_key /etc/nginx/conf.d/key.pem;
    location / {
        proxy_pass http://sns-mock:9911;
        proxy_store /var/log/nginx/requests/$request_id.json;
    }
}

如果您也希望通过纯 HTTP 协议提供请求,您可以对纯 HTTP 服务器块使用相同的配置,而不是您在问题中显示的配置:

server {
    listen 80;
    listen [::]:80;
    access_log /var/log/nginx/access.log include_id;
    location / {
        proxy_pass http://sns-mock:9911;
        proxy_store /var/log/nginx/requests/$request_id.json;
    }
}

或发出 HTTP-to-HTTPS 重定向:

server {
    listen 80;
    listen [::]:80;
    return 308 https://$host$request_uri;
}

现在每个请求的响应正文都可以通过其请求 ID 从访问日志中识别出来,例如

172.18.0.1 - - [20/May/2022:19:54:14 +0000] d6010d713b2dce3cd2713f1ea178e140 "POST / HTTP/1.1" 200 615 "-" "aws-sdk-php/3.222.17 OS/Linux/5.13.0-41-generic lang/php/8.1.6 GuzzleHttp/7" "-"

将在 /var/log/nginx/requests 目录下可用(给定访问日志条目中显示的请求的响应正文将作为 d6010d713b2dce3cd2713f1ea178e140.json 文件提供)。