Apache HTTP 服务器版本 2.4
为了有效地管理 Web 服务器,需要获取有关服务器活动和性能以及可能发生的任何问题的反馈。Apache HTTP 服务器提供了非常全面且灵活的日志记录功能。本文档介绍如何配置其日志记录功能以及如何理解日志内容。
相关模块 | 相关指令 |
---|---|
Apache HTTP 服务器提供了各种不同的机制来记录服务器上发生的所有事件,从初始请求到 URL 映射过程,再到连接的最终解析,包括过程中可能发生的任何错误。除此之外,第三方模块可能会提供日志记录功能,或将条目注入到现有的日志文件中,而 CGI 程序、PHP 脚本或其他处理程序等应用程序可能会向服务器错误日志发送消息。
本文档讨论了作为 http 服务器标准部分的日志记录模块。
任何可以写入 Apache httpd 正在写入日志文件的目录的人几乎可以肯定地获得服务器启动时使用的 uid 的访问权限,该 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
日志条目中的第一项是消息的日期和时间。接下来是生成消息的模块(在本例中为 core)以及该消息的严重级别。接下来是遇到该条件的进程的进程 ID 以及(如果适用)线程 ID。接下来是发出请求的客户端地址。最后是详细的错误消息,在本例中,它指示请求了一个不存在的文件。
错误日志中可能会出现各种各样的消息。大多数消息看起来与上面的示例类似。错误日志还将包含来自 CGI 脚本的调试输出。任何写入 CGI 脚本的stderr
的信息都将直接复制到错误日志。
在错误日志和访问日志中都放置一个%L
标记将生成一个日志条目 ID,您可以使用该 ID 将错误日志中的条目与访问日志中的条目关联起来。如果加载了mod_unique_id
,它的唯一请求 ID 也将用作日志条目 ID。
在测试期间,持续监控错误日志以查找任何问题通常很有用。在 Unix 系统上,您可以使用以下方法来实现这一点
tail -f error_log
LogLevel
指令允许您为每个模块指定日志严重级别。这样,如果您只是在对一个特定模块的问题进行故障排除,就可以提高其日志记录量,而不会同时获取您不感兴趣的其他模块的详细信息。这对于mod_proxy
或mod_rewrite
等模块特别有用,因为您希望了解有关它尝试执行的操作的详细信息。
通过在LogLevel
指令中指定模块名称来实现这一点
LogLevel info rewrite:trace5
这将主LogLevel
设置为 info,但将其提高到mod_rewrite
的trace5
。
RewriteLog
。相关模块 | 相关指令 |
---|---|
服务器访问日志记录服务器处理的所有请求。访问日志的位置和内容由CustomLog
指令控制。LogFormat
指令可用于简化日志内容的选择。本节介绍如何配置服务器以在访问日志中记录信息。
将信息存储在访问日志中只是日志管理的开始。下一步是分析这些信息以生成有用的统计信息。日志分析通常超出了本文档的范围,并且实际上不是 Web 服务器本身的工作的一部分。
Apache httpd 的各种版本使用过其他模块和指令来控制访问日志记录,包括 mod_log_referer、mod_log_agent 和TransferLog
指令。CustomLog
指令现在包含了所有旧指令的功能。
访问日志的格式高度可配置。格式使用格式字符串指定,该格式字符串看起来很像 C 样式的 printf(1) 格式字符串。下一节中提供了一些示例。有关格式字符串可能包含的完整内容列表,请参阅mod_log_config
格式字符串。
访问日志的典型配置可能如下所示。
LogFormat "%h %l %u %t \"%r\" %>s %b" common CustomLog logs/access_log common
这定义了昵称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
下面描述了此日志条目中的每个部分。
127.0.0.1
(%h
)HostnameLookups
设置为On
,则服务器将尝试确定主机名并将其记录在 IP 地址的位置。但是,不建议使用此配置,因为它会显着降低服务器速度。相反,最好使用日志后处理器(例如logresolve
)来确定主机名。此处报告的 IP 地址不一定是用户所坐机器的地址。如果用户和服务器之间存在代理服务器,则此地址将是代理的地址,而不是源机器的地址。-
(%l
)identd
确定的 RFC 1413 身份。此信息非常不可靠,几乎不应该使用,除非在严格控制的内部网络中。除非IdentityCheck
设置为On
,否则 Apache httpd 甚至不会尝试确定此信息。frank
(%u
)REMOTE_USER
环境变量。如果请求的状态代码(见下文)为 401,则不应信任此值,因为用户尚未通过身份验证。如果文档没有密码保护,则此部分将为“-
”,就像前面的部分一样。[10/Oct/2000:13:55:36 -0700]
(%t
)
[day/month/year:hour:minute:second zone]
day = 2*digit
month = 3*letter
year = 4*digit
hour = 2*digit
minute = 2*digit
second = 2*digit
zone = (`+' | `-') 4*digit
可以通过在日志格式字符串中指定%{format}t
来以其他格式显示时间,其中format
与 C 标准库中的strftime(3)
相同,或者是一个支持的特殊标记。有关详细信息,请参阅mod_log_config
格式字符串。
"GET /apache_pb.gif HTTP/1.0"
(\"%r\"
)GET
。其次,客户端请求的资源是/apache_pb.gif
,第三,客户端使用的协议是HTTP/1.0
。也可以独立地记录请求行的部分内容。例如,格式字符串“%m %U%q %H
”将记录方法、路径、查询字符串和协议,结果与“%r
”完全相同。200
(%>s
)2326
(%b
)-
”。要为无内容记录“0
”,请使用%B
。另一个常用的格式字符串称为组合日志格式。它可以按如下方式使用。
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined CustomLog log/access_log combined
此格式与通用日志格式完全相同,只是增加了两个字段。每个附加字段都使用百分比指令%{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)"
附加字段是
"http://www.example.com/start.html"
(\"%{Referer}i\"
)/apache_pb.gif
的页面)。"Mozilla/4.08 [en] (Win98; I ;Nav)"
(\"%{User-agent}i\"
)只需在配置文件中指定多个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"
此示例还表明,在LogFormat
指令中不需要定义别名。相反,可以在CustomLog
指令中直接指定日志格式。
有时,根据客户端请求的特征从访问日志中排除某些条目会很方便。这可以通过环境变量轻松实现。首先,必须设置一个环境变量来指示请求满足某些条件。这通常通过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
再举一个例子,考虑将来自英语使用者的请求记录到一个日志文件中,而将来自非英语使用者的请求记录到另一个日志文件中。
SetEnvIf Accept-Language "en" english CustomLog logs/english_log common env=english CustomLog logs/non_english_log common env=!english
在缓存场景中,您需要了解缓存的效率。一个非常简单的方法是
SetEnv CACHE_MISS 1 LogFormat "%h %l %u %t "%r " %>s %b %{CACHE_MISS}e" common-cache CustomLog logs/access_log common-cache
mod_cache
将在mod_env
之前运行,如果成功,将直接提供内容。在这种情况下,缓存命中将记录-
,而缓存未命中将记录1
。
除了env=
语法之外,LogFormat
还支持根据 HTTP 响应代码记录值
LogFormat "%400,501{User-agent}i" browserlog LogFormat "%!200,304,302{Referer}i" refererlog
在第一个示例中,如果 HTTP 状态代码为 400 或 501,则将记录User-agent
。在其他情况下,将记录一个文字“-”。同样,在第二个示例中,如果 HTTP 状态代码不为 200、304 或 302,则将记录Referer
。(注意状态代码前的“!”)。
虽然我们刚刚展示了条件日志记录非常强大且灵活,但它并不是控制日志内容的唯一方法。当日志文件包含服务器活动的完整记录时,它们更有用。通常,只需对日志文件进行后期处理以删除您不想考虑的请求,这更容易。
即使在中等繁忙的服务器上,存储在日志文件中的信息量也非常大。访问日志文件通常每 10,000 个请求增长 1 MB 或更多。因此,必须定期通过移动或删除现有日志来轮换日志文件。这不能在服务器运行时完成,因为只要 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
另一种执行日志轮换的方法是使用下一节中讨论的管道日志。
Apache httpd 能够通过管道写入另一个进程的错误和访问日志文件,而不是直接写入文件。此功能极大地提高了日志记录的灵活性,而无需在主服务器中添加代码。为了将日志写入管道,只需将文件名替换为管道字符“|
”,后跟应在其标准输入上接受日志条目的可执行文件名称。服务器将在服务器启动时启动管道日志进程,如果服务器运行时进程崩溃,则会重新启动它。(最后一个功能是我们可以将此技术称为“可靠的管道日志记录”的原因)。
管道日志进程由父 Apache httpd 进程生成,并继承该进程的用户 ID。这意味着管道日志程序通常以 root 身份运行。因此,保持程序简单安全非常重要。
管道日志的一个重要用途是允许日志轮换,而无需重新启动服务器。Apache HTTP 服务器包含一个名为rotatelogs
的简单程序,用于此目的。例如,要每 24 小时轮换一次日志,可以使用
CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
请注意,引号用于括起将为管道调用的整个命令。虽然这些示例适用于访问日志,但相同的技术可用于错误日志。
与条件日志记录一样,管道日志是一个非常强大的工具,但它们不应在可以使用更简单的解决方案(如脱机后期处理)的地方使用。
默认情况下,管道日志进程在不调用 shell 的情况下生成。使用“|$
”而不是“|
”来使用 shell 生成(通常使用/bin/sh -c
)
# Invoke "rotatelogs" using a shell CustomLog "|$/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common
这是 Apache 2.2 的默认行为。根据 shell 的具体情况,这可能会导致在日志管道程序的整个生命周期内出现额外的 shell 进程,以及在重启期间出现信号处理问题。为了与 Apache 2.2 保持兼容性,符号“||
”也受支持,等效于使用“|
”。
请注意,在 Windows 上,在运行许多管道记录器进程时,您可能会遇到问题,尤其是在 HTTPD 作为服务运行时。这是由于耗尽了桌面堆空间。分配给每个服务的桌面堆空间由 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\SubSystems\Windows 注册表值中SharedSection
参数的第三个参数指定。谨慎更改此值;更改 Windows 注册表的正常注意事项适用,但如果调整的值过高,您也可能会耗尽桌面堆池。
在运行具有多个虚拟主机的服务器时,有几种处理日志文件的方法。首先,可以使用与单主机服务器完全相同的日志。只需将日志记录指令放在主服务器上下文中的<VirtualHost>
部分之外,就可以将所有请求记录到同一个访问日志和错误日志中。此技术不允许轻松收集有关各个虚拟主机的统计信息。
如果CustomLog
或ErrorLog
指令放在<VirtualHost>
部分内,则该虚拟主机的所有请求或错误将仅记录到指定的文件中。任何没有日志记录指令的虚拟主机仍然会将其请求发送到主服务器日志。此技术对于少量虚拟主机非常有用,但如果主机数量非常多,则管理起来可能很复杂。此外,它通常会导致文件描述符不足的问题。
对于访问日志,有一个非常好的折衷方案。通过将有关虚拟主机的信息添加到日志格式字符串中,可以将所有主机记录到同一个日志中,然后将日志拆分为各个文件。例如,考虑以下指令。
LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost CustomLog logs/access_log comonvhost
%v
用于记录正在处理请求的虚拟主机的名称。然后,可以使用类似split-logfile的程序对访问日志进行后期处理,以便将其拆分为每个虚拟主机一个文件。
相关模块 | 相关指令 |
---|---|
mod_logio
添加了两个额外的LogFormat
字段(%I 和 %O),它们记录在网络上实际接收和发送的字节数。
mod_log_forensic
提供对客户端请求的取证日志记录。日志记录在处理请求之前和之后完成,因此取证日志包含每个请求的两行日志。取证记录器非常严格,没有自定义选项。它可以成为宝贵的调试和安全工具。
在启动时,Apache httpd 将父 httpd 进程的进程 ID 保存到文件logs/httpd.pid
中。可以使用PidFile
指令更改此文件名。进程 ID 供管理员使用,用于通过向父进程发送信号来重新启动和终止守护程序;在 Windows 上,请改用 -k 命令行选项。有关更多信息,请参见停止和重新启动页面。
为了帮助调试,ScriptLog
指令允许您记录 CGI 脚本的输入和输出。这应该只在测试中使用,而不是在生产服务器上使用。更多信息请参阅 mod_cgi 文档。