From: Jan Kaluža
Using syslog instead of a filename enables logging
- via syslogd(8) if the system supports it. The default is to use
- syslog facility local7, but you can override this by
- using the syslog:facility syntax where
- facility can be one of the names usually documented in
+ via syslogd(8) if the system supports it and if local7,
+ but you can override this by using the syslog:facility
+ syntax where facility can be one of the names usually documented in
syslog(1). The facility is effectively global, and if it is changed
in individual virtual hosts, the final facility specified affects the
entire server.
Additional modules can provide their own ErrorLog providers. The syntax
+ is similar to syslog example above.
SECURITY: See the security tips
document for details on why your security could be compromised
diff --git a/include/http_core.h b/include/http_core.h
index 80ce6cdd961..a31aa054a17 100644
--- a/include/http_core.h
+++ b/include/http_core.h
@@ -833,8 +833,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 to syslog, 0 otherwise */
- int using_syslog;
+ /** 1 if logging using provider, 0 otherwise */
+ int using_provider;
/** 1 if APLOG_STARTUP was set for the log message, 0 otherwise */
int startup;
@@ -842,6 +842,30 @@ typedef struct ap_errorlog_info {
const char *format;
} ap_errorlog_info;
+#define AP_ERRORLOG_PROVIDER_GROUP "error_log_writer"
+#define AP_ERRORLOG_PROVIDER_VERSION "0"
+#define AP_ERRORLOG_DEFAULT_PROVIDER "file"
+
+typedef struct ap_errorlog_provider ap_errorlog_provider;
+
+struct ap_errorlog_provider {
+ /** Initializes the error log writer.
+ * @param p The pool to create any storage from
+ * @param s Server for which the logger is initialized
+ * @return Pointer to handle passed later to writer() function
+ */
+ void * (*init)(apr_pool_t *p, server_rec *s);
+
+ /** Logs the error message to external error log.
+ * @param info Context of the error message
+ * @param handle Handle created by init() function
+ * @param errstr Error message
+ * @param len Length of the error message
+ */
+ apr_status_t (*writer)(const ap_errorlog_info *info, void *handle,
+ const char *errstr, int len);
+};
+
/**
* callback function prototype for a external errorlog handler
* @note To avoid unbounded memory usage, these functions must not allocate
diff --git a/include/httpd.h b/include/httpd.h
index 175c7d867f6..0439e97e27b 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -1245,6 +1245,10 @@ struct server_rec {
apr_file_t *error_log;
/** The log level configuration */
struct ap_logconf log;
+ /** External error log writer provider */
+ struct ap_errorlog_provider *errorlog_provider;
+ /** Handle to be passed to external log provider's logging method */
+ void *errorlog_provider_handle;
/* Module-specific configuration for server, and defaults... */
diff --git a/server/core.c b/server/core.c
index 9e01154dc32..de7997952c8 100644
--- a/server/core.c
+++ b/server/core.c
@@ -48,6 +48,7 @@
#include "mod_core.h"
#include "mod_proxy.h"
#include "ap_listen.h"
+#include "ap_provider.h"
#include "mod_so.h" /* for ap_find_loaded_module_symbol */
@@ -3955,6 +3956,49 @@ static apr_array_header_t *parse_errorlog_string(apr_pool_t *p,
return a;
}
+static const char *set_errorlog(cmd_parms *cmd, void *dummy, const char *arg1,
+ const char *arg2)
+{
+ ap_errorlog_provider *provider;
+ cmd->server->errorlog_provider = NULL;
+
+ if (!arg2) {
+ /* Stay backward compatible and check for "syslog" */
+ if (strncmp("syslog", arg1, 6) == 0) {
+ arg2 = arg1 + 7; /* skip the ':' if any */
+ arg1 = "syslog";
+ }
+ else {
+ /* Admin can define only "ErrorLog provider" and we should
+ * still handle that using the defined provider, but with empty
+ * error_fname. */
+ provider = ap_lookup_provider(AP_ERRORLOG_PROVIDER_GROUP, arg1,
+ AP_ERRORLOG_PROVIDER_VERSION);
+ if (provider) {
+ arg2 = "";
+ }
+ else {
+ return set_server_string_slot(cmd, dummy, arg1);
+ }
+ }
+ }
+
+ if (strcmp("file", arg1) == 0) {
+ return set_server_string_slot(cmd, dummy, arg2);
+ }
+
+ provider = ap_lookup_provider(AP_ERRORLOG_PROVIDER_GROUP, arg1,
+ AP_ERRORLOG_PROVIDER_VERSION);
+ if (!provider) {
+ return apr_psprintf(cmd->pool,
+ "Unknown ErrorLog provider: %s",
+ arg1);
+ }
+
+ cmd->server->errorlog_provider = provider;
+ return set_server_string_slot(cmd, dummy, arg2);
+}
+
static const char *set_errorlog_format(cmd_parms *cmd, void *dummy,
const char *arg1, const char *arg2)
{
@@ -4118,7 +4162,7 @@ AP_INIT_TAKE1("ServerRoot", set_server_root, NULL, RSRC_CONF | EXEC_ON_READ,
"Common directory of server-related files (logs, confs, etc.)"),
AP_INIT_TAKE1("DefaultRuntimeDir", set_runtime_dir, NULL, RSRC_CONF | EXEC_ON_READ,
"Common directory for run-time files (shared memory, locks, etc.)"),
-AP_INIT_TAKE1("ErrorLog", set_server_string_slot,
+AP_INIT_TAKE12("ErrorLog", set_errorlog,
(void *)APR_OFFSETOF(server_rec, error_fname), RSRC_CONF,
"The filename of the error log"),
AP_INIT_TAKE12("ErrorLogFormat", set_errorlog_format, NULL, RSRC_CONF,
@@ -4560,7 +4604,7 @@ AP_DECLARE(int) ap_sys_privileges_handlers(int inc)
static int check_errorlog_dir(apr_pool_t *p, server_rec *s)
{
if (!s->error_fname || s->error_fname[0] == '|'
- || strcmp(s->error_fname, "syslog") == 0) {
+ || s->errorlog_provider != NULL) {
return APR_SUCCESS;
}
else {
@@ -4986,7 +5030,7 @@ static void core_dump_config(apr_pool_t *p, server_rec *s)
apr_file_printf(out, "ServerRoot: \"%s\"\n", ap_server_root);
tmp = ap_server_root_relative(p, sconf->ap_document_root);
apr_file_printf(out, "Main DocumentRoot: \"%s\"\n", tmp);
- if (s->error_fname[0] != '|' && strcmp(s->error_fname, "syslog") != 0)
+ if (s->error_fname[0] != '|' && s->errorlog_provider == NULL)
tmp = ap_server_root_relative(p, s->error_fname);
else
tmp = s->error_fname;
diff --git a/server/log.c b/server/log.c
index 161cc9d3343..01851241690 100644
--- a/server/log.c
+++ b/server/log.c
@@ -53,6 +53,7 @@
#include "http_main.h"
#include "util_time.h"
#include "ap_mpm.h"
+#include "ap_provider.h"
#if HAVE_GETTID
#include