![apache2 日志记录如何在多个实例中工作](https://linux22.com/image/576531/apache2%20%E6%97%A5%E5%BF%97%E8%AE%B0%E5%BD%95%E5%A6%82%E4%BD%95%E5%9C%A8%E5%A4%9A%E4%B8%AA%E5%AE%9E%E4%BE%8B%E4%B8%AD%E5%B7%A5%E4%BD%9C.png)
apache2 是否支持access_log
和的同步日志error_log
?apache2 中的日志如何用于多个实例?如果多个实例同时写入,是否会达到锁定状态?
答案1
简而言之,Apache 本身不会锁定文件——至少,这会对性能和资源造成影响。最多,这是一个潜在的死锁等待发生。由于正常设置中可能会有数百个线程争夺锁,因此您会看到大量资源匮乏的情况。
更复杂的答案是,你当然可以自己尝试实现这一点。也就是说,你可以使用类似管道日志向他们传递处理锁定的中间脚本。
正确的答案是,为什么你需要把它们放在同一个文件中?如果它们托管相同的数据,那么它们应该是同一个服务器——将其分成两个独立的监督者(即 root 拥有的 httpd)和每个监督者的多个子节点(即 apache 拥有的 httpd)不会给你带来任何好处;如果有的话,启动资源所需的额外资源会降低性能。如果它们不托管相同的内容,那么它们不应该将事物记录到同一个日志中。
如果你确实需要合并内容,有很多东西可以合并两个 Apache 日志文件的内容。
希望这可以帮助!
答案2
Does apache2 support synchronized log for access_log and error_log?
不。正如其他人指出的那样,没有锁定或机制来确保条目同步写入任何两个 Apache 日志文件。事件是不管怎样,都有时间戳(因此,您可以将 error_log 事件与触发该事件的 access_log 事件进行匹配),而且,如果您想这样做,也可以使用工具来合并日志。
How the log in apache2 works for multiple instances?
查看Apache 手册中有关日志记录的部分, 特别虚拟主机子部分。
基本上答案是“无论你如何配置它,它都能在手册中描述的限制内工作”。MrTuttle 给你的建议(为每个站点使用单独的日志,并且为单独的 httpd 实例使用单独的日志!)是非常好的建议 - 我建议遵循它...
Will it get to lock condition if multiple instances write at the same time?
否(见上文),但是你可能遇到一些事件被无序地写入日志文件极端加载。(我曾经见过这种情况,并且该网站使用的是管道日志,因此处理日志的程序可能只是失去了理智……)
答案3
不,您不会获得锁定条件。Apache 的日志不是用独占锁打开的(我不知道在 Windows 上是否如此)。
也就是说,为了您自己的理智,请使用单独的日志。
答案4
答案是,Apache2 显然被编程为支持任意数量的子进程或线程,并且它们都能够在任何时候写入某个日志文件。
为此,Apache2 使用一个名为 的库libapr
。日志实现调用一个函数,该函数apr_file_puts()
对日志消息进行完全处理(即一个字符串)。反过来,该apr_file_puts()
函数调用apr_file_write()
下面介绍的函数。
我们在该函数中看到了什么?
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(thefile->mutex);
}
哦!锁……所以那些说没有锁的人还没有仔细研究。这个锁对于线程间使用这个函数很有用。它不会阻止两个并行运行的进程交错日志数据(如果您将 Apache2 设置为fork()
子进程而不是使用线程)。所以……
APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t *nbytes)
{
apr_status_t rv;
DWORD bwrote;
/* If the file is open for xthread support, allocate and
* initialize the overlapped and io completion event (hEvent).
* Threads should NOT share an apr_file_t or its hEvent.
*/
if ((thefile->flags & APR_FOPEN_XTHREAD) && !thefile->pOverlapped ) {
thefile->pOverlapped = (OVERLAPPED*) apr_pcalloc(thefile->pool,
sizeof(OVERLAPPED));
thefile->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!thefile->pOverlapped->hEvent) {
rv = apr_get_os_error();
return rv;
}
}
if (thefile->buffered) {
char *pos = (char *)buf;
apr_size_t blocksize;
apr_size_t size = *nbytes;
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_lock(thefile->mutex);
}
if (thefile->direction == 0) {
/* Position file pointer for writing at the offset we are logically reading from */
apr_off_t offset = thefile->filePtr - thefile->dataRead + thefile->bufpos;
DWORD offlo = (DWORD)offset;
LONG offhi = (LONG)(offset >> 32);
if (offset != thefile->filePtr)
SetFilePointer(thefile->filehand, offlo, &offhi, FILE_BEGIN);
thefile->bufpos = thefile->dataRead = 0;
thefile->direction = 1;
}
rv = 0;
while (rv == 0 && size > 0) {
if (thefile->bufpos == thefile->bufsize) /* write buffer is full */
rv = apr_file_flush(thefile);
blocksize = size > thefile->bufsize - thefile->bufpos ?
thefile->bufsize - thefile->bufpos : size;
memcpy(thefile->buffer + thefile->bufpos, pos, blocksize);
thefile->bufpos += blocksize;
pos += blocksize;
size -= blocksize;
}
if (thefile->flags & APR_FOPEN_XTHREAD) {
apr_thread_mutex_unlock(thefile->mutex);
}
return rv;
} else {
if (!thefile->pipe) {
apr_off_t offset = 0;
apr_status_t rc;
if (thefile->append) {
/* apr_file_lock will mutex the file across processes.
* The call to apr_thread_mutex_lock is added to avoid
* a race condition between LockFile and WriteFile
* that occasionally leads to deadlocked threads.
*/
apr_thread_mutex_lock(thefile->mutex);
rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
if (rc != APR_SUCCESS) {
apr_thread_mutex_unlock(thefile->mutex);
return rc;
}
rc = apr_file_seek(thefile, APR_END, &offset);
if (rc != APR_SUCCESS) {
apr_thread_mutex_unlock(thefile->mutex);
return rc;
}
}
if (thefile->pOverlapped) {
thefile->pOverlapped->Offset = (DWORD)thefile->filePtr;
thefile->pOverlapped->OffsetHigh = (DWORD)(thefile->filePtr >> 32);
}
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
thefile->pOverlapped);
if (thefile->append) {
apr_file_unlock(thefile);
apr_thread_mutex_unlock(thefile->mutex);
}
}
else {
rv = WriteFile(thefile->filehand, buf, (DWORD)*nbytes, &bwrote,
thefile->pOverlapped);
}
if (rv) {
*nbytes = bwrote;
rv = APR_SUCCESS;
}
else {
(*nbytes) = 0;
rv = apr_get_os_error();
/* XXX: This must be corrected, per the apr_file_read logic!!! */
if (rv == APR_FROM_OS_ERROR(ERROR_IO_PENDING)) {
DWORD timeout_ms;
if (thefile->timeout == 0) {
timeout_ms = 0;
}
else if (thefile->timeout < 0) {
timeout_ms = INFINITE;
}
else {
timeout_ms = (DWORD)(thefile->timeout / 1000);
}
rv = WaitForSingleObject(thefile->pOverlapped->hEvent, timeout_ms);
switch (rv) {
case WAIT_OBJECT_0:
GetOverlappedResult(thefile->filehand, thefile->pOverlapped,
&bwrote, TRUE);
*nbytes = bwrote;
rv = APR_SUCCESS;
break;
case WAIT_TIMEOUT:
rv = (timeout_ms == 0) ? APR_EAGAIN : APR_TIMEUP;
break;
case WAIT_FAILED:
rv = apr_get_os_error();
break;
default:
break;
}
if (rv != APR_SUCCESS) {
if (apr_os_level >= APR_WIN_98)
CancelIo(thefile->filehand);
}
}
}
if (rv == APR_SUCCESS && thefile->pOverlapped && !thefile->pipe) {
thefile->filePtr += *nbytes;
}
}
return rv;
}
如果您仔细观察该apr_file_write()
函数,您会注意到对该apr_file_lock()
函数的调用,该函数显然会(暂时)锁定文件。您会注意到该flock()
函数被调用。
rc = apr_file_lock(thefile, APR_FLOCK_EXCLUSIVE);
该函数将阻止其他使用该flock()
函数的进程读取或写入该文件。
APR_DECLARE(apr_status_t) apr_file_lock(apr_file_t *thefile, int type)
{
int rc;
int ltype;
if ((type & APR_FLOCK_TYPEMASK) == APR_FLOCK_SHARED)
ltype = LOCK_SH;
else
ltype = LOCK_EX;
if ((type & APR_FLOCK_NONBLOCK) != 0)
ltype |= LOCK_NB;
/* keep trying if flock() gets interrupted (by a signal) */
while ((rc = flock(thefile->filedes, ltype)) < 0 && errno == EINTR)
continue;
if (rc == -1)
return errno;
return APR_SUCCESS;
}
但我总是可以查看该文件,所以它一定没有锁吧?!
在 Linux 中确实如此。在 MS-Windows 中可能并非如此。无论如何,该文件始终可访问。 不会flock()
阻止另一个进程读取该文件,除非该进程也尝试锁定。这就是 和 等工具tail
即使less
在 Apache2 仍在写入日志文件时也可以读取它们的原因。
因此您始终都会进入锁定状态。但是,由于以下两个原因,它相当透明:
写入非常短,因为一行日志通常少于 256 个字符。
Apache2 最有可能使用日志线程;
日志被添加到 FIFO 并进行处理之后FIFO 还使用互斥锁(没有一种 FIFO 实现可以在没有锁的情况下工作),但这几乎是透明的。一旦数据进入该 FIFO,您的进程将继续运行。日志记录线程可能需要一些时间才能将日志保存到文件中。
(我们看到该线程实现,它被称为“Overlapped”,因为它与 MS-Windows 下的界面相同。)