mirror of
https://github.com/apache/httpd.git
synced 2025-08-10 02:56:11 +00:00
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:
104
docs/manual/mod/mod_journald.xml
Normal file
104
docs/manual/mod/mod_journald.xml
Normal 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>
|
@ -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
|
||||
|
275
modules/loggers/mod_journald.c
Normal file
275
modules/loggers/mod_journald.c
Normal 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,
|
||||
};
|
Reference in New Issue
Block a user