Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some tweaks to logging APIs and add a "mod_log_tee" #279

Open
wants to merge 4 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions include/http_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -911,8 +911,8 @@ typedef struct ap_errorlog_info {
/** apr error status related to the log message, 0 if no error */
apr_status_t status;

/** 1 if logging using provider, 0 otherwise */
int using_provider;
/** 1 if timestamp should be used if no format is configured */
int timestamp;
/** 1 if APLOG_STARTUP was set for the log message, 0 otherwise */
int startup;

Expand All @@ -925,7 +925,11 @@ typedef struct ap_errorlog_info {
#define AP_ERRORLOG_DEFAULT_PROVIDER "file"

/** add APR_EOL_STR to the end of log message */
#define AP_ERRORLOG_PROVIDER_ADD_EOL_STR 1
#define AP_ERRORLOG_PROVIDER_ADD_EOL_STR (0x0001)

/** add timestamps if no log format is configured */
#define AP_ERRORLOG_PROVIDER_ADD_TIMESTAMP (0x0002)


typedef struct ap_errorlog_provider ap_errorlog_provider;

Expand Down
7 changes: 7 additions & 0 deletions include/http_log.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,13 @@ int ap_open_logs(apr_pool_t *pconf, apr_pool_t *plog,
*/
void ap_logs_child_init(apr_pool_t *p, server_rec *s);

/* Opens a log file (or piped log) of given name.
* @param name Log file name
* @param is_main Non-zero if this is the error log for the main server
* @param p Pool from which the file is allocated
*/
AP_DECLARE(apr_file_t *) ap_open_error_log(const char *name, int is_main, apr_pool_t *p);

/*
* The primary logging functions, ap_log_error, ap_log_rerror, ap_log_cerror,
* and ap_log_perror use a printf style format string to build the log message.
Expand Down
1 change: 1 addition & 0 deletions modules/loggers/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ APACHE_MODULE(log_json, logging in jsonn, , , most, [
APACHE_MODULE(log_config, logging configuration. You won't be able to log requests to the server without this module., , , yes)
APACHE_MODULE(log_debug, configurable debug logging, , , most)
APACHE_MODULE(log_forensic, forensic logging)
APACHE_MODULE(log_tee, multi-way error log output)

if test "x$enable_log_forensic" != "xno"; then
# mod_log_forensic needs test_char.h
Expand Down
10 changes: 2 additions & 8 deletions modules/loggers/mod_log_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1703,19 +1703,13 @@ static apr_status_t ap_default_log_writer( request_rec *r,
}
else {
errorlog_provider_data *data = log_writer->log_writer;
ap_errorlog_info info;
ap_errorlog_info info = {0};
info.r = r;
info.s = r->server;
info.c = r->connection;
info.pool = r->pool;
info.file = NULL;
info.line = 0;
info.status = 0;
info.using_provider = 1;
info.startup = 0;
info.format = "";
rv = data->provider->writer(&info, data->handle,
str, len);
rv = data->provider->writer(&info, data->handle, str, len);
}

return rv;
Expand Down
161 changes: 161 additions & 0 deletions modules/loggers/mod_log_tee.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <ap_config.h>
#include <apr_version.h>
#include <apr_pools.h>
#include <apr_strings.h>

#include "http_core.h"
#include "httpd.h"
#include "http_log.h"
#include "http_main.h"
#include "ap_provider.h"

struct tee_handle {
apr_file_t *fd;
ap_errorlog_provider *provider;
void *handle;
struct tee_handle *next;
};

static void *tee_error_log_init(apr_pool_t *p, server_rec *s)
{
struct tee_handle *prev = NULL, *this = NULL;
char *tok;
const char *lognames = s->error_fname;
/* If this is the error log provider for the main vhost then the
* first file-based log should be treated as main (for stderr
* handling). */
int is_main = s == ap_server_conf;

if (lognames == NULL) {
/* doesn't make sense */
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
"tee: cannot be used without an argument");
return NULL;
}

while (*lognames) {
char *arg, *name;

tok = ap_getword_conf(p, &lognames);
arg = strchr(tok, ':');

this = apr_pcalloc(p, sizeof *this);

if (arg) {
name = apr_pstrmemdup(p, tok, arg - tok);
arg++;

this->provider = ap_lookup_provider(AP_ERRORLOG_PROVIDER_GROUP, name,
AP_ERRORLOG_PROVIDER_VERSION);
if (!this->provider) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, 0, s,
"cannot find error log provider %s", name);
return NULL;
}

/* Substitute the configured "filename" (argument) for
* provider initialization. */
s->error_fname = arg;
this->handle = this->provider->init(p, s);

if (!this->handle) {
/* Must already be logged. */
return NULL;
}
}
else {
this->fd = ap_open_error_log(tok, is_main, p);
if (!this->fd) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, 0, s,
"cannot open error log file %s", tok);
return NULL;
}
/* Only the first iteration is treated as "main". */
is_main = 0;
}

this->next = prev;
prev = this;
tok = NULL;
}

if (this == NULL) {
ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_CRIT, 0, s,
"no log files specified to tee for: %s", s->error_fname);
return NULL;
}

return this;
}

