Apache HTTP Server提供了各种不同的机制,用于记录服务器上发生的所有事情,从初始请求到URL映射过程,再到最终的连接解决方案,包括流程中可能发生的任何错误。除此之外,第三方模块可以提供日志记录功能,或者将条目注入到现有日志文件中,并且诸如CGI程序或PHP脚本或其他处理程序之类的应用程序可以向服务器错误日志发送消息。
在本文中,我们将讨论作为http服务器标准部分的日志记录模块。
安全警告
任何能够写入Apache httpd正在编写日志文件的目录的用户几乎可以访问服务器启动的uid,通常是root用户。不要在不知道后果的情况下对存储日志的目录进行写访问;。
此外,日志文件可能包含客户端直接提供的信息,而不会转义。因此,恶意客户端可能会在日志文件中插入控制字符,因此在处理原始日志时必须小心。
错误日志
服务器错误日志(其名称和位置由ErrorLog
指令设置)是最重要的日志文件。这是Apache httpd将发送诊断信息并记录它在处理请求时遇到的任何错误的地方。当启动服务器或服务器操作出现问题时,它是第一个查看的地方,因为它通常包含错误的详细信息以及如何修复它。
错误日志通常写入文件(通常是Unix系统上的error_log和Windows和OS/2上的error.log
)。在Unix系统上,也可以让服务器向syslog
发送错误或将它们传递给程序。
错误日志的格式由ErrorLogFormat
指令定义,可以使用该指令自定义记录的值。如果未指定默认格式,则默认为格式。典型的日志消息如下:
[Fri Sep 09 10:42:29.902022 2011] [core:error] [pid 35708:tid 4328636416] [client 72.15.99.187] File does not exist: /usr/local/apache2/htdocs/favicon.ico
Shell
日志条目中的第一项是消息的日期和时间。接下来是生成消息的模块(在本例中为核心)以及该消息的严重性级别。接下来是经历该条件的进程ID以及(如果适用的话)线程ID。接下来,我们有发出请求的客户端地址。最后是详细的错误消息,在这种情况下表示对不存在的文件的请求。
错误日志中可能会出现各种各样的不同消息。大多数看起来类似于上面的例子。错误日志还将包含CGI脚本的调试输出。通过CGI脚本写入stderr
的任何信息都将直接复制到错误日志中。
在错误日志和访问日志中放置%L
标识符将生成一个日志条目ID,您可以使用该ID将错误日志中的条目与访问日志中的条目相关联。如果加载了mod_unique_id
,则其唯一请求ID也将用作日志条目ID。
在测试期间,连续监视错误日志以查找任何问题通常很有用。在Unix系统上,您可以使用以下方法完成此操作 -
$ tail -f error_log
Shell
按模块记录日志
LogLevel
指令用于基于每个模块指定日志严重性级别。通过这种方式,如果您只使用一个特定模块来解决问题,则可以调高其日志记录量,而无需获取不感兴趣的其他模块的详细信息。这对于mod_proxy
或mod_rewrite
等模块特别有用。你想知道它想要做什么的细节。
通过在LogLevel
指令中指定模块的名称来执行此操作:
LogLevel info rewrite:trace5
Shell
这会将主LogLevel
设置为info
,但将其设置为trace5
以获取mod_rewrite
。
访问日志
服务器访问日志记录服务器处理的所有请求。访问日志的位置和内容由CustomLog
指令控制。LogFormat
指令可用于简化日志内容的选择。本节介绍如何配置服务器以在访问日志中记录信息。
当然,将信息存储在访问日志中只是日志管理的开始。下一步是分析此信息以生成有用的统计信息。一般而言,日志分析超出了本文档的范围,并不是Web服务器本身的部分工作。
各种版本的Apache httpd使用其他模块和指令来控制访问日志记录,包括mod_log_referer
,mod_log_agent
和TransferLog
指令。CustomLog
指令现在包含所有旧指令的功能。
访问日志的格式是高度可配置的。使用格式字符串指定格式,该字符串看起来很像C样式的printf(1)格式字符串。
通用日志格式
访问日志的典型配置可能如下所示-
LogFormat "%h %l %u %t "%r" %>s %b" common CustomLog logs/access_log common
Shell
这定义了昵称common
,并将其与特定的日志格式字符串相关联。格式字符串由百分比指令组成,每个指令指示服务器记录特定的信息。文字字符也可以放在格式字符串中,并直接复制到日志输出中。引号字符("
)必须通过在它前面放一个反斜杠来转义,以防止它被解释为格式字符串的结尾。格式字符串也可能包含特殊控制字符\n
表示换行符和\t
为标签。
CustomLog
指令使用定义的昵称设置新的日志文件。访问日志的文件名相对于ServerRoot
,除非它以斜杠开头。
上述配置将以称为通用日志格式(CLF)的格式写入日志条目。这种标准格式可以由许多不同的Web服务器生成,并由许多日志分析程序读取。CLF
中生成的日志文件条目如下所示:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
Shell
组合日志格式
另一种常用的格式字符串称为组合日志格式,它可以如下使用。
LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i"" combined CustomLog log/access_log combined
Shell
此格式与通用日志格式完全相同,另外还添加了两个字段。每个附加字段都使用percent-directive%{header}i
,其中header
可以是任何HTTP请求标头。此格式下的访问日志如下所示:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)"
Shell
多个访问日志
只需在配置文件中指定多个CustomLog
指令即可创建多个访问日志。例如,以下指令将创建三个访问日志。第一个包含基本的CLF信息,第二个和第三个包含引用和浏览器信息。最后两条CustomLog
行显示了如何模仿ReferLog
和AgentLog
指令的效果。
LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common CustomLog logs/referer_log "%{Referer}i -> %U" CustomLog logs/agent_log "%{User-agent}i"
Shell
条件日志
有时,根据客户端请求的特征从访问日志中排除某些条目是方便的。这可以通过环境变量轻松完成。首先,必须设置环境变量以指示请求满足特定条件。通常通过SetEnvIf
完成。然后,CustomLog
指令的env =
子句用于包含或排除设置环境变量的请求。一些例子:
# Mark requests from the loop-back interface SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog # Mark requests for the robots.txt file SetEnvIf Request_URI "^/robots\.txt$" dontlog # Log what remains CustomLog logs/access_log common env=!dontlog
Shell
作为另一个示例,考虑将来自英语用户的请求记录到一个日志文件,将非英语用户记录到不同的日志文件。
SetEnvIf Accept-Language "en" english CustomLog logs/english_log common env=english CustomLog logs/non_english_log common env=!english
Shell
在缓存场景中,如果想知道缓存的效率。一个非常简单的方法是:
SetEnv CACHE_MISS 1 LogFormat "%h %l %u %t "%r " %>s %b %{CACHE_MISS}e" common-cache CustomLog logs/access_log common-cache
Shell
mod_cache
将在mod_env
之前运行,并且在成功时将在没有它的情况下传递内容。在这种情况下,缓存命中将记录 - 而缓存未命中将记录1。
除了env =
语法之外,LogFormat还支持以HTTP响应代码为条件的日志记录值:
LogFormat "%400,501{User-agent}i" browserlog LogFormat "%!200,304,302{Referer}i" refererlog
Shell
在第一个示例中,如果HTTP状态代码为400或501,则将记录用户代理。在其他情况下,将记录文字-
。同样,在第二个示例中,如果HTTP状态代码不是200,204或302,则将记录Referer
。
记录轮换
即使是在中等繁忙的服务器上,日志文件中存储的信息量也非常大。访问日志文件通常每10,000个请求增长1MB或更多。因此,有必要通过移动或删除现有日志来定期轮换日志文件。这在服务器运行时无法完成,因为Apache httpd将继续写入旧的日志文件,只要它保持文件打开即可。相反,必须在移动或删除日志文件后重新启动服务器,以便它将打开新的日志文件。
通过使用正常重新启动,可以指示服务器打开新的日志文件,而不会丢失来自客户端的任何现有或挂起的连接。但是,为了实现此目的,服务器必须在完成旧请求的服务时继续写入旧日志文件。因此,在对日志文件进行任何处理之前,必须在重新启动后等待一段时间。简单地旋转日志并压缩旧日志以节省空间的典型方案是:
mv access_log access_log.old mv error_log error_log.old apachectl graceful sleep 600 gzip access_log.old error_log.old
Shell
管道日志
Apache httpd能够通过管道将错误和访问日志文件写入另一个进程,而不是直接写入文件。此功能可显着提高日志记录的灵活性,而无需向主服务器添加代码。要将日志写入管道,只需使用管道符“|”替换文件名,然后替换应接受其标准输入上的日志条目的可执行文件的名称。服务器启动时服务器将启动管道日志进程,如果在服务器运行时崩溃,它将重新启动它。
管道日志进程由父Apache httpd进程生成,并继承该进程的用户标识。这意味着管道日志程序通常以root身份运行。因此,保持程序简单安全非常重要。
管道日志的一个重要用途是允许日志轮换而无需重新启动服务器。为此,Apache HTTP Server包含一个名为rotatelogs
的简单程序。例如,要每24小时轮换一次日志,您可以使用:
CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
Shell
请注意,引号用于包含将为管道调用的整个命令。虽然这些示例适用于访问日志,但相同的技术可用于错误日志。
与条件日志记录一样,管道日志是一种非常强大的工具,但是如果可以使用更简单的解决方案(如离线后处理),则不应使用它们。
默认情况下,在不调用shell的情况下生成管道日志进程。使用|$
代替|
使用shell生成(通常使用/bin/sh -c
):
# Invoke "rotatelogs" using a shell CustomLog "|$/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
Shell
这是Apache 2.2的默认行为。根据shell的具体情况,这可能会导致日志记录管道程序的生命周期内的额外shell进程以及重新启动期间的信号处理问题。出于与Apache 2.2的兼容性原因,符号||
也支持并等同于使用|
。
虚拟主机日志
运行具有许多虚拟主机的服务器时,有几个选项可用于处理日志文件。首先,可以使用与单主机服务器完全相同的日志。只需将日志记录指令放在主服务器上下文中的<VirtualHost>
部分之外,就可以在同一访问日志和错误日志中记录所有请求。此技术不允许在单个虚拟主机上轻松收集统计信息。
如果将CustomLog
或ErrorLog
指令放在<VirtualHost>
部分中,则该虚拟主机的所有请求或错误将仅记录到指定的文件。任何没有日志记录指令的虚拟主机仍会将其请求发送到主服务器日志。此技术对于少量虚拟主机非常有用,但如果主机数量非常大,则管理起来可能很复杂。此外,它通常会产生文件描述符不足的问题。
对于访问日志,有一个非常好的折衷方案。通过将虚拟主机上的信息添加到日志格式字符串,可以将所有主机记录到同一日志中,然后将日志拆分为单个文件。例如,请考虑以下指令。
LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost CustomLog logs/access_log comonvhost
Shell
%v
用于记录为请求提供服务的虚拟主机的名称。然后,可以使用像split-logfile
这样的程序对访问日志进行后处理,以便将其分成每个虚拟主机的一个文件。
更多建议: