mod_journald: New module implementing error_log provider for systemd-journald.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1610339 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jan Kaluža
2014-07-14 05:52:45 +00:00
parent 4b7b9cd8c4
commit f66a511609
3 changed files with 394 additions and 0 deletions

View File

@ -0,0 +1,104 @@
<?xml version="1.0"?>
<!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
<?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
<!-- $LastChangedRevision: 1533940 $ -->
<!--
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.
-->
<modulesynopsis metafile="mod_journald.xml.meta">
<name>mod_journald</name>
<description>Provides "journald" ErrorLog provider</description>
<status>Extension</status>
<sourcefile>mod_journald.c</sourcefile>
<identifier>journald_module</identifier>
<summary>
<p>This module provides "journald" ErrorLog provider. It allows logging
error messages and CustomLog/TransferLog via systemd-journald(8).</p>
</summary>
<directivesynopsis>
<name>JournaldCustomLog</name>
<description>Enable logging of CustomLog/TransferLog to systemd-journald</description>
<syntax>JournaldCustomLog on|off</syntax>
<default>JournaldCustomLog off</default>
<contextlist><context>server config</context></contextlist>
<usage>
<p>The <directive>JournaldCustomLog</directive> directive enables logging
of CustomLog and TransferLog messages to systemd-journald.
</p>
<note type="warning"><title>Performance warning</title><p>
Currently, systemd-journald is not designed for high-throughput logging
and logging access_log to systemd-journald could decrease the performance
a lot.
</p></note>
</usage>
</directivesynopsis>
<section id="structured">
<title>Structured logging</title>
<p>Systemd-journald allows structured logging and therefore it is
possible to filter logged messages according to various variables.
Currently supported variables are:
</p>
<dl>
<dt><code>LOG</code></dt>
<dd>The name of the log. For ErrorLog, the value is "error_log".
For CustomLog or TransferLog, the value is the first argument of
these directives.</dd>
<dt><code>REQUEST_HOSTNAME</code></dt>
<dd>Host, as set by full URI or Host: header in the request.</dd>
<dt><code>REQUEST_USER</code></dt>
<dd>If an authentication check was made, this gets set to the user
name.</dd>
<dt><code>REQUEST_USERAGENT_IP</code></dt>
<dd>The address that originated the request.</dd>
<dt><code>REQUEST_URI</code></dt>
<dd>The path portion of the URI, or "/" if no path provided.</dd>
<dt><code>SERVER_HOSTNAME</code></dt>
<dd>The hostname of server for which the log message has been
generated.</dd>
</dl>
<p>These variables can be for example used to show only log messages
for particular URI using <code>journalctl</code>:
</p>
<highlight>journalctl REQUEST_URI=/index.html -a</highlight>
<p>For more examples, see systemd-journalctl documentation.</p>
</section>
<section id="examples">
<title>Examples</title>
<p>Using <code>journald</code> in ErrorLog directive (see <module>core</module>)
instead of a filename enables logging via systemd-journald(8)
if the system supports it.
</p>
<highlight language="config">ErrorLog journald</highlight>
</section>
</modulesynopsis>

View File