static apr_status_t tee_error_log(const ap_errorlog_info *info,
void *handle, const char *errstr,
apr_size_t len)
{
struct tee_handle *mlh;
apr_status_t rv = APR_SUCCESS;

for (mlh = handle; rv == APR_SUCCESS && mlh != NULL; mlh = mlh->next) {
if (mlh->fd) {
apr_size_t wlen = len;
apr_file_write(mlh->fd, errstr, &wlen);
apr_file_flush(mlh->fd);
}
else {
apr_size_t errlen = len;

if ((mlh->provider->flags & AP_ERRORLOG_PROVIDER_ADD_EOL_STR) == 0)
errlen -= strlen(APR_EOL_STR);

mlh->provider->writer(info, mlh->handle, errstr, errlen);
}
}

return APR_SUCCESS;
}

static const char *tee_error_log_parse(cmd_parms *cmd, const char *arg)
{
return NULL;
}

static void log_tee_register_hooks(apr_pool_t *p)
{
static const ap_errorlog_provider log_tee_provider = {
&tee_error_log_init,
&tee_error_log,
&tee_error_log_parse,
AP_ERRORLOG_PROVIDER_ADD_EOL_STR | AP_ERRORLOG_PROVIDER_ADD_TIMESTAMP,
};

ap_register_provider(p, AP_ERRORLOG_PROVIDER_GROUP, "tee",
AP_ERRORLOG_PROVIDER_VERSION, &log_tee_provider);
}

AP_DECLARE_MODULE(log_tee) =
{
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
log_tee_register_hooks,
};
61 changes: 36 additions & 25 deletions server/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -294,17 +294,15 @@ static int log_child(apr_pool_t *p, const char *progname,
return rc;
}

/* Open the error log for the given server_rec. If IS_MAIN is
* non-zero, s is the main server. */
static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
AP_DECLARE(apr_file_t *) ap_open_error_log(const char *name, int is_main, apr_pool_t *p)
{
apr_file_t *rv = NULL;
const char *fname;
int rc;

if (*s->error_fname == '|') {
apr_file_t *dummy = NULL;
if (*name == '|') {
apr_cmdtype_e cmdtype = APR_PROGRAM_ENV;
fname = s->error_fname + 1;
fname = name + 1;

/* In 2.4 favor PROGRAM_ENV, accept "||prog" syntax for compatibility
* and "|$cmd" to override the default.
Expand All @@ -322,38 +320,50 @@ static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
* the new child must use a dummy stderr since the current
* stderr might be a pipe to the old logger. Otherwise, the
* child inherits the parents stderr. */
rc = log_child(p, fname, &dummy, cmdtype, is_main);
rc = log_child(p, fname, &rv, cmdtype, is_main);
if (rc != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, ap_server_conf, APLOGNO(00089)
"Couldn't start ErrorLog process '%s'.",
s->error_fname + 1);
return DONE;
}

s->error_log = dummy;
}
else if (s->errorlog_provider) {
s->errorlog_provider_handle = s->errorlog_provider->init(p, s);
s->error_log = NULL;
if (!s->errorlog_provider_handle) {
/* provider must log something to the console */
return DONE;
name + 1);
return NULL;
}
}
else {
fname = ap_server_root_relative(p, s->error_fname);
fname = ap_server_root_relative(p, name);
if (!fname) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EBADPATH, ap_server_conf, APLOGNO(00090)
"%s: Invalid error log path %s.",
ap_server_argv0, s->error_fname);
return DONE;
ap_server_argv0, name);
return NULL;
}
if ((rc = apr_file_open(&s->error_log, fname,
if ((rc = apr_file_open(&rv, fname,
APR_APPEND | APR_WRITE | APR_CREATE | APR_LARGEFILE,
APR_OS_DEFAULT, p)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, rc, ap_server_conf, APLOGNO(00091)
"%s: could not open error log file %s.",
ap_server_argv0, fname);
return NULL;
}
}

return rv;
}

/* Open the error log for the given server_rec. If IS_MAIN is
* non-zero, s is the main server. */
static int open_error_log(server_rec *s, int is_main, apr_pool_t *p)
{
if (s->errorlog_provider) {
s->errorlog_provider_handle = s->errorlog_provider->init(p, s);
s->error_log = NULL;
if (!s->errorlog_provider_handle) {
/* provider must log something to the console */
return DONE;
}
}
else {
s->error_log = ap_open_error_log(s->error_fname, is_main, p);
if (!s->error_log) {
return DONE;
}
}
Expand Down Expand Up @@ -865,7 +875,7 @@ static int do_errorlog_default(const ap_errorlog_info *info, char *buf,
char scratch[MAX_STRING_LEN];
#endif

if (!info->using_provider && !info->startup) {
if (info->timestamp && !info->startup) {
buf[len++] = '[';
len += log_ctime(info, "u", buf + len, buflen - len);
buf[len++] = ']';
Expand Down Expand Up @@ -1138,7 +1148,8 @@ static void log_error_core(const char *file, int line, int module_index,
info.file = NULL;
info.line = 0;
info.status = 0;
info.using_provider= (logf == NULL);
info.timestamp = (errorlog_provider == NULL
|| (errorlog_provider->flags & AP_ERRORLOG_PROVIDER_ADD_TIMESTAMP));
info.startup = ((level & APLOG_STARTUP) == APLOG_STARTUP);
info.format = fmt;

Expand Down