如何在CentOS 7上将日志模块添加到Nginx

介绍

服务器管理不仅仅与服务的初始配置有关。它还涉及监督这些服务并确保它们尽可能顺利地运行。管理员最重要的知识来源之一是日志文件,其中包含有关系统事件的信息。

对于Web服务器(如Nginx),日志包含有关通过Web服务器访问资源的每次尝试的有价值信息。每个网站访问者和看到的图像或下载的文件都在日志中精心注册。发生错误时,它们也会保存在日志中。使用结构良好的日志文件要容易得多。

在本指南中,我们将了解如何使用Nginx的日志记录模块。我们将为不同的服务器块设置单独的日志文件,然后自定义日志记录输出。我们还将向访问日志添加有关请求的附加信息(在本教程的示例中,提供请求所需的时间),超出Nginx默认包含的范围。

要完成本教程,你需要具备以下内容:

  • 一台已经设置好可以使用sudo命令的非root账号的CentOS服务器,并且已开启防火墙。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后在购买服务器。
  • 日志分析系统。您可以使用腾讯云容器服务,他提供了比较完整的日志分析系统。腾讯云容器服务基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务。腾讯云容器服务完全兼容原生 kubernetes API ,扩展了腾讯云的 CBS、CLB 等 kubernetes 插件,为容器化的应用提供高效部署、资源调度、服务发现和动态伸缩等一系列完整功能,解决用户开发、测试及运维过程的环境一致性问题,提高了大规模容器集群管理的便捷性,帮助用户降低成本,提高效率。容器服务提供免费使用,涉及的其他云产品另外单独计费。

先决条件

要学习本教程,您需要:

  • 一个安装了CentOS 7的服务器,包括一个可以使用sudo权限的非root用户。
  • 在服务器上安装Nginx。

第1步 - 创建测试文件

在此步骤中,我们将在默认的Nginx网站目录中创建多个测试文件。我们将使用它们来测试我们的日志配置。

当Nginx(或任何其他Web服务器)收到文件的HTTP请求时,它会打开该文件,并通过网络传输其内容将其提供给用户。文件越小,传输速度越快。当文件完全传输时,该请求被认为是完整的,然后才会传输记录。

在本教程的后面,我们将修改日志记录配置,以包含有关每个请求花费多少时间的有用信息。测试修改后的配置并注意不同请求之间差异的最简单方法是创建多个不同大小的测试文件,这些文件将在不同的时间内传输。

我们先使用truncate在默认Nginx目录中创建一个命名为1mb.test的1兆字节文件。

代码语言:javascript
复制
sudo truncate -s 1M /usr/share/nginx/html/1mb.test

同样地,我们再创建两个不同大小的文件,第一个是10兆字节,第二个是100兆字节,相应地命名它们。

代码语言:javascript
复制
sudo truncate -s 10M /usr/share/nginx/html/10mb.test
sudo truncate -s 100M /usr/share/nginx/html/100mb.test

最后但同样重要的是,我们来创建一个空文件:

代码语言:javascript
复制
sudo touch /usr/share/nginx/html/empty.test

我们将在下一步中使用这些文件使用默认配置填充日志文件,然后在教程中稍后演示自定义配置。

第2步 - 了解默认配置

日志模块是核心Nginx模块,这意味着它不需要单独安装即可使用。但是,默认配置是最低限度。在此步骤中,我们将看到默认配置的工作原理。

在全新安装中,Nginx将所有请求记录到两个单独的文件:访问日志和错误日志。位于/var/log/nginx/error.log的错误日志存储有关异常服务器错误或处理请求时的错误的信息。

位于/var/log/nginx/access.log中的访问日志更常用。这是保存所有Nginx请求的信息的地方。在此日志中,您可以看到用户正在访问哪些文件,他们正在使用哪些Web浏览器,他们拥有哪些IP地址,以及Nginx响应每个请求的HTTP状态代码。

让我们看看访问日志文件的示例是什么样的。首先,从Nginx请求我们在步骤1中创建的空文件,这样日志文件就不会为空。

代码语言:javascript
复制
curl -i http://localhost/empty.test

作为响应,您应该看到几个HTTP响应标头:

Nginx响应头:

代码语言:javascript
复制
HTTP/1.1 200 OK
Server: nginx/1.6.3
Date: Fri, 05 Aug 2016 22:05:03 GMT
Content-Type: application/octet-stream
Content-Length: 0
Last-Modified: Fri, 05 Aug 2016 22:04:55 GMT
Connection: keep-alive
ETag: "57a50d87-0"
Accept-Ranges: bytes

