无论运行何种平台,日志记录通常都是大数据处理、统计、审计、客户报告和事务处理的核心要求,也是调试客户端与服务器通信及潜在问题的重要手段。
本博客将探讨日志记录在调试中的作用。日志记录是定位互联网通信中各种常见问题的重要工具。

记录客户端与Web服务器之间的通信,有助于调试可能与浏览器版本、客户端网络及所访问文件相关的问题。不过,日志记录并不局限于此。可记录的内容多种多样,具体取决于所用平台和实际需求。
NGINX日志架构
运行反向代理、负载均衡器或内容分发网络(CDN)时,相当于在客户端与服务器之间的通信流中增加了一个节点。反向代理等处理数据的中间服务器往往会引发意外问题。
作为反向代理,NGINX会终止来自客户端的连接,并创建一个连接到Web服务器(或其他应用服务器)的新连接,从而将客户端与服务器之间的连接转换为两个独立连接。这种连接“拆分”也会产生两个独立的日志记录上下文,而NGINX对二者的日志记录支持略有不同。
- 对于客户端与反向代理之间的流量,NGINX同时提供错误日志和访问日志。访问日志记录处理事件以及非错误操作。使用
error_log指令可启用错误日志,并可设置要记录的错误严重级别。使用access_log指令可启用访问日志。通过关联的log_format指令,可以自定义日志中包含的信息类型和日志条目的格式。日志可写入文件、syslog或同时写入两者。 - 对于反向代理与Web或应用服务器(NGINX将其称为“上游服务器”)之间的流量,NGINX支持错误日志,但不支持访问日志。要查看代理服务器与上游服务器之间的非错误事件,只能将错误日志的严重性级别设置为
debug。此设置的缺点是会记录海量数据,不仅会降低请求处理速度,还会生成非常大的文件,可能很快占满存储空间。(请注意,由于默认未启用调试支持,还必须使用--with-debug参数执行configure命令,重新编译NGINX。)
我们的上游日志解决方案
作为CDN提供商,我们经常遇到上游服务器无法与反向代理服务器和客户端正常通信的情况。错误日志中的debug级消息并不总能提供解决上游服务器问题所需的信息。
解决方案很快就明确了:添加一个函数,仅收集反向代理服务器与上游服务器之间通信的必要信息,并据此生成上游访问日志。

总体思路是,每次向上游服务器发出请求时,都让NGINX调用我们的函数。这样,我们就可以在函数本身中编写与上游日志记录相关的所有逻辑。
标准的ngx_http_upstream_module负责处理上游请求,我们需要它调用该函数。该模块目前不支持此功能,因此我们对其进行了修补,以便在需要时启用回调。
日志记录本身由我们编写的独立模块处理,该模块使用我们在修补后的上游模块中添加的日志回调功能。新模块定义了新的upstream_log指令,用于配置日志记录功能。该指令使用与access_log指令相同的解析器,因此数据既可以写入文件,也可以通过套接字发送到syslog服务器。
NGINX在启动期间读取upstream_log指令时,会从nginx.conf中调用两个函数:
ngx_http_upstream_log_set_log,该函数解析指令,并在内部使用ngx_log_t准备日志结构本身(ngx_log_set_log)ngx_http_upstream_log_init(在配置后步骤中),用于向上游模块注册主日志记录函数
这样,当请求需要代理到上游时,一切都已准备就绪。上游模块会与上游服务器建立连接,而我们的补丁可确保调用日志记录函数,以记录请求的详细信息。
日志格式已硬编码到上游模块中。我们仍可选择添加对使用log_format指令进行配置的支持,但这对我们的用例并非必要。
从上游记录的值
与上游服务器的连接关闭后,会立即调用日志记录函数。该函数的参数是一个指向当前正在处理的请求(即ngx_http_ request_t结构)的指针,因此函数可以访问并记录该结构中的所有数据。upstream字段(指向ngx_http_upstream_t的指针)尤其值得关注,因为它包含上游请求的相关数据。我们尤其关注以下内容:
- 源站响应状态码
- 请求持续时间
- 响应中的HTTP标头或Host标头的值
该模块可以访问完整的请求结构,因此能够灵活记录各种信息。
我们遇到的问题
我们最初在ngx_http_core_module中实现了此功能。对于原型而言,这已经足够,但并不是一个很完善的解决方案,因为它可能会增加未来更新和修改的复杂性。最终,我们按照我们的上游日志解决方案中的说明,将上游日志记录功能拆分为独立模块。
当然,实施过程中也出现了一些问题。最突出的是,我们在某些地方对ngx_str_t字符串处理不当,例如使用了C库函数sprintf,而不是ngx_snprintf。这可能会导致未定义数据写入上游日志,甚至造成工作线程出现段错误。经过大量调试,并使用Valgrind和AddressSanitizer等工具进行测试后,这些问题得到了解决。
CDN77中使用的NGINX功能
CDN77 使用 NGINX 的主要原因是其缓存功能。CDN 服务器是部署在客户端与 Web 服务器(上游服务器)之间的节点,用于转发客户端请求,并向上游服务器请求相应文件。文件缓存后,如果其他用户从同一位置请求同一文件,CDN 服务器会将该文件提供给这些用户。
“本地提供文件”是我们使用的功能之一,可让NGINX在收到请求时从服务器磁盘提供文件。
安全缓存需要一些额外的功能和配置。我们使用SSL(支持0‑RTT的TLS 1.3)或可针对特定IP地址生成的安全令牌,以妥善保护内容。
我们使用的其他功能包括为客户定制的错误页面、NGINX默认实现的OCSP装订,以及用于PHP的FastCGI,以减少所需的PHP进程数量。
结论
上游日志不仅过去和现在都在帮助我们调试各种问题,还为我们深入了解NGINX的核心功能提供了绝佳机会,并简化了我们开展的许多其他项目。
关于CDN77
CDN77致力于在全球范围内提供更优质、更便捷的内容分发服务。凭借30多个数据中心,我们能够在全球高效缓存和分发内容,包括网站静态内容、软件分发、视频点播(VoD),以及通过专用流媒体引擎,使用HLS或MPEG‑DASH等多种协议进行直播。
CDN77入门简单、快捷且直观。注册免费试用,创建CDN资源,然后使用生成的CDN URL或自定义的CNAME记录,将其与网站或流媒体解决方案集成。丰富的功能、设置和可选的定制解决方案可确保CDN77满足您的需求。
关于作者
相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