@ -4,6 +4,21 @@ dnl APACHE_MODULE(name, helptext[, objects[, structname[, default[, config]]]])
APACHE_MODPATH_INIT(loggers)
APACHE_MODULE(journald, Journald support, , , all, [
AC_CHECK_LIB(systemd-journal, sd_journal_sendv, JOURNALD_LIBS="-lsystemd-journal")
AC_CHECK_LIB(systemd-id128, sd_id128_to_string, ID128_LIBS="-lsystemd-id128")
AC_CHECK_HEADERS(systemd/sd-journal.h, [ap_HAVE_SD_JOURNAL_H="yes"], [ap_HAVE_SD_JOURNAL_H="no"])
AC_CHECK_HEADERS(systemd/sd-id128.h, [ap_HAVE_SD_ID128_H="yes"], [ap_HAVE_SD_ID128_H="no"])
if test $ap_HAVE_SD_JOURNAL_H = "no" || test $ap_HAVE_SD_ID128_H = "no" || test -z "${JOURNALD_LIBS}" || test -z "${ID128_LIBS}"; then
AC_MSG_WARN([Your system does not support Journald.])
enable_journald="no"
else
APR_ADDTO(MOD_JOURNALD_LDADD, [$JOURNALD_LIBS])
APR_ADDTO(MOD_JOURNALD_LDADD, [$ID128_LIBS])
enable_journald="yes"
fi
])
APACHE_MODULE(syslog, logging to syslog, , , all, [
AC_CHECK_HEADERS(syslog.h, [ap_HAVE_SYSLOG_H="yes"], [ap_HAVE_SYSLOG_H="no"])
if test $ap_HAVE_SYSLOG_H = "no"; then

View File

@ -0,0 +1,275 @@
/* 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 <stdint.h>
#include <ap_config.h>
#include "ap_mpm.h"
#include "ap_provider.h"
#include <http_core.h>
#include <httpd.h>
#include <http_log.h>
#include <apr_version.h>
#include <apr_pools.h>
#include <apr_strings.h>
#include "unixd.h"
#include "scoreboard.h"
#include "mpm_common.h"
#include "mod_log_config.h"
#define SD_JOURNAL_SUPPRESS_LOCATION 1
#include "systemd/sd-journal.h"
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#define MAX_ENTRIES 15
static int handle_custom_log = 0;
static int journald_info_get_priority(int level)
{
switch(level) {
/* We don't use EMERG here, because journald broadcasts EMERG messages
* to all terminals. APLOG_EMERG is usually not used in this context.
* in httpd code. */
case APLOG_EMERG: return LOG_ALERT;
case APLOG_ALERT: return LOG_ALERT;
case APLOG_CRIT: return LOG_CRIT;
case APLOG_ERR: return LOG_ERR;
case APLOG_WARNING: return LOG_WARNING;
case APLOG_NOTICE: return LOG_NOTICE;
case APLOG_INFO: return LOG_INFO;
case APLOG_DEBUG: return LOG_DEBUG;
case -1: return LOG_INFO;
default: return LOG_DEBUG;
}
return LOG_INFO;
}
static apr_pool_t *journald_info_get_pool(const ap_errorlog_info *info)
{
if (info->r && info->r->pool)
return info->r->pool;
if (info->c && info->c->pool)
return info->c->pool;
if (info->pool)
return info->pool;
if (info->s && info->s->process && info->s->process->pool)
return info->s->process->pool;
return 0;
}
static apr_status_t iovec_add_entry(apr_pool_t *pool, struct iovec *iov,
const char *format, int len, ...)
{
va_list ap;
va_start(ap, len);
iov->iov_base = apr_pvsprintf(pool, format, ap);
va_end(ap);
if (!iov->iov_base) {
return APR_ENOMEM;
}
if (len < 0) {
iov->iov_len = strlen(iov->iov_base);
}
else {
iov->iov_len = len;
}
return APR_SUCCESS;
}
static void journald_log(apr_pool_t *pool, const char *log,
const char *errstr, int len, int priority,
const server_rec *s, const request_rec *r)
{
apr_pool_t *subpool;
apr_status_t rv = APR_SUCCESS;
struct iovec iov[MAX_ENTRIES];
int iov_size = 0;
if (apr_pool_create(&subpool, pool) != APR_SUCCESS) {
/* We were not able to create subpool, log at least what we have. */
sd_journal_send("MESSAGE=%s", errstr, "LOG=%s", log,
"PRIORITY=%i", priority,
NULL);
return;
}
/* Adds new entry to iovec if previous additions were successful. */
#define IOVEC_ADD_LEN(FORMAT, VAR, LEN) \
if (rv == APR_SUCCESS && iov_size < MAX_ENTRIES) { \
if ((rv = iovec_add_entry(subpool, &iov[iov_size], FORMAT, LEN, VAR)) \
== APR_SUCCESS) \
iov_size++; \
}
#define IOVEC_ADD(FORMAT, VAR) IOVEC_ADD_LEN(FORMAT, VAR, -1)
IOVEC_ADD_LEN("MESSAGE=%s", errstr, len + 8);
IOVEC_ADD("LOG=%s", log);
IOVEC_ADD("PRIORITY=%i", priority);
if (s) {
IOVEC_ADD("SERVER_HOSTNAME=%s", s->server_hostname);
}
if (r) {
IOVEC_ADD("REQUEST_HOSTNAME=%s", r->hostname);
IOVEC_ADD("REQUEST_USER=%s", r->user ? r->user : "");
IOVEC_ADD("REQUEST_URI=%s", r->uri ? r->uri : "");
IOVEC_ADD("REQUEST_USERAGENT_IP=%s", r->useragent_ip);
}
sd_journal_sendv(iov, iov_size);
apr_pool_destroy(subpool);
}
static void *journald_error_log_init(apr_pool_t *p, server_rec *s)
{
void *success = (void *)p; /* anything non-NULL is success */
return success;
}
static apr_status_t journald_error_log(const ap_errorlog_info *info,
void *handle, const char *errstr,
apr_size_t len)
{
const server_rec *s = info->s;
const request_rec *r = info->r;
apr_pool_t *pool;
const char *log_name = (s && s->error_fname && *s->error_fname) ?
s->error_fname : "error_log";
pool = journald_info_get_pool(info);
if (!pool) {
/* We don't have any pool, so at least log the message without
* any additional data. */
sd_journal_send("MESSAGE=%s", errstr, "LOG=%s", "log_name",
"PRIORITY=%i", journald_info_get_priority(info->level),
NULL);
return APR_SUCCESS;
}
journald_log(pool, log_name, errstr, len,
journald_info_get_priority(info->level), s, r);
return APR_SUCCESS;
}
static const char *journald_error_log_parse(cmd_parms *cmd, const char *arg)
{
return NULL;
}
static apr_status_t journald_log_writer(request_rec *r,
void *handle,
const char **strs,
int *strl,
int nelts,
apr_size_t len)
{
char *str;
char *s;
int i;
apr_status_t rv = APR_SUCCESS;
str = apr_palloc(r->pool, len + 1);
/* Last string is always \n, so skipt it */
for (i = 0, s = str; i < nelts - 1; ++i) {
memcpy(s, strs[i], strl[i]);
s += strl[i];
}
journald_log(r->pool, (char *) handle, str, len,
LOG_INFO, r->server, r);
return rv;
}
static void *journald_log_writer_init(apr_pool_t *p, server_rec *s,
const char* name)
{
char *log_name = apr_pstrdup(p, name);
return log_name;
}
static int journald_open_logs(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
APR_OPTIONAL_FN_TYPE(ap_log_set_writer_init) *log_set_writer_init;
APR_OPTIONAL_FN_TYPE(ap_log_set_writer) *log_set_writer;
if (!handle_custom_log) {
return OK;
}
log_set_writer_init = APR_RETRIEVE_OPTIONAL_FN(ap_log_set_writer_init);
log_set_writer = APR_RETRIEVE_OPTIONAL_FN(ap_log_set_writer);
if (log_set_writer_init) {
log_set_writer_init(&journald_log_writer_init);
}
if (log_set_writer) {
log_set_writer(&journald_log_writer);
}
return OK;
}
static void journald_register_hooks(apr_pool_t *p)
{
static const ap_errorlog_provider journald_provider = {
&journald_error_log_init,
&journald_error_log,
&journald_error_log_parse,
0
};
ap_register_provider(p, AP_ERRORLOG_PROVIDER_GROUP, "journald",
AP_ERRORLOG_PROVIDER_VERSION,
&journald_provider);
ap_hook_open_logs(journald_open_logs, NULL, NULL, APR_HOOK_FIRST);
}
static const char *set_custom_log_on(cmd_parms *parms, void *dummy, int flag)
{
handle_custom_log = flag;
return NULL;
}
static const command_rec journald_cmds[] =
{
AP_INIT_FLAG("JournaldCustomLog", set_custom_log_on, NULL, RSRC_CONF,
"Enable logging of CustomLog/TransferLog to journald"),
{NULL}
};
AP_DECLARE_MODULE(journald) =
{
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
journald_cmds,
journald_register_hooks,
};