通过此响应,您可以了解以下几点:

  • HTTP/1.1 200 OK告诉我们Nginx使用200 OK状态代码回复来告诉我们没有错误。
  • Content-Length: 0 表示返回的文档为零长度。
  • Fri, 05 Aug 2016 22:05:03 GMT,请求已处理完毕。

让我们看看这是否与Nginx在其访问日志中存储的内容相匹配。日志文件只能由管理用户读取,因此必须使用sudo来访问它们。

代码语言:javascript
复制
sudo tail /var/log/nginx/access.log

日志将包含这样的一行,对应于我们之前发布的测试请求。

访问日志条目:

代码语言:javascript
复制
::1 - - [05/Aug/2016:22:05:03 +0000] "GET /empty.test HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"

Nginx使用组合日志格式,这是Web服务器常用于互操作性的访问日志的标准格式。在这种格式中,每条信息都由一个空格分隔; 连字符代表缺少的信息。

从左到右,类别是:

  • 请求资源的用户IP地址。因为您在本地使用了curl,所以地址指向本地主机,::1
  • 远程日志信息。这将始终是一个连字符,因为Nginx不支持此信息。
  • 根据HTTP基本身份验证登录用户用户名。对于所有匿名请求,这将为空。
  • 请求日期。您可以看到这与我们的响应标头中的日期相匹配。
  • 请求路径,它包括请求方法(GET),该路径所请求的文件(/empty.text),以及所使用的协议(HTTP/1.1)。
  • 响应状态代码,如果是200 OK,就意味着成功了。
  • 传输文件的长度,因为文件为空所以这里是0
  • HTTP引用头,它包含其中该请求来源文档的地址。在此示例中,它是空的,但如果这是一个图像文件,则引用者将指向使用该图像的页面。 HTTP引用头(HTTP Referer header)中的"referer"是单词“referrer”的拼写错误,它可以追溯到HTTP的起源并且是HTTP标准的一部分。
  • 用户代理,在这里这是curl
  • 拓展头部X-Forwarded-For ,这里为空,其中包含有关源IP地址的信息,如果原始请求已经通过代理转发。

即使访问日志中的单个日志条目也包含大量有关请求的有价值信息。但是,缺少一个重要的信息。虽然我们已经请求了确切的位置http://localhost/empty.test,但只有/empty.test文件的路径在日志条目中; 有关主机名的信息(此处是localhost)将丢失。

第3步 - 配置单独的访问日志

接下来,我们将覆盖默认日志记录配置(其中Nginx为所有请求存储一个访问日志文件),并使Nginx存储单独的日志文件,用于清理Nginx安装附带的默认服务器块。

为每个服务器块存储单独的日志文件是一种很好的做法,可以有效地将来自不同网站的日志彼此分开。这不仅使日志文件变小,而且重要的是使日志更容易分析以发现错误和可疑活动。

要更改默认的Nginx服务器块配置,请在编辑器vi或您喜欢的文本编辑器打开服务器块Nginx配置文件。

代码语言:javascript
复制
sudo vi /etc/nginx/nginx.conf

找到server配置块,如下所示:

代码语言:javascript
复制
server {
    listen       80 default_server;
    listen       [::]:80 default_server;

添加如下的两行添加到配置中:

代码语言:javascript
复制
    access_log /var/log/nginx/default-access.log;
    error_log /var/log/nginx/default-error.log;

access_log指令设置存储访问日志的文件路径,error_log也对错误日志执行相同操作。我们使用相同的目录作为默认的Nginx logs(/var/log/nginx),但使用不同的文件名。如果您有多个服务器块,最好以一致且有意义的方式命名日志文件,例如使用文件名中的域名。

保存并关闭文件以退出。

注意:请记住,为了为每个服务器块维护单独的日志文件,每次在Nginx配置中创建新的服务器块时,都必须应用上述配置更改。

要启用新配置,请重新启动Nginx。

代码语言:javascript
复制
sudo systemctl restart nginx

要测试新配置,请像以前一样对我们的空测试文件执行相同的请求。

代码语言:javascript
复制
curl -i http://localhost/empty.test

检查与我们之前看到的日志行相同的日志行是否写入我们刚刚配置的单独文件中。

代码语言:javascript
复制
sudo tail /var/log/nginx/default-access.log

在下一步中,我们将自定义此新文件中的日志格式并包含其他信息。

第4步 - 配置自定义日志格式

在这里,我们将设置一个自定义日志记录格式,以使Nginx记录其他信息(处理请求需要多长时间),并配置默认服务器块以使用此新格式。

我们需要在使用之前定义新的日志格式。在Nginx中,每种日志格式都有一个唯一的名称,对于整个服务器而言是全局的。可以将各个服务器块配置为稍后通过引用其名称来使用这些格式。

要定义新的日志记录格式,请在Nginx额外配置目录中创建一个新配置文件timed-log-format.conf

代码语言:javascript
复制
sudo vi /etc/nginx/conf.d/timed-log-format.conf

添加以下内容:

代码语言:javascript
复制
log_format timed '$remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '"$http_referer" "$http_user_agent" "$http_x_forwarded_for" $request_time';

保存并关闭文件以退出。

设置指令log_format定义了新的日志格式。下一个元素是此格式的唯一标识符; 这里我们使用的是定时,但您可以选择任何名称。

接下来是日志格式本身,为了便于阅读,分为三行。Nginx公开了有关以美元符号开头的命名系统变量中所有请求的信息。在将请求详细信息写入访问日志时,这些将被有关请求的实际信息所取代(例如,$request_addr将被替换为访问者的IP地址)。

上面的格式与前面讨论的通用日志格式相同,只有一个区别:在最后添加了系统变量$request_time。Nginx使用此变量来存储请求花费的时间(以毫秒为单位),并且通过在我们的日志格式中使用此变量,我们告诉Nginx将该信息写入日志文件。

现在我们在Nginx配置中定义了一个名为timed的自定义日志格式,但默认服务器块尚未使用此格式。接下来,打开服务器块Nginx配置文件。

代码语言:javascript
复制
sudo vi /etc/nginx/nginx.conf

找到我们之前修改的配置块server,并将timed日志格式名称添加到access_log设置中,如下所示:

代码语言:javascript
复制
. . .
server {
    listen 80 default_server;
    listen [::]:80 default_server;
​
    access_log /var/log/nginx/default-access.log timed;
    error_log /var/log/nginx/default-error.log;
. . .

保存并关闭文件以退出。

要启用新配置,请重新启动Nginx。

代码语言:javascript
复制
sudo systemctl restart nginx

现在一切都已设置好,让我们检查它是否有效。

第5步 - 验证新配置

我们可以通过curl调用对Nginx的一些请求来测试新配置,就像我们在第2步中所做的那样。这次我们将使用在步骤1中创建的示例文件:

代码语言:javascript
复制
curl -i http://localhost/empty.test
curl -i http://localhost/1mb.test
curl -i http://localhost/10mb.test
curl -i http://localhost/100mb.test

您会注意到每个后续命令将花费更长的时间来执行,因为文件变大并且传输它们需要更多时间。

让我们在执行这些请求后显示访问日志。

代码语言:javascript
复制
sudo tail /var/log/nginx/default-access.log

日志现在将包含更多行,但最后四行将对应您刚刚执行的测试请求。

访问日志条目:

代码语言:javascript
复制
::1 - - [05/Aug/2016:22:14:04 +0000] "GET /empty.test HTTP/1.1" 200 0 "-" "curl/7.29.0" "-" 0.000
::1 - - [05/Aug/2016:22:14:04 +0000] "GET /1mb.test HTTP/1.1" 200 1048576 "-" "curl/7.29.0" "-" 0.000
::1 - - [05/Aug/2016:22:14:07 +0000] "GET /10mb.test HTTP/1.1" 200 10485760 "-" "curl/7.29.0" "-" 2.063
::1 - - [05/Aug/2016:22:15:10 +0000] "GET /100mb.test HTTP/1.1" 200 104857600 "-" "curl/7.29.0" "-" 47.318

您将看到每次路径都不同,显示正确的文件名,每次请求大小都会增加。重要的部分是最后突出显示的数字,即我们刚刚以自定义日志格式配置的请求处理时间(以毫秒为单位)。正如您所想的那样,文件越大,传输所需的时间就越长。

如果是这种情况,您已成功在Nginx中配置自定义日志格式!

结论

虽然看到更大的文件需要更长的传输时间并不是特别有用,但是当使用Nginx为动态网站提供服务时,请求处理时间非常有用。它可用于跟踪网站中的瓶颈,并轻松查找花费时间超过应有的请求。

$request_time只是Nginx公开的许多系统变量之一,可以在自定义日志记录配置中使用。其他包括,例如,响应发送到客户端的响应标头的值。将其他变量添加到日志格式就像将它们放入日志格式字符串一样简单,就像我们对$request_time做的一样。它是一个功能强大的工具,您可以在为网站配置日志记录时使用它。

想了解更多日志模块的相关教程,请前往腾讯云+社区学习更多知识。

参考文献:《How To Add the log Module to Nginx on CentOS 7》