apache2 日志记录如何在多个实例中工作

apache2 日志记录如何在多个实例中工作

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 仍在写入日志文件时也可以读取它们的原因。

因此您始终都会进入锁定状态。但是,由于以下两个原因,它相当透明:

  1. 写入非常短,因为一行日志通常少于 256 个字符。

  2. Apache2 最有可能使用日志线程

    日志被添加到 FIFO 并进行处理之后FIFO 还使用互斥锁(没有一种 FIFO 实现可以在没有锁的情况下工作),但这几乎是透明的。一旦数据进入该 FIFO,您的进程将继续运行。日志记录线程可能需要一些时间才能将日志保存到文件中

    (我们看到该线程实现,它被称为“Overlapped”,因为它与 MS-Windows 下的界面相同。)

相关内容