2008-11-06 16:36:07 +00:00
|
|
|
/*
|
2012-12-12 17:59:27 +00:00
|
|
|
* virlog.c: internal logging and debugging
|
2008-11-06 16:36:07 +00:00
|
|
|
*
|
2014-10-28 18:38:04 +00:00
|
|
|
* Copyright (C) 2008, 2010-2014 Red Hat, Inc.
|
2008-11-06 16:36:07 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2008-11-06 16:36:07 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2009-01-06 18:32:03 +00:00
|
|
|
#include <unistd.h>
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <syslog.h>
|
2008-12-22 10:36:54 +00:00
|
|
|
#endif
|
|
|
|
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 17:44:57 +00:00
|
|
|
#include "virutil.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2011-11-29 12:32:31 +00:00
|
|
|
#include "virtime.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2016-10-31 11:50:24 +00:00
|
|
|
#include "configmake.h"
|
2020-01-14 17:38:59 +00:00
|
|
|
#include "virsocket.h"
|
2008-11-06 16:36:07 +00:00
|
|
|
|
2012-10-01 22:38:56 +00:00
|
|
|
/* Journald output is only supported on Linux new enough to expose
|
|
|
|
* htole64. */
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H && defined(__linux__) && WITH_DECL_HTOLE64
|
2012-10-01 22:38:56 +00:00
|
|
|
# define USE_JOURNALD 1
|
2020-01-22 11:49:45 +00:00
|
|
|
# include <sys/uio.h>
|
2012-10-01 22:38:56 +00:00
|
|
|
#endif
|
|
|
|
|
2011-03-08 10:31:20 +00:00
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("util.log");
|
|
|
|
|
2019-11-13 14:38:58 +00:00
|
|
|
static GRegex *virLogRegex;
|
2013-03-04 20:46:32 +00:00
|
|
|
|
|
|
|
|
2013-04-22 16:33:01 +00:00
|
|
|
#define VIR_LOG_DATE_REGEX "[0-9]{4}-[0-9]{2}-[0-9]{2}"
|
|
|
|
#define VIR_LOG_TIME_REGEX "[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}\\+[0-9]{4}"
|
2013-03-04 20:46:32 +00:00
|
|
|
#define VIR_LOG_PID_REGEX "[0-9]+"
|
2013-10-11 16:07:54 +00:00
|
|
|
#define VIR_LOG_LEVEL_REGEX "(debug|info|warning|error)"
|
2013-03-04 20:46:32 +00:00
|
|
|
|
|
|
|
#define VIR_LOG_REGEX \
|
|
|
|
VIR_LOG_DATE_REGEX " " VIR_LOG_TIME_REGEX ": " \
|
|
|
|
VIR_LOG_PID_REGEX ": " VIR_LOG_LEVEL_REGEX " : "
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2016-03-15 20:35:17 +00:00
|
|
|
VIR_ENUM_DECL(virLogDestination);
|
2019-03-16 18:20:32 +00:00
|
|
|
VIR_ENUM_IMPL(virLogDestination,
|
|
|
|
VIR_LOG_TO_OUTPUT_LAST,
|
2019-01-20 16:30:15 +00:00
|
|
|
"stderr", "syslog", "file", "journald",
|
|
|
|
);
|
2016-03-15 20:35:17 +00:00
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/*
|
|
|
|
* Filters are used to refine the rules on what to keep or drop
|
|
|
|
* based on a matching pattern (currently a substring)
|
|
|
|
*/
|
|
|
|
struct _virLogFilter {
|
2013-09-03 11:36:22 +00:00
|
|
|
char *match;
|
2012-09-27 12:58:58 +00:00
|
|
|
virLogPriority priority;
|
2008-12-22 10:36:54 +00:00
|
|
|
};
|
|
|
|
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
static int virLogFiltersSerial = 1;
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogFilter **virLogFilters;
|
2016-03-29 13:54:08 +00:00
|
|
|
static size_t virLogNbFilters;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Outputs are used to emit the messages retained
|
|
|
|
* after filtering, multiple output can be used simultaneously
|
|
|
|
*/
|
|
|
|
struct _virLogOutput {
|
2015-12-04 17:35:54 +00:00
|
|
|
bool logInitMessage;
|
2008-12-22 10:36:54 +00:00
|
|
|
void *data;
|
|
|
|
virLogOutputFunc f;
|
|
|
|
virLogCloseFunc c;
|
2012-09-27 12:58:58 +00:00
|
|
|
virLogPriority priority;
|
2009-10-08 15:05:01 +00:00
|
|
|
virLogDestination dest;
|
2013-09-03 11:36:22 +00:00
|
|
|
char *name;
|
2008-12-22 10:36:54 +00:00
|
|
|
};
|
|
|
|
|
2016-10-31 11:50:24 +00:00
|
|
|
static char *virLogDefaultOutput;
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogOutput **virLogOutputs;
|
2016-03-17 13:52:57 +00:00
|
|
|
static size_t virLogNbOutputs;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Default priorities
|
|
|
|
*/
|
2009-07-01 11:21:15 +00:00
|
|
|
static virLogPriority virLogDefaultPriority = VIR_LOG_DEFAULT;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2016-03-30 10:25:35 +00:00
|
|
|
static void virLogResetFilters(void);
|
|
|
|
static void virLogResetOutputs(void);
|
2021-03-11 07:16:13 +00:00
|
|
|
static void virLogOutputToFd(virLogSource *src,
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogPriority priority,
|
2012-09-27 13:28:44 +00:00
|
|
|
const char *filename,
|
2012-09-27 13:14:01 +00:00
|
|
|
int linenr,
|
2012-09-27 13:28:44 +00:00
|
|
|
const char *funcname,
|
2012-09-20 18:24:00 +00:00
|
|
|
const char *timestamp,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata,
|
2012-09-27 13:14:01 +00:00
|
|
|
const char *rawstr,
|
|
|
|
const char *str,
|
2012-09-20 18:24:00 +00:00
|
|
|
void *data);
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2014-02-27 17:44:53 +00:00
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/*
|
|
|
|
* Logs accesses must be serialized though a mutex
|
|
|
|
*/
|
2009-01-15 19:56:05 +00:00
|
|
|
virMutex virLogMutex;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
void
|
|
|
|
virLogLock(void)
|
2008-12-22 10:36:54 +00:00
|
|
|
{
|
2009-01-15 19:56:05 +00:00
|
|
|
virMutexLock(&virLogMutex);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
2012-09-27 13:14:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
virLogUnlock(void)
|
2008-12-22 10:36:54 +00:00
|
|
|
{
|
2009-01-15 19:56:05 +00:00
|
|
|
virMutexUnlock(&virLogMutex);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
static void
|
2016-10-31 11:50:24 +00:00
|
|
|
virLogSetDefaultOutputToStderr(void)
|
|
|
|
{
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogDefaultOutput = g_strdup_printf("%d:stderr",
|
|
|
|
virLogDefaultPriority);
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
static void
|
2016-10-31 11:50:24 +00:00
|
|
|
virLogSetDefaultOutputToJournald(void)
|
|
|
|
{
|
|
|
|
virLogPriority priority = virLogDefaultPriority;
|
|
|
|
|
|
|
|
/* By default we don't want to log too much stuff into journald as
|
|
|
|
* it may employ rate limiting and thus block libvirt execution. */
|
|
|
|
if (priority == VIR_LOG_DEBUG)
|
|
|
|
priority = VIR_LOG_INFO;
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogDefaultOutput = g_strdup_printf("%d:journald", priority);
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2019-06-21 15:40:02 +00:00
|
|
|
virLogSetDefaultOutputToFile(const char *binary, bool privileged)
|
2016-10-31 11:50:24 +00:00
|
|
|
{
|
2019-12-19 09:11:49 +00:00
|
|
|
g_autofree char *logdir = NULL;
|
2016-10-31 11:50:24 +00:00
|
|
|
mode_t old_umask;
|
|
|
|
|
|
|
|
if (privileged) {
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogDefaultOutput = g_strdup_printf("%d:file:%s/log/libvirt/%s.log",
|
|
|
|
virLogDefaultPriority, LOCALSTATEDIR, binary);
|
2016-10-31 11:50:24 +00:00
|
|
|
} else {
|
2019-12-19 09:12:56 +00:00
|
|
|
logdir = virGetUserCacheDirectory();
|
2016-10-31 11:50:24 +00:00
|
|
|
|
|
|
|
old_umask = umask(077);
|
2021-02-26 08:37:10 +00:00
|
|
|
if (g_mkdir_with_parents(logdir, 0777) < 0) {
|
2016-10-31 11:50:24 +00:00
|
|
|
umask(old_umask);
|
2019-12-19 09:11:49 +00:00
|
|
|
return -1;
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
umask(old_umask);
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogDefaultOutput = g_strdup_printf("%d:file:%s/%s.log",
|
|
|
|
virLogDefaultPriority, logdir, binary);
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
|
2019-12-19 09:11:49 +00:00
|
|
|
return 0;
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* virLogSetDefaultOutput:
|
2019-06-21 15:40:02 +00:00
|
|
|
* @binary: the binary for which logging is performed. The log file name
|
|
|
|
* will be derived from the binary name, with ".log" appended.
|
2016-10-31 11:50:24 +00:00
|
|
|
* @godaemon: whether we're running daemonized
|
|
|
|
* @privileged: whether we're running with root privileges or not (session)
|
|
|
|
*
|
|
|
|
* Decides on what the default output (journald, file, stderr) should be
|
2019-06-21 15:40:02 +00:00
|
|
|
* according to @binary, @godaemon, @privileged. This function should be run
|
2016-10-31 11:50:24 +00:00
|
|
|
* exactly once at daemon startup, so no locks are used.
|
|
|
|
*/
|
2019-10-22 13:26:14 +00:00
|
|
|
void
|
2019-06-21 15:40:02 +00:00
|
|
|
virLogSetDefaultOutput(const char *binary, bool godaemon, bool privileged)
|
2016-10-31 11:50:24 +00:00
|
|
|
{
|
2019-01-03 13:32:36 +00:00
|
|
|
bool have_journald = access("/run/systemd/journal/socket", W_OK) >= 0;
|
|
|
|
|
|
|
|
if (godaemon) {
|
|
|
|
if (have_journald)
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogSetDefaultOutputToJournald();
|
|
|
|
else
|
|
|
|
virLogSetDefaultOutputToFile(binary, privileged);
|
2019-01-03 13:32:36 +00:00
|
|
|
} else {
|
|
|
|
if (!isatty(STDIN_FILENO) && have_journald)
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogSetDefaultOutputToJournald();
|
|
|
|
else
|
|
|
|
virLogSetDefaultOutputToStderr();
|
2019-01-03 13:32:36 +00:00
|
|
|
}
|
2016-10-31 11:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
virLogGetDefaultOutput(void)
|
|
|
|
{
|
|
|
|
return virLogDefaultOutput;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
static const char *
|
|
|
|
virLogPriorityString(virLogPriority lvl)
|
|
|
|
{
|
2008-12-22 10:36:54 +00:00
|
|
|
switch (lvl) {
|
2011-01-21 16:30:17 +00:00
|
|
|
case VIR_LOG_DEBUG:
|
|
|
|
return "debug";
|
|
|
|
case VIR_LOG_INFO:
|
|
|
|
return "info";
|
|
|
|
case VIR_LOG_WARN:
|
|
|
|
return "warning";
|
|
|
|
case VIR_LOG_ERROR:
|
|
|
|
return "error";
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
2011-01-21 16:30:17 +00:00
|
|
|
return "unknown";
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
static int
|
|
|
|
virLogOnceInit(void)
|
2012-07-11 13:35:42 +00:00
|
|
|
{
|
2009-01-15 19:56:05 +00:00
|
|
|
if (virMutexInit(&virLogMutex) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogLock();
|
2009-07-01 11:21:15 +00:00
|
|
|
virLogDefaultPriority = VIR_LOG_DEFAULT;
|
2013-03-04 20:46:32 +00:00
|
|
|
|
2019-11-13 14:38:58 +00:00
|
|
|
virLogRegex = g_regex_new(VIR_LOG_REGEX, G_REGEX_OPTIMIZE, 0, NULL);
|
2013-03-04 20:46:32 +00:00
|
|
|
|
2019-12-23 14:55:45 +00:00
|
|
|
/* GLib caches the hostname using a one time thread initializer.
|
|
|
|
* We want to prime this cache early though, because at later time
|
2018-01-27 22:43:58 +00:00
|
|
|
* it might not be possible to load NSS modules via getaddrinfo()
|
|
|
|
* (e.g. at container startup the host filesystem will not be
|
2018-02-12 10:03:08 +00:00
|
|
|
* accessible anymore.
|
|
|
|
*/
|
2019-12-23 14:55:45 +00:00
|
|
|
ignore_value(g_get_host_name());
|
2018-01-27 22:43:58 +00:00
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogUnlock();
|
2011-01-21 16:30:17 +00:00
|
|
|
return 0;
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2019-01-20 17:23:29 +00:00
|
|
|
VIR_ONCE_GLOBAL_INIT(virLog);
|
2012-07-11 13:35:42 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/**
|
|
|
|
* virLogReset:
|
|
|
|
*
|
|
|
|
* Reset the logging module to its default initial state
|
|
|
|
*
|
|
|
|
* Returns 0 if successful, and -1 in case or error
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
int
|
|
|
|
virLogReset(void)
|
|
|
|
{
|
2012-07-11 13:35:42 +00:00
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
virLogLock();
|
|
|
|
virLogResetFilters();
|
|
|
|
virLogResetOutputs();
|
2009-07-01 11:21:15 +00:00
|
|
|
virLogDefaultPriority = VIR_LOG_DEFAULT;
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogUnlock();
|
2011-01-21 16:30:17 +00:00
|
|
|
return 0;
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogSetDefaultPriority:
|
|
|
|
* @priority: the default priority level
|
|
|
|
*
|
|
|
|
* Set the default priority level, i.e. any logged data of a priority
|
|
|
|
* equal or superior to this level will be logged, unless a specific rule
|
|
|
|
* was defined for the log category of the message.
|
|
|
|
*
|
|
|
|
* Returns 0 if successful, -1 in case of error.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
int
|
|
|
|
virLogSetDefaultPriority(virLogPriority priority)
|
|
|
|
{
|
2009-08-06 13:38:11 +00:00
|
|
|
if ((priority < VIR_LOG_DEBUG) || (priority > VIR_LOG_ERROR)) {
|
2016-03-21 13:35:37 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Failed to set logging priority, argument '%u' is "
|
|
|
|
"invalid"), priority);
|
2011-01-21 16:30:17 +00:00
|
|
|
return -1;
|
2009-08-06 13:38:11 +00:00
|
|
|
}
|
2012-07-11 13:35:42 +00:00
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogDefaultPriority = priority;
|
2011-01-21 16:30:17 +00:00
|
|
|
return 0;
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/**
|
|
|
|
* virLogResetFilters:
|
|
|
|
*
|
|
|
|
* Removes the set of logging filters defined.
|
|
|
|
*/
|
2016-03-30 10:25:35 +00:00
|
|
|
static void
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogResetFilters(void)
|
|
|
|
{
|
2016-03-29 20:07:23 +00:00
|
|
|
virLogFilterListFree(virLogFilters, virLogNbFilters);
|
|
|
|
virLogFilters = NULL;
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogNbFilters = 0;
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
virLogFiltersSerial++;
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2016-07-04 10:16:36 +00:00
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilterFree(virLogFilter *filter)
|
2016-07-04 10:16:36 +00:00
|
|
|
{
|
|
|
|
if (!filter)
|
|
|
|
return;
|
|
|
|
|
2021-02-03 19:32:34 +00:00
|
|
|
g_free(filter->match);
|
|
|
|
g_free(filter);
|
2016-07-04 10:16:36 +00:00
|
|
|
}
|
|
|
|
|
2016-03-29 20:07:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogFilterFreeList:
|
|
|
|
* @list: list of filters to be freed
|
|
|
|
* @count: number of elements in the list
|
|
|
|
*
|
|
|
|
* Frees a list of filters.
|
|
|
|
*/
|
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilterListFree(virLogFilter **list, int count)
|
2016-03-29 20:07:23 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!list || count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
virLogFilterFree(list[i]);
|
2021-02-03 19:32:34 +00:00
|
|
|
g_free(list);
|
2016-03-29 20:07:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/**
|
|
|
|
* virLogResetOutputs:
|
|
|
|
*
|
|
|
|
* Removes the set of logging output defined.
|
|
|
|
*/
|
2016-03-30 10:25:35 +00:00
|
|
|
static void
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogResetOutputs(void)
|
|
|
|
{
|
2016-03-30 12:11:12 +00:00
|
|
|
virLogOutputListFree(virLogOutputs, virLogNbOutputs);
|
|
|
|
virLogOutputs = NULL;
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogNbOutputs = 0;
|
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2016-07-04 10:19:38 +00:00
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutputFree(virLogOutput *output)
|
2016-07-04 10:19:38 +00:00
|
|
|
{
|
|
|
|
if (!output)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (output->c)
|
|
|
|
output->c(output->data);
|
2021-02-03 19:32:34 +00:00
|
|
|
g_free(output->name);
|
|
|
|
g_free(output);
|
2016-07-04 10:19:38 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-30 12:11:12 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogOutputsFreeList:
|
|
|
|
* @list: list of outputs to be freed
|
|
|
|
* @count: number of elements in the list
|
|
|
|
*
|
|
|
|
* Frees a list of outputs.
|
|
|
|
*/
|
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutputListFree(virLogOutput **list, int count)
|
2016-03-30 12:11:12 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!list || count < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
virLogOutputFree(list[i]);
|
2021-02-03 19:32:34 +00:00
|
|
|
g_free(list);
|
2016-03-30 12:11:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
static void
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
virLogFormatString(char **msg,
|
2012-09-27 13:00:21 +00:00
|
|
|
int linenr,
|
2012-09-27 13:28:44 +00:00
|
|
|
const char *funcname,
|
2012-09-27 12:58:58 +00:00
|
|
|
virLogPriority priority,
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
const char *str)
|
|
|
|
{
|
|
|
|
if ((funcname != NULL)) {
|
2019-10-22 13:26:14 +00:00
|
|
|
*msg = g_strdup_printf("%llu: %s : %s:%d : %s\n",
|
2013-06-07 15:10:28 +00:00
|
|
|
virThreadSelfID(), virLogPriorityString(priority),
|
|
|
|
funcname, linenr, str);
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
} else {
|
2019-10-22 13:26:14 +00:00
|
|
|
*msg = g_strdup_printf("%llu: %s : %s\n",
|
2013-06-07 15:10:28 +00:00
|
|
|
virThreadSelfID(), virLogPriorityString(priority),
|
|
|
|
str);
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
static void
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogVersionString(const char **rawmsg,
|
|
|
|
char **msg)
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
{
|
2015-02-02 10:28:30 +00:00
|
|
|
*rawmsg = VIR_LOG_VERSION_STRING;
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogFormatString(msg, 0, NULL, VIR_LOG_INFO, VIR_LOG_VERSION_STRING);
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 17:35:54 +00:00
|
|
|
/* Similar to virGetHostname() but avoids use of error
|
|
|
|
* reporting APIs or logging APIs, to prevent recursion
|
|
|
|
*/
|
2019-10-22 13:26:14 +00:00
|
|
|
static void
|
2016-01-06 16:14:44 +00:00
|
|
|
virLogHostnameString(char **rawmsg,
|
2015-12-04 17:35:54 +00:00
|
|
|
char **msg)
|
|
|
|
{
|
|
|
|
char *hoststr;
|
|
|
|
|
2019-12-23 14:55:45 +00:00
|
|
|
hoststr = g_strdup_printf("hostname: %s", g_get_host_name());
|
2015-12-04 17:35:54 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogFormatString(msg, 0, NULL, VIR_LOG_INFO, hoststr);
|
2015-12-04 17:35:54 +00:00
|
|
|
*rawmsg = hoststr;
|
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
static void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogSourceUpdate(virLogSource *source)
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
{
|
|
|
|
virLogLock();
|
|
|
|
if (source->serial < virLogFiltersSerial) {
|
|
|
|
unsigned int priority = virLogDefaultPriority;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < virLogNbFilters; i++) {
|
2019-12-20 16:02:49 +00:00
|
|
|
if (g_pattern_match_simple(virLogFilters[i]->match, source->name)) {
|
2016-03-29 13:54:08 +00:00
|
|
|
priority = virLogFilters[i]->priority;
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
source->priority = priority;
|
|
|
|
source->serial = virLogFiltersSerial;
|
|
|
|
}
|
|
|
|
virLogUnlock();
|
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2012-01-23 14:57:16 +00:00
|
|
|
/**
|
|
|
|
* virLogVMessage:
|
2012-09-27 13:44:22 +00:00
|
|
|
* @source: where is that message coming from
|
2012-01-23 14:57:16 +00:00
|
|
|
* @priority: the priority level
|
2012-09-27 13:44:22 +00:00
|
|
|
* @filename: file where the message was emitted
|
2012-01-23 14:57:16 +00:00
|
|
|
* @linenr: line where the message was emitted
|
2012-09-27 13:44:22 +00:00
|
|
|
* @funcname: the function emitting the (debug) message
|
Add a metadata parameter to virLog{, V}Message
... and update all users. No change in functionality, the parameter
will be used later.
The metadata representation is as minimal as possible, but requires
the caller to allocate an array on stack explicitly.
The alternative of using varargs in the virLogMessage() callers:
* Would not allow the caller to optionally omit some metadata elements,
except by having two calls to virLogMessage.
* Would not be as type-safe (e.g. using int vs. size_t), and the compiler
wouldn't be able to do type checking
* Depending on parameter order:
a) virLogMessage(..., message format, message params...,
metadata..., NULL)
can not be portably implemented (parse_printf_format() is a glibc
function)
b) virLogMessage(..., metadata..., NULL,
message format, message params...)
would prevent usage of ATTRIBUTE_FMT_PRINTF and the associated
compiler checking.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2012-10-17 18:17:14 +00:00
|
|
|
* @metadata: NULL or metadata array, terminated by an item with NULL key
|
2012-01-23 14:57:16 +00:00
|
|
|
* @fmt: the string format
|
|
|
|
* @vargs: format args
|
|
|
|
*
|
|
|
|
* Call the libvirt logger with some information. Based on the configuration
|
|
|
|
* the message may be stored, sent to output or just discarded
|
|
|
|
*/
|
2020-02-13 23:12:24 +00:00
|
|
|
static void
|
|
|
|
G_GNUC_PRINTF(7, 0)
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogVMessage(virLogSource *source,
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogPriority priority,
|
2012-09-27 13:28:44 +00:00
|
|
|
const char *filename,
|
2012-09-27 13:14:01 +00:00
|
|
|
int linenr,
|
2012-09-27 13:28:44 +00:00
|
|
|
const char *funcname,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata,
|
2012-09-27 13:14:01 +00:00
|
|
|
const char *fmt,
|
|
|
|
va_list vargs)
|
2011-07-06 22:29:02 +00:00
|
|
|
{
|
2015-12-04 17:35:54 +00:00
|
|
|
static bool logInitMessageStderr = true;
|
2020-11-23 22:09:35 +00:00
|
|
|
g_autofree char *str = NULL;
|
|
|
|
g_autofree char *msg = NULL;
|
2011-11-29 12:32:31 +00:00
|
|
|
char timestamp[VIR_TIME_STRING_BUFLEN];
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2011-02-16 23:49:15 +00:00
|
|
|
int saved_errno = errno;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2012-07-11 13:35:42 +00:00
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
if (fmt == NULL)
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
return;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
/*
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
* 3 intentionally non-thread safe variable reads.
|
|
|
|
* Since writes to the variable are serialized on
|
|
|
|
* virLogLock, worst case result is a log message
|
|
|
|
* is accidentally dropped or emitted, if another
|
|
|
|
* thread is updating log filter list concurrently
|
|
|
|
* with a log message emission.
|
2008-12-22 10:36:54 +00:00
|
|
|
*/
|
Switch to filtering based on log source name instead of filename
Currently the log filter strings are used in a string comparison
against the source filename each time log message is emitted.
If no log filters at all are set, there's obviously no string
comparison to be done. If any single log filter is set though,
this imposes a compute burden on every logging call even if logs
from the file in question are disabled. This string comparison
must also be done while the logging mutex is held, which has
implications for concurrency when multiple threads are emitting
log messages.
This changes the log filtering to be done based on the virLogSource
object name. The virLogSource struct is extended to contain
'serial' and 'priority' fields. Any time the global log filter
rules are changed a global serial number is incremented. When a
log message is emitted, the serial in the virLogSource instance
is compared with the global serial number. If out of date, then
the 'priority' field in the virLogSource instance is updated based
on the new filter rules. The 'priority' field is checked to see
whether the log message should be sent to the log outputs.
The comparisons of the 'serial' and 'priority' fields are done
with no locks held. So in the common case each logging call has
an overhead of 2 integer comparisons, with no locks held. Only
if the decision is made to forward the message to the log output,
or if the 'serial' value is out of date do locks need to be
acquired.
Technically the comparisons of the 'serial' and 'priority' fields
should be done with locks held, or using atomic operations. Both
of these options have a notable performance impact, however, and
since all writes a protected by a global mutex, it is believed
that worst case behaviour where the fields are read concurrently
with being written would merely result in an mistaken emission
or dropping of the log message in question. This is an acceptable
tradeoff for the performance benefit of avoiding locking.
As a quick benchmark, a demo program that registers 500 file
descriptors with the event loop (eg equiv of 500 QEMU monitor
commands), creates pending read I/O on every FD, and then runs
virEventRunDefaultImpl() took 4.6 seconds to do 51200 iterations.
After this optimization it only takes 3.3 seconds, with the log
APIs no longer being a relevant factor in the running time.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2014-02-28 13:55:11 +00:00
|
|
|
if (source->serial < virLogFiltersSerial)
|
|
|
|
virLogSourceUpdate(source);
|
|
|
|
if (priority < source->priority)
|
2011-03-08 10:31:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/*
|
|
|
|
* serialize the error message, add level and timestamp
|
|
|
|
*/
|
2019-10-22 12:11:15 +00:00
|
|
|
str = g_strdup_vprintf(fmt, vargs);
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogFormatString(&msg, linenr, funcname, priority, str);
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2011-11-29 12:32:31 +00:00
|
|
|
if (virTimeStringNowRaw(timestamp) < 0)
|
|
|
|
timestamp[0] = '\0';
|
2011-09-28 13:20:07 +00:00
|
|
|
|
2014-03-03 14:54:33 +00:00
|
|
|
virLogLock();
|
|
|
|
|
2008-12-22 10:36:54 +00:00
|
|
|
/*
|
2014-03-03 14:54:33 +00:00
|
|
|
* Push the message to the outputs defined, if none exist then
|
2008-12-22 10:36:54 +00:00
|
|
|
* use stderr.
|
|
|
|
*/
|
2011-09-28 13:20:07 +00:00
|
|
|
for (i = 0; i < virLogNbOutputs; i++) {
|
2016-03-17 13:52:57 +00:00
|
|
|
if (priority >= virLogOutputs[i]->priority) {
|
|
|
|
if (virLogOutputs[i]->logInitMessage) {
|
2015-12-04 17:35:54 +00:00
|
|
|
const char *rawinitmsg;
|
2016-01-06 16:14:44 +00:00
|
|
|
char *hoststr = NULL;
|
2015-12-04 17:35:54 +00:00
|
|
|
char *initmsg = NULL;
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogVersionString(&rawinitmsg, &initmsg);
|
|
|
|
virLogOutputs[i]->f(&virLogSelf, VIR_LOG_INFO,
|
|
|
|
__FILE__, __LINE__, __func__,
|
|
|
|
timestamp, NULL, rawinitmsg, initmsg,
|
|
|
|
virLogOutputs[i]->data);
|
2015-12-04 17:35:54 +00:00
|
|
|
VIR_FREE(initmsg);
|
2019-10-22 13:26:14 +00:00
|
|
|
|
|
|
|
virLogHostnameString(&hoststr, &initmsg);
|
|
|
|
virLogOutputs[i]->f(&virLogSelf, VIR_LOG_INFO,
|
|
|
|
__FILE__, __LINE__, __func__,
|
|
|
|
timestamp, NULL, hoststr, initmsg,
|
|
|
|
virLogOutputs[i]->data);
|
2016-01-06 16:14:44 +00:00
|
|
|
VIR_FREE(hoststr);
|
2015-12-04 17:35:54 +00:00
|
|
|
VIR_FREE(initmsg);
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogOutputs[i]->logInitMessage = false;
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogOutputs[i]->f(source, priority,
|
2018-09-19 08:38:14 +00:00
|
|
|
filename, linenr, funcname,
|
2019-10-02 10:51:51 +00:00
|
|
|
timestamp, metadata,
|
2018-09-19 08:38:14 +00:00
|
|
|
str, msg, virLogOutputs[i]->data);
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
2014-03-03 18:29:33 +00:00
|
|
|
if (virLogNbOutputs == 0) {
|
2015-12-04 17:35:54 +00:00
|
|
|
if (logInitMessageStderr) {
|
|
|
|
const char *rawinitmsg;
|
2016-01-06 16:14:44 +00:00
|
|
|
char *hoststr = NULL;
|
2015-12-04 17:35:54 +00:00
|
|
|
char *initmsg = NULL;
|
2019-10-22 13:26:14 +00:00
|
|
|
virLogVersionString(&rawinitmsg, &initmsg);
|
|
|
|
virLogOutputToFd(&virLogSelf, VIR_LOG_INFO,
|
|
|
|
__FILE__, __LINE__, __func__,
|
|
|
|
timestamp, NULL, rawinitmsg, initmsg,
|
|
|
|
(void *) STDERR_FILENO);
|
2015-12-04 17:35:54 +00:00
|
|
|
VIR_FREE(initmsg);
|
2019-10-22 13:26:14 +00:00
|
|
|
|
|
|
|
virLogHostnameString(&hoststr, &initmsg);
|
|
|
|
virLogOutputToFd(&virLogSelf, VIR_LOG_INFO,
|
|
|
|
__FILE__, __LINE__, __func__,
|
|
|
|
timestamp, NULL, hoststr, initmsg,
|
|
|
|
(void *) STDERR_FILENO);
|
2016-01-06 16:14:44 +00:00
|
|
|
VIR_FREE(hoststr);
|
2015-12-04 17:35:54 +00:00
|
|
|
VIR_FREE(initmsg);
|
|
|
|
logInitMessageStderr = false;
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
2012-09-27 13:44:22 +00:00
|
|
|
virLogOutputToFd(source, priority,
|
2012-09-27 13:28:44 +00:00
|
|
|
filename, linenr, funcname,
|
2019-10-02 10:51:51 +00:00
|
|
|
timestamp, metadata,
|
2012-09-27 11:45:33 +00:00
|
|
|
str, msg, (void *) STDERR_FILENO);
|
Imprint all logs with version + package build information
The logging functions are enhanced so that immediately prior to
the first log message being printed to any output channel, the
libvirt package version will be printed.
eg
$ LIBVIRT_DEBUG=1 virsh
18:13:28.013: 17536: info : libvirt version: 0.8.7
18:13:28.013: 17536: debug : virInitialize:361 : register drivers
...
The 'configure' script gains two new arguments which can be
used as
--with-packager="Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10"
--with-packager-version="1.fc14"
to allow distros to append a custom string with package specific
data.
The RPM specfile is modified so that it appends the RPM version,
the build host, the build date and the packager name.
eg
$ LIBVIRT_DEBUG=1 virsh
18:14:52.086: 17551: info : libvirt version: 0.8.7, package: 1.fc13 (Fedora Project, x86-01.phx2.fedoraproject.org, 01-27-2011-18:00:10)
18:14:52.086: 17551: debug : virInitialize:361 : register drivers
Thus when distro packagers receive bug reports they can clearly
see what version was in use, even if the bug reporter mistakenly
or intentionally lies about version/builds
* src/util/logging.c: Output version data prior to first log message
* libvirt.spec.in: Include RPM release, date, hostname & packager
* configure.ac: Add --with-packager & --with-packager-version args
2011-01-27 18:11:16 +00:00
|
|
|
}
|
2008-12-22 10:36:54 +00:00
|
|
|
virLogUnlock();
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2011-02-16 23:49:15 +00:00
|
|
|
errno = saved_errno;
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-05-09 14:18:56 +00:00
|
|
|
|
2020-02-16 20:22:18 +00:00
|
|
|
/**
|
|
|
|
* virLogMessage:
|
|
|
|
* @source: where is that message coming from
|
|
|
|
* @priority: the priority level
|
|
|
|
* @filename: file where the message was emitted
|
|
|
|
* @linenr: line where the message was emitted
|
|
|
|
* @funcname: the function emitting the (debug) message
|
|
|
|
* @metadata: NULL or metadata array, terminated by an item with NULL key
|
|
|
|
* @fmt: the string format
|
|
|
|
* @...: the arguments
|
|
|
|
*
|
|
|
|
* Call the libvirt logger with some information. Based on the configuration
|
|
|
|
* the message may be stored, sent to output or just discarded
|
|
|
|
*/
|
|
|
|
void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogMessage(virLogSource *source,
|
2020-02-16 20:22:18 +00:00
|
|
|
virLogPriority priority,
|
|
|
|
const char *filename,
|
|
|
|
int linenr,
|
|
|
|
const char *funcname,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata,
|
2020-02-16 20:22:18 +00:00
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
virLogVMessage(source, priority,
|
|
|
|
filename, linenr, funcname,
|
|
|
|
metadata, fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
static void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutputToFd(virLogSource *source G_GNUC_UNUSED,
|
2019-10-14 12:45:33 +00:00
|
|
|
virLogPriority priority G_GNUC_UNUSED,
|
|
|
|
const char *filename G_GNUC_UNUSED,
|
|
|
|
int linenr G_GNUC_UNUSED,
|
|
|
|
const char *funcname G_GNUC_UNUSED,
|
2012-09-27 13:14:01 +00:00
|
|
|
const char *timestamp,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata G_GNUC_UNUSED,
|
2019-10-14 12:45:33 +00:00
|
|
|
const char *rawstr G_GNUC_UNUSED,
|
2012-09-27 13:14:01 +00:00
|
|
|
const char *str,
|
|
|
|
void *data)
|
2011-09-28 13:20:07 +00:00
|
|
|
{
|
2012-01-25 20:13:25 +00:00
|
|
|
int fd = (intptr_t) data;
|
2011-09-28 13:20:07 +00:00
|
|
|
char *msg;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
|
|
|
if (fd < 0)
|
2012-09-20 18:24:00 +00:00
|
|
|
return;
|
2011-09-28 13:20:07 +00:00
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
msg = g_strdup_printf("%s: %s", timestamp, str);
|
2012-09-20 18:24:00 +00:00
|
|
|
ignore_value(safewrite(fd, msg, strlen(msg)));
|
2011-09-28 13:20:07 +00:00
|
|
|
VIR_FREE(msg);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
virLogCloseFd(void *data)
|
2012-06-07 13:16:50 +00:00
|
|
|
{
|
2012-01-25 20:13:25 +00:00
|
|
|
int fd = (intptr_t) data;
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2012-06-07 13:16:50 +00:00
|
|
|
VIR_LOG_CLOSE(fd);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogOutput *
|
2016-07-15 12:47:58 +00:00
|
|
|
virLogNewOutputToStderr(virLogPriority priority)
|
|
|
|
{
|
|
|
|
return virLogOutputNew(virLogOutputToFd, NULL, (void *)STDERR_FILENO,
|
|
|
|
priority, VIR_LOG_TO_STDERR, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogOutput *
|
2016-07-15 12:47:58 +00:00
|
|
|
virLogNewOutputToFile(virLogPriority priority,
|
|
|
|
const char *file)
|
|
|
|
{
|
|
|
|
int fd;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *ret = NULL;
|
2016-07-15 12:47:58 +00:00
|
|
|
|
|
|
|
fd = open(file, O_CREAT | O_APPEND | O_WRONLY, S_IRUSR | S_IWUSR);
|
2017-03-31 08:05:08 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
virReportSystemError(errno, _("failed to open %s"), file);
|
2016-07-15 12:47:58 +00:00
|
|
|
return NULL;
|
2017-03-31 08:05:08 +00:00
|
|
|
}
|
2016-07-15 12:47:58 +00:00
|
|
|
|
|
|
|
if (!(ret = virLogOutputNew(virLogOutputToFd, virLogCloseFd,
|
|
|
|
(void *)(intptr_t)fd,
|
|
|
|
priority, VIR_LOG_TO_FILE, file))) {
|
|
|
|
VIR_LOG_CLOSE(fd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H || USE_JOURNALD
|
2014-02-24 16:21:55 +00:00
|
|
|
|
|
|
|
/* Compat in case we build with journald, but no syslog */
|
|
|
|
# ifndef LOG_DEBUG
|
|
|
|
# define LOG_DEBUG 7
|
|
|
|
# endif
|
|
|
|
# ifndef LOG_INFO
|
|
|
|
# define LOG_INFO 6
|
|
|
|
# endif
|
|
|
|
# ifndef LOG_WARNING
|
|
|
|
# define LOG_WARNING 4
|
|
|
|
# endif
|
|
|
|
# ifndef LOG_ERR
|
|
|
|
# define LOG_ERR 3
|
|
|
|
# endif
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
static int
|
|
|
|
virLogPrioritySyslog(virLogPriority priority)
|
2012-09-27 11:34:04 +00:00
|
|
|
{
|
|
|
|
switch (priority) {
|
|
|
|
case VIR_LOG_DEBUG:
|
|
|
|
return LOG_DEBUG;
|
|
|
|
case VIR_LOG_INFO:
|
|
|
|
return LOG_INFO;
|
|
|
|
case VIR_LOG_WARN:
|
|
|
|
return LOG_WARNING;
|
|
|
|
case VIR_LOG_ERROR:
|
|
|
|
return LOG_ERR;
|
|
|
|
default:
|
|
|
|
return LOG_ERR;
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* WITH_SYSLOG_H || USE_JOURNALD */
|
2012-09-27 11:34:04 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H
|
2012-09-27 13:14:01 +00:00
|
|
|
static void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutputToSyslog(virLogSource *source G_GNUC_UNUSED,
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogPriority priority,
|
2019-10-14 12:45:33 +00:00
|
|
|
const char *filename G_GNUC_UNUSED,
|
|
|
|
int linenr G_GNUC_UNUSED,
|
|
|
|
const char *funcname G_GNUC_UNUSED,
|
|
|
|
const char *timestamp G_GNUC_UNUSED,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata G_GNUC_UNUSED,
|
2019-10-14 12:45:33 +00:00
|
|
|
const char *rawstr G_GNUC_UNUSED,
|
2012-09-27 13:14:01 +00:00
|
|
|
const char *str,
|
2019-10-14 12:45:33 +00:00
|
|
|
void *data G_GNUC_UNUSED)
|
2011-09-28 13:20:07 +00:00
|
|
|
{
|
2012-09-27 11:34:04 +00:00
|
|
|
syslog(virLogPrioritySyslog(priority), "%s", str);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2014-10-28 18:38:04 +00:00
|
|
|
static char *current_ident;
|
2009-01-20 21:34:44 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
|
|
|
static void
|
2019-10-14 12:45:33 +00:00
|
|
|
virLogCloseSyslog(void *data G_GNUC_UNUSED)
|
2012-09-27 13:14:01 +00:00
|
|
|
{
|
2008-12-22 10:36:54 +00:00
|
|
|
closelog();
|
2009-01-20 21:34:44 +00:00
|
|
|
VIR_FREE(current_ident);
|
2008-12-22 10:36:54 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogOutput *
|
2016-07-15 12:47:58 +00:00
|
|
|
virLogNewOutputToSyslog(virLogPriority priority,
|
|
|
|
const char *ident)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *ret = NULL;
|
2016-07-15 12:47:58 +00:00
|
|
|
int at = -1;
|
|
|
|
|
|
|
|
/* There are a couple of issues with syslog:
|
|
|
|
* 1) If we re-opened the connection by calling openlog now, it would change
|
|
|
|
* the message tag immediately which is not what we want, since we might be
|
|
|
|
* in the middle of parsing of a new set of outputs where anything still can
|
|
|
|
* go wrong and we would introduce an inconsistent state to the log. We're
|
|
|
|
* also not holding a lock on the logger if we tried to change the tag
|
|
|
|
* while other workers are actively logging.
|
|
|
|
*
|
|
|
|
* 2) Syslog keeps the open file descriptor private, so we can't just dup()
|
|
|
|
* it like we would do with files if an output already existed.
|
|
|
|
*
|
|
|
|
* If a syslog connection already exists changing the message tag has to be
|
|
|
|
* therefore special-cased and postponed until the very last moment.
|
|
|
|
*/
|
|
|
|
if ((at = virLogFindOutput(virLogOutputs, virLogNbOutputs,
|
|
|
|
VIR_LOG_TO_SYSLOG, NULL)) < 0) {
|
|
|
|
/*
|
|
|
|
* rather than copying @ident, syslog uses caller's reference instead
|
|
|
|
*/
|
|
|
|
VIR_FREE(current_ident);
|
2019-10-20 11:49:46 +00:00
|
|
|
current_ident = g_strdup(ident);
|
2016-07-15 12:47:58 +00:00
|
|
|
|
|
|
|
openlog(current_ident, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ret = virLogOutputNew(virLogOutputToSyslog, virLogCloseSyslog,
|
|
|
|
NULL, priority, VIR_LOG_TO_SYSLOG, ident))) {
|
|
|
|
if (at < 0) {
|
|
|
|
closelog();
|
|
|
|
VIR_FREE(current_ident);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-01 22:38:56 +00:00
|
|
|
# if USE_JOURNALD
|
2017-11-03 12:09:47 +00:00
|
|
|
# define IOVEC_SET(iov, data, size) \
|
|
|
|
do { \
|
|
|
|
struct iovec *_i = &(iov); \
|
|
|
|
_i->iov_base = (void*)(data); \
|
|
|
|
_i->iov_len = (size); \
|
2012-09-25 17:31:01 +00:00
|
|
|
} while (0)
|
|
|
|
|
2012-10-17 18:17:16 +00:00
|
|
|
# define IOVEC_SET_STRING(iov, str) IOVEC_SET(iov, str, strlen(str))
|
|
|
|
|
|
|
|
/* Used for conversion of numbers to strings, and for length of binary data */
|
2020-01-14 13:30:07 +00:00
|
|
|
# define JOURNAL_BUF_SIZE (MAX(VIR_INT64_STR_BUFLEN, sizeof(uint64_t)))
|
2012-10-17 18:17:16 +00:00
|
|
|
|
|
|
|
struct journalState
|
|
|
|
{
|
|
|
|
struct iovec *iov, *iov_end;
|
|
|
|
char (*bufs)[JOURNAL_BUF_SIZE], (*bufs_end)[JOURNAL_BUF_SIZE];
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
journalAddString(struct journalState *state, const char *field,
|
|
|
|
const char *value)
|
|
|
|
{
|
|
|
|
static const char newline = '\n', equals = '=';
|
|
|
|
|
|
|
|
if (strchr(value, '\n') != NULL) {
|
|
|
|
uint64_t nstr;
|
|
|
|
|
|
|
|
/* If 'str' contains a newline, then we must
|
|
|
|
* encode the string length, since we can't
|
|
|
|
* rely on the newline for the field separator
|
|
|
|
*/
|
|
|
|
if (state->iov_end - state->iov < 5 || state->bufs == state->bufs_end)
|
|
|
|
return; /* Silently drop */
|
|
|
|
nstr = htole64(strlen(value));
|
|
|
|
memcpy(state->bufs[0], &nstr, sizeof(nstr));
|
|
|
|
|
|
|
|
IOVEC_SET_STRING(state->iov[0], field);
|
|
|
|
IOVEC_SET(state->iov[1], &newline, sizeof(newline));
|
|
|
|
IOVEC_SET(state->iov[2], state->bufs[0], sizeof(nstr));
|
|
|
|
state->bufs++;
|
|
|
|
state->iov += 3;
|
|
|
|
} else {
|
|
|
|
if (state->iov_end - state->iov < 4)
|
|
|
|
return; /* Silently drop */
|
|
|
|
IOVEC_SET_STRING(state->iov[0], field);
|
|
|
|
IOVEC_SET(state->iov[1], (void *)&equals, sizeof(equals));
|
|
|
|
state->iov += 2;
|
|
|
|
}
|
|
|
|
IOVEC_SET_STRING(state->iov[0], value);
|
|
|
|
IOVEC_SET(state->iov[1], (void *)&newline, sizeof(newline));
|
|
|
|
state->iov += 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
journalAddInt(struct journalState *state, const char *field, int value)
|
|
|
|
{
|
|
|
|
static const char newline = '\n', equals = '=';
|
|
|
|
|
|
|
|
char *num;
|
|
|
|
|
|
|
|
if (state->iov_end - state->iov < 4 || state->bufs == state->bufs_end)
|
|
|
|
return; /* Silently drop */
|
|
|
|
|
|
|
|
num = virFormatIntDecimal(state->bufs[0], sizeof(state->bufs[0]), value);
|
|
|
|
|
|
|
|
IOVEC_SET_STRING(state->iov[0], field);
|
|
|
|
IOVEC_SET(state->iov[1], (void *)&equals, sizeof(equals));
|
|
|
|
IOVEC_SET_STRING(state->iov[2], num);
|
|
|
|
IOVEC_SET(state->iov[3], (void *)&newline, sizeof(newline));
|
|
|
|
state->bufs++;
|
|
|
|
state->iov += 4;
|
|
|
|
}
|
2012-09-25 17:31:01 +00:00
|
|
|
|
|
|
|
static void
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutputToJournald(virLogSource *source,
|
2012-09-25 17:31:01 +00:00
|
|
|
virLogPriority priority,
|
|
|
|
const char *filename,
|
|
|
|
int linenr,
|
|
|
|
const char *funcname,
|
2019-10-14 12:45:33 +00:00
|
|
|
const char *timestamp G_GNUC_UNUSED,
|
2021-03-11 07:16:13 +00:00
|
|
|
struct _virLogMetadata *metadata,
|
2012-09-25 17:31:01 +00:00
|
|
|
const char *rawstr,
|
2019-10-14 12:45:33 +00:00
|
|
|
const char *str G_GNUC_UNUSED,
|
2016-08-17 15:20:35 +00:00
|
|
|
void *data)
|
2012-09-25 17:31:01 +00:00
|
|
|
{
|
|
|
|
int buffd = -1;
|
2016-08-17 15:20:35 +00:00
|
|
|
int journalfd = (intptr_t) data;
|
2012-09-25 17:31:01 +00:00
|
|
|
struct msghdr mh;
|
|
|
|
struct sockaddr_un sa;
|
|
|
|
union {
|
|
|
|
struct cmsghdr cmsghdr;
|
|
|
|
uint8_t buf[CMSG_SPACE(sizeof(int))];
|
|
|
|
} control;
|
|
|
|
struct cmsghdr *cmsg;
|
|
|
|
/* We use /dev/shm instead of /tmp here, since we want this to
|
|
|
|
* be a tmpfs, and one that is available from early boot on
|
|
|
|
* and where unprivileged users can create files. */
|
|
|
|
char path[] = "/dev/shm/journal.XXXXXX";
|
2014-02-21 17:21:55 +00:00
|
|
|
size_t nmetadata = 0;
|
2012-09-25 17:31:01 +00:00
|
|
|
|
2014-02-21 17:21:55 +00:00
|
|
|
# define NUM_FIELDS_CORE 6
|
|
|
|
# define NUM_FIELDS_META 5
|
|
|
|
# define NUM_FIELDS (NUM_FIELDS_CORE + NUM_FIELDS_META)
|
2012-10-17 18:17:16 +00:00
|
|
|
struct iovec iov[NUM_FIELDS * 5];
|
|
|
|
char iov_bufs[NUM_FIELDS][JOURNAL_BUF_SIZE];
|
|
|
|
struct journalState state;
|
2012-09-25 17:31:01 +00:00
|
|
|
|
2012-10-17 18:17:16 +00:00
|
|
|
state.iov = iov;
|
2019-10-15 11:55:26 +00:00
|
|
|
state.iov_end = iov + G_N_ELEMENTS(iov);
|
2012-10-17 18:17:16 +00:00
|
|
|
state.bufs = iov_bufs;
|
2019-10-15 11:55:26 +00:00
|
|
|
state.bufs_end = iov_bufs + G_N_ELEMENTS(iov_bufs);
|
2012-09-25 17:31:01 +00:00
|
|
|
|
2013-11-19 23:00:32 +00:00
|
|
|
journalAddString(&state, "MESSAGE", rawstr);
|
2014-02-24 16:21:55 +00:00
|
|
|
journalAddInt(&state, "PRIORITY",
|
|
|
|
virLogPrioritySyslog(priority));
|
2015-09-21 18:06:55 +00:00
|
|
|
journalAddInt(&state, "SYSLOG_FACILITY", LOG_DAEMON);
|
2014-02-27 17:44:53 +00:00
|
|
|
journalAddString(&state, "LIBVIRT_SOURCE", source->name);
|
2013-08-02 11:15:57 +00:00
|
|
|
if (filename)
|
|
|
|
journalAddString(&state, "CODE_FILE", filename);
|
2012-10-17 18:17:16 +00:00
|
|
|
journalAddInt(&state, "CODE_LINE", linenr);
|
2013-08-02 11:15:57 +00:00
|
|
|
if (funcname)
|
|
|
|
journalAddString(&state, "CODE_FUNC", funcname);
|
2014-02-21 17:21:55 +00:00
|
|
|
if (metadata != NULL) {
|
|
|
|
while (metadata->key != NULL &&
|
|
|
|
nmetadata < NUM_FIELDS_META) {
|
|
|
|
if (metadata->s != NULL)
|
|
|
|
journalAddString(&state, metadata->key, metadata->s);
|
|
|
|
else
|
|
|
|
journalAddInt(&state, metadata->key, metadata->iv);
|
|
|
|
metadata++;
|
|
|
|
nmetadata++;
|
|
|
|
}
|
|
|
|
}
|
2012-09-25 17:31:01 +00:00
|
|
|
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sun_family = AF_UNIX;
|
2018-07-20 07:50:37 +00:00
|
|
|
if (virStrcpyStatic(sa.sun_path, "/run/systemd/journal/socket") < 0)
|
2012-09-25 17:31:01 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
memset(&mh, 0, sizeof(mh));
|
|
|
|
mh.msg_name = &sa;
|
|
|
|
mh.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path);
|
|
|
|
mh.msg_iov = iov;
|
2012-10-17 18:17:16 +00:00
|
|
|
mh.msg_iovlen = state.iov - iov;
|
2012-09-25 17:31:01 +00:00
|
|
|
|
|
|
|
if (sendmsg(journalfd, &mh, MSG_NOSIGNAL) >= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (errno != EMSGSIZE && errno != ENOBUFS)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Message was too large, so dump to temporary file
|
|
|
|
* and pass an FD to the journal
|
|
|
|
*/
|
|
|
|
|
2019-11-18 16:45:54 +00:00
|
|
|
if ((buffd = g_mkstemp_full(path, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR)) < 0)
|
2012-09-25 17:31:01 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (unlink(path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-10-17 18:17:16 +00:00
|
|
|
if (writev(buffd, iov, state.iov - iov) < 0)
|
2012-09-25 17:31:01 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
mh.msg_iov = NULL;
|
|
|
|
mh.msg_iovlen = 0;
|
|
|
|
|
|
|
|
memset(&control, 0, sizeof(control));
|
|
|
|
mh.msg_control = &control;
|
|
|
|
mh.msg_controllen = sizeof(control);
|
|
|
|
|
|
|
|
cmsg = CMSG_FIRSTHDR(&mh);
|
|
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
|
|
|
memcpy(CMSG_DATA(cmsg), &buffd, sizeof(int));
|
|
|
|
|
|
|
|
mh.msg_controllen = cmsg->cmsg_len;
|
|
|
|
|
2014-03-25 16:57:58 +00:00
|
|
|
ignore_value(sendmsg(journalfd, &mh, MSG_NOSIGNAL));
|
2012-09-25 17:31:01 +00:00
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2012-09-25 17:31:01 +00:00
|
|
|
VIR_LOG_CLOSE(buffd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-11 07:16:13 +00:00
|
|
|
static virLogOutput *
|
2016-07-15 12:47:58 +00:00
|
|
|
virLogNewOutputToJournald(int priority)
|
|
|
|
{
|
|
|
|
int journalfd;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *ret = NULL;
|
2016-07-15 12:47:58 +00:00
|
|
|
|
|
|
|
if ((journalfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (virSetInherit(journalfd, false) < 0) {
|
|
|
|
VIR_LOG_CLOSE(journalfd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ret = virLogOutputNew(virLogOutputToJournald, virLogCloseFd,
|
|
|
|
(void *)(intptr_t) journalfd, priority,
|
|
|
|
VIR_LOG_TO_JOURNALD, NULL))) {
|
|
|
|
VIR_LOG_CLOSE(journalfd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2012-10-01 22:38:56 +00:00
|
|
|
# endif /* USE_JOURNALD */
|
2013-06-07 09:55:27 +00:00
|
|
|
|
|
|
|
int virLogPriorityFromSyslog(int priority)
|
|
|
|
{
|
|
|
|
switch (priority) {
|
|
|
|
case LOG_EMERG:
|
|
|
|
case LOG_ALERT:
|
|
|
|
case LOG_CRIT:
|
|
|
|
case LOG_ERR:
|
|
|
|
return VIR_LOG_ERROR;
|
|
|
|
case LOG_WARNING:
|
|
|
|
case LOG_NOTICE:
|
|
|
|
return VIR_LOG_WARN;
|
|
|
|
case LOG_INFO:
|
|
|
|
return VIR_LOG_INFO;
|
|
|
|
case LOG_DEBUG:
|
|
|
|
return VIR_LOG_DEBUG;
|
|
|
|
}
|
|
|
|
return VIR_LOG_ERROR;
|
|
|
|
}
|
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#else /* WITH_SYSLOG_H */
|
2019-10-14 12:45:33 +00:00
|
|
|
int virLogPriorityFromSyslog(int priority G_GNUC_UNUSED)
|
2013-06-07 09:55:27 +00:00
|
|
|
{
|
|
|
|
return VIR_LOG_ERROR;
|
|
|
|
}
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* WITH_SYSLOG_H */
|
2008-12-22 10:36:54 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-08-06 13:45:50 +00:00
|
|
|
/**
|
|
|
|
* virLogGetDefaultPriority:
|
|
|
|
*
|
|
|
|
* Returns the current logging priority level.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
virLogPriority
|
|
|
|
virLogGetDefaultPriority(void)
|
|
|
|
{
|
2011-01-21 16:30:17 +00:00
|
|
|
return virLogDefaultPriority;
|
2009-08-06 13:45:50 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-10-08 15:05:01 +00:00
|
|
|
/**
|
|
|
|
* virLogGetFilters:
|
|
|
|
*
|
|
|
|
* Returns a string listing the current filters, in the format originally
|
|
|
|
* specified in the config file or environment. Caller must free the
|
|
|
|
* result.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
char *
|
|
|
|
virLogGetFilters(void)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2020-07-03 02:30:20 +00:00
|
|
|
g_auto(virBuffer) filterbuf = VIR_BUFFER_INITIALIZER;
|
2009-10-08 15:05:01 +00:00
|
|
|
|
|
|
|
virLogLock();
|
|
|
|
for (i = 0; i < virLogNbFilters; i++) {
|
2012-05-09 14:18:56 +00:00
|
|
|
const char *sep = ":";
|
|
|
|
virBufferAsprintf(&filterbuf, "%d%s%s ",
|
2016-03-29 13:54:08 +00:00
|
|
|
virLogFilters[i]->priority,
|
2012-05-09 14:18:56 +00:00
|
|
|
sep,
|
2016-03-29 13:54:08 +00:00
|
|
|
virLogFilters[i]->match);
|
2009-10-08 15:05:01 +00:00
|
|
|
}
|
|
|
|
virLogUnlock();
|
|
|
|
|
|
|
|
return virBufferContentAndReset(&filterbuf);
|
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-10-08 15:05:01 +00:00
|
|
|
/**
|
|
|
|
* virLogGetOutputs:
|
|
|
|
*
|
|
|
|
* Returns a string listing the current outputs, in the format originally
|
|
|
|
* specified in the config file or environment. Caller must free the
|
|
|
|
* result.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
char *
|
|
|
|
virLogGetOutputs(void)
|
|
|
|
{
|
Convert 'int i' to 'size_t i' in src/util/ files
Convert the type of loop iterators named 'i', 'j', k',
'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or
'unsigned int', also santizing 'ii', 'jj', 'kk' to use
the normal 'i', 'j', 'k' naming
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2013-07-08 14:09:33 +00:00
|
|
|
size_t i;
|
2020-07-03 02:30:20 +00:00
|
|
|
g_auto(virBuffer) outputbuf = VIR_BUFFER_INITIALIZER;
|
2009-10-08 15:05:01 +00:00
|
|
|
|
|
|
|
virLogLock();
|
|
|
|
for (i = 0; i < virLogNbOutputs; i++) {
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogDestination dest = virLogOutputs[i]->dest;
|
2009-10-08 15:05:01 +00:00
|
|
|
if (i)
|
2013-05-07 10:28:50 +00:00
|
|
|
virBufferAddChar(&outputbuf, ' ');
|
2009-10-08 15:05:01 +00:00
|
|
|
switch (dest) {
|
|
|
|
case VIR_LOG_TO_SYSLOG:
|
|
|
|
case VIR_LOG_TO_FILE:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&outputbuf, "%d:%s:%s",
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogOutputs[i]->priority,
|
2016-03-15 20:35:17 +00:00
|
|
|
virLogDestinationTypeToString(dest),
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogOutputs[i]->name);
|
2009-10-08 15:05:01 +00:00
|
|
|
break;
|
2018-02-14 09:43:59 +00:00
|
|
|
case VIR_LOG_TO_STDERR:
|
|
|
|
case VIR_LOG_TO_JOURNALD:
|
2011-04-30 16:34:49 +00:00
|
|
|
virBufferAsprintf(&outputbuf, "%d:%s",
|
2016-03-17 13:52:57 +00:00
|
|
|
virLogOutputs[i]->priority,
|
2016-03-15 20:35:17 +00:00
|
|
|
virLogDestinationTypeToString(dest));
|
2018-02-14 09:43:59 +00:00
|
|
|
break;
|
|
|
|
case VIR_LOG_TO_OUTPUT_LAST:
|
|
|
|
default:
|
|
|
|
virReportEnumRangeError(virLogDestination, dest);
|
|
|
|
goto error;
|
2009-10-08 15:05:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 09:43:59 +00:00
|
|
|
virLogUnlock();
|
2009-10-08 15:05:01 +00:00
|
|
|
return virBufferContentAndReset(&outputbuf);
|
2018-02-14 09:43:59 +00:00
|
|
|
|
|
|
|
error:
|
|
|
|
virLogUnlock();
|
|
|
|
return NULL;
|
2009-10-08 15:05:01 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-08-06 13:45:50 +00:00
|
|
|
/**
|
|
|
|
* virLogGetNbFilters:
|
|
|
|
*
|
|
|
|
* Returns the current number of defined log filters.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
int
|
|
|
|
virLogGetNbFilters(void)
|
|
|
|
{
|
2011-01-21 16:30:17 +00:00
|
|
|
return virLogNbFilters;
|
2009-08-06 13:45:50 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-08-06 13:45:50 +00:00
|
|
|
/**
|
|
|
|
* virLogGetNbOutputs:
|
|
|
|
*
|
|
|
|
* Returns the current number of defined log outputs.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
int
|
|
|
|
virLogGetNbOutputs(void)
|
|
|
|
{
|
2011-01-21 16:30:17 +00:00
|
|
|
return virLogNbOutputs;
|
2009-08-06 13:45:50 +00:00
|
|
|
}
|
2009-08-06 13:55:07 +00:00
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-08-06 13:55:07 +00:00
|
|
|
/**
|
|
|
|
* virLogParseDefaultPriority:
|
2016-03-21 13:35:37 +00:00
|
|
|
* @priority: string defining the desired logging level (either a numeric or a
|
|
|
|
* word form, see below)
|
2009-08-06 13:55:07 +00:00
|
|
|
*
|
2016-03-21 13:35:37 +00:00
|
|
|
* Parses the desired log priority level. The input @priority shall conform to
|
|
|
|
* one of the following levels:
|
|
|
|
* 1 = DEBUG
|
|
|
|
* 2 = INFO
|
|
|
|
* 3 = WARNING
|
|
|
|
* 4 = ERROR
|
2009-08-06 13:55:07 +00:00
|
|
|
*
|
2016-03-21 13:35:37 +00:00
|
|
|
*
|
|
|
|
* Returns the corresponding priority enum on success, -1 in case of error. A
|
|
|
|
* call to virLogSetDefaultPriority should be issued upon returning from this
|
|
|
|
* function.
|
2009-08-06 13:55:07 +00:00
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
int
|
|
|
|
virLogParseDefaultPriority(const char *priority)
|
|
|
|
{
|
2009-08-06 13:55:07 +00:00
|
|
|
if (STREQ(priority, "1") || STREQ(priority, "debug"))
|
2016-03-21 13:35:37 +00:00
|
|
|
return VIR_LOG_DEBUG;
|
2009-08-06 13:55:07 +00:00
|
|
|
else if (STREQ(priority, "2") || STREQ(priority, "info"))
|
2016-03-21 13:35:37 +00:00
|
|
|
return VIR_LOG_INFO;
|
2009-08-06 13:55:07 +00:00
|
|
|
else if (STREQ(priority, "3") || STREQ(priority, "warning"))
|
2016-03-21 13:35:37 +00:00
|
|
|
return VIR_LOG_WARN;
|
2009-08-06 13:55:07 +00:00
|
|
|
else if (STREQ(priority, "4") || STREQ(priority, "error"))
|
2016-03-21 13:35:37 +00:00
|
|
|
return VIR_LOG_ERROR;
|
|
|
|
return -1;
|
2009-08-06 13:55:07 +00:00
|
|
|
}
|
|
|
|
|
2012-09-27 13:14:01 +00:00
|
|
|
|
2009-08-06 13:55:07 +00:00
|
|
|
/**
|
|
|
|
* virLogSetFromEnv:
|
|
|
|
*
|
|
|
|
* Sets virLogDefaultPriority, virLogFilters and virLogOutputs based on
|
|
|
|
* environment variables.
|
|
|
|
*/
|
2012-09-27 13:14:01 +00:00
|
|
|
void
|
|
|
|
virLogSetFromEnv(void)
|
|
|
|
{
|
2013-10-09 10:18:15 +00:00
|
|
|
const char *debugEnv;
|
2009-08-06 13:55:07 +00:00
|
|
|
|
2013-10-11 16:07:54 +00:00
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return;
|
|
|
|
|
2019-08-01 12:35:56 +00:00
|
|
|
debugEnv = getenv("LIBVIRT_DEBUG");
|
2009-08-06 13:55:07 +00:00
|
|
|
if (debugEnv && *debugEnv)
|
2016-03-21 13:35:37 +00:00
|
|
|
virLogSetDefaultPriority(virLogParseDefaultPriority(debugEnv));
|
2019-08-01 12:35:56 +00:00
|
|
|
debugEnv = getenv("LIBVIRT_LOG_FILTERS");
|
2009-08-06 13:55:07 +00:00
|
|
|
if (debugEnv && *debugEnv)
|
2016-03-29 14:48:08 +00:00
|
|
|
virLogSetFilters(debugEnv);
|
2019-08-01 12:35:56 +00:00
|
|
|
debugEnv = getenv("LIBVIRT_LOG_OUTPUTS");
|
2009-08-06 13:55:07 +00:00
|
|
|
if (debugEnv && *debugEnv)
|
2016-03-17 14:26:40 +00:00
|
|
|
virLogSetOutputs(debugEnv);
|
2009-08-06 13:55:07 +00:00
|
|
|
}
|
2013-03-04 20:46:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns a true value if the first line in @str is
|
|
|
|
* probably a log message generated by the libvirt
|
|
|
|
* logging layer
|
|
|
|
*/
|
|
|
|
bool virLogProbablyLogMessage(const char *str)
|
|
|
|
{
|
|
|
|
if (!virLogRegex)
|
|
|
|
return false;
|
2019-11-13 14:38:58 +00:00
|
|
|
if (g_regex_match(virLogRegex, str, 0, NULL))
|
|
|
|
return true;
|
|
|
|
return false;
|
2013-03-04 20:46:32 +00:00
|
|
|
}
|
2016-07-08 11:46:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogOutputNew:
|
|
|
|
* @f: the function to call to output a message
|
|
|
|
* @c: the function to call to close the output (or NULL)
|
|
|
|
* @data: extra data passed as first arg to functions @f and @c
|
|
|
|
* @priority: minimal priority for this filter, use 0 for none
|
|
|
|
* @dest: where to send output of this priority (see virLogDestination)
|
|
|
|
* @name: additional data associated with syslog and file-based outputs (ident
|
|
|
|
* and filename respectively)
|
|
|
|
*
|
|
|
|
* Allocates and returns a new log output object. The object has to be later
|
|
|
|
* defined, so that the output will be taken into account when emitting a
|
|
|
|
* message.
|
|
|
|
*
|
|
|
|
* Returns reference to a newly created object or NULL in case of failure.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *
|
2016-07-08 11:46:36 +00:00
|
|
|
virLogOutputNew(virLogOutputFunc f,
|
|
|
|
virLogCloseFunc c,
|
|
|
|
void *data,
|
|
|
|
virLogPriority priority,
|
|
|
|
virLogDestination dest,
|
|
|
|
const char *name)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *ret = NULL;
|
2016-07-08 11:46:36 +00:00
|
|
|
char *ndup = NULL;
|
|
|
|
|
|
|
|
if (dest == VIR_LOG_TO_SYSLOG || dest == VIR_LOG_TO_FILE) {
|
|
|
|
if (!name) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
|
|
_("Missing auxiliary data in output definition"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
ndup = g_strdup(name);
|
2016-07-08 11:46:36 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 17:12:37 +00:00
|
|
|
ret = g_new0(virLogOutput, 1);
|
2016-07-08 11:46:36 +00:00
|
|
|
|
|
|
|
ret->logInitMessage = true;
|
|
|
|
ret->f = f;
|
|
|
|
ret->c = c;
|
|
|
|
ret->data = data;
|
|
|
|
ret->priority = priority;
|
|
|
|
ret->dest = dest;
|
|
|
|
ret->name = ndup;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2016-07-08 12:15:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogFilterNew:
|
|
|
|
* @match: the pattern to match
|
|
|
|
* @priority: the priority to give to messages matching the pattern
|
|
|
|
*
|
|
|
|
* Allocates and returns a new log filter object. The object has to be later
|
|
|
|
* defined, so that the pattern will be taken into account when executing the
|
|
|
|
* log filters (to select or reject a particular message) on messages.
|
|
|
|
*
|
|
|
|
* The filter defines a rules that will apply only to messages matching
|
|
|
|
* the pattern (currently if @match is a substring of the message category)
|
|
|
|
*
|
|
|
|
* Returns a reference to a newly created filter that needs to be defined using
|
|
|
|
* virLogDefineFilters, or NULL in case of an error.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilter *
|
2016-07-08 12:15:43 +00:00
|
|
|
virLogFilterNew(const char *match,
|
2019-10-02 10:51:51 +00:00
|
|
|
virLogPriority priority)
|
2016-07-08 12:15:43 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilter *ret = NULL;
|
2018-05-14 13:51:14 +00:00
|
|
|
size_t mlen = strlen(match);
|
2016-07-08 12:15:43 +00:00
|
|
|
|
|
|
|
if (priority < VIR_LOG_DEBUG || priority > VIR_LOG_ERROR) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG, _("Invalid log priority %d"),
|
|
|
|
priority);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-09-11 11:42:04 +00:00
|
|
|
ret = g_new0(virLogFilter, 1);
|
|
|
|
ret->priority = priority;
|
|
|
|
|
2019-12-20 16:02:49 +00:00
|
|
|
/* We must treat 'foo' as equiv to '*foo*' for g_pattern_match
|
2018-05-14 13:51:14 +00:00
|
|
|
* todo substring matches, so add 2 extra bytes
|
|
|
|
*/
|
2020-09-11 11:42:04 +00:00
|
|
|
ret->match = g_new0(char, mlen + 3);
|
|
|
|
ret->match[0] = '*';
|
|
|
|
memcpy(ret->match + 1, match, mlen);
|
|
|
|
ret->match[mlen + 1] = '*';
|
2016-07-08 12:15:43 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2016-03-30 10:22:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogFindOutput:
|
|
|
|
* @outputs: a list of outputs where to look for the output of type @dest
|
|
|
|
* @noutputs: number of elements in @outputs
|
|
|
|
* @dest: destination type of an output
|
|
|
|
* @opaque: opaque data to the method (only filename at the moment)
|
|
|
|
*
|
|
|
|
* Looks for an output of destination type @dest in the source list @outputs.
|
|
|
|
* If such an output exists, index of the object in the list is returned.
|
|
|
|
* In case of the destination being of type FILE also a comparison of the
|
|
|
|
* output's filename with @opaque is performed first.
|
|
|
|
*
|
|
|
|
* Returns the index of the object in the list or -1 if no object matching the
|
|
|
|
* specified @dest type and/or @opaque data one was found.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFindOutput(virLogOutput **outputs, size_t noutputs,
|
2016-03-30 10:22:15 +00:00
|
|
|
virLogDestination dest, const void *opaque)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
const char *name = opaque;
|
|
|
|
|
|
|
|
for (i = 0; i < noutputs; i++) {
|
|
|
|
if (dest == outputs[i]->dest &&
|
|
|
|
(STREQ_NULLABLE(outputs[i]->name, name)))
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
2016-03-17 14:18:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogDefineOutputs:
|
|
|
|
* @outputs: new set of outputs to be defined
|
|
|
|
* @noutputs: number of outputs in @outputs
|
|
|
|
*
|
|
|
|
* Resets any existing set of outputs and defines a completely new one.
|
|
|
|
*
|
|
|
|
* Returns number of outputs successfully defined or -1 in case of error;
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogDefineOutputs(virLogOutput **outputs, size_t noutputs)
|
2016-03-17 14:18:06 +00:00
|
|
|
{
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H
|
2016-03-30 10:38:39 +00:00
|
|
|
int id;
|
|
|
|
char *tmp = NULL;
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* WITH_SYSLOG_H */
|
2016-03-30 10:38:39 +00:00
|
|
|
|
2016-03-17 14:18:06 +00:00
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virLogLock();
|
|
|
|
virLogResetOutputs();
|
2016-03-30 10:38:39 +00:00
|
|
|
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H
|
2016-03-30 10:38:39 +00:00
|
|
|
/* syslog needs to be special-cased, since it keeps the fd in private */
|
|
|
|
if ((id = virLogFindOutput(outputs, noutputs, VIR_LOG_TO_SYSLOG,
|
|
|
|
current_ident)) != -1) {
|
|
|
|
/* nothing can go wrong now (except for malloc) and since we're also
|
|
|
|
* holding the lock so it's safe to call openlog and change the message
|
|
|
|
* tag
|
|
|
|
*/
|
2019-10-20 11:49:46 +00:00
|
|
|
tmp = g_strdup(outputs[id]->name);
|
2016-03-30 10:38:39 +00:00
|
|
|
VIR_FREE(current_ident);
|
|
|
|
current_ident = tmp;
|
|
|
|
openlog(current_ident, 0, 0);
|
|
|
|
}
|
2020-09-01 11:27:44 +00:00
|
|
|
#endif /* WITH_SYSLOG_H */
|
2016-03-30 10:38:39 +00:00
|
|
|
|
2016-03-17 14:18:06 +00:00
|
|
|
virLogOutputs = outputs;
|
|
|
|
virLogNbOutputs = noutputs;
|
|
|
|
|
2016-03-30 10:38:39 +00:00
|
|
|
virLogUnlock();
|
2016-10-14 02:09:03 +00:00
|
|
|
return 0;
|
2016-03-17 14:18:06 +00:00
|
|
|
}
|
2016-03-29 15:04:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogDefineFilters:
|
|
|
|
* @filters: new set of filters to be defined
|
|
|
|
* @nfilters: number of filters in @filters
|
|
|
|
*
|
|
|
|
* Resets any existing set of filters and defines a completely new one. The
|
|
|
|
* resulting set can also be empty in which case NULL should be passed to
|
|
|
|
* @filters.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogDefineFilters(virLogFilter **filters, size_t nfilters)
|
2016-03-29 15:04:36 +00:00
|
|
|
{
|
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
virLogLock();
|
|
|
|
virLogResetFilters();
|
|
|
|
virLogFilters = filters;
|
|
|
|
virLogNbFilters = nfilters;
|
|
|
|
virLogUnlock();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-08-16 14:27:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogParseOutput:
|
|
|
|
* @src: string defining a single output
|
|
|
|
*
|
|
|
|
* The format of @src should be one of the following:
|
|
|
|
* x:stderr - output is sent to stderr
|
|
|
|
* x:journald - output is sent to journald
|
|
|
|
* x:syslog:name - output is sent to syslog using 'name' as the message tag
|
|
|
|
* x:file:abs_file_path - output is sent to file specified by 'abs_file_path'
|
|
|
|
*
|
|
|
|
* 'x' - minimal priority level which acts as a filter meaning that only
|
|
|
|
* messages with priority level greater than or equal to 'x' will be
|
|
|
|
* sent to output @src; supported values for 'x' are as follows:
|
|
|
|
* 1: DEBUG
|
|
|
|
* 2: INFO
|
|
|
|
* 3: WARNING
|
|
|
|
* 4: ERROR
|
|
|
|
*
|
|
|
|
* Parses @src string into a logging object type. If running in setuid mode,
|
|
|
|
* then only destination of type 'stderr' is permitted.
|
|
|
|
*
|
|
|
|
* Returns a newly created logging object from @src on success or NULL in case
|
|
|
|
* of an error.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *
|
2016-08-16 14:27:47 +00:00
|
|
|
virLogParseOutput(const char *src)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *ret = NULL;
|
2021-03-22 16:47:41 +00:00
|
|
|
g_auto(GStrv) tokens = NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
char *abspath = NULL;
|
|
|
|
size_t count = 0;
|
|
|
|
virLogPriority prio;
|
|
|
|
int dest;
|
|
|
|
|
|
|
|
VIR_DEBUG("output=%s", src);
|
|
|
|
|
|
|
|
/* split our format prio:destination:additional_data to tokens and parse
|
|
|
|
* them individually
|
|
|
|
*/
|
2021-03-22 17:02:28 +00:00
|
|
|
if (!(tokens = g_strsplit(src, ":", 0)) ||
|
|
|
|
(count = g_strv_length(tokens)) < 2) {
|
2016-08-16 14:27:47 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Malformed format for output '%s'"), src);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_uip(tokens[0], NULL, 10, &prio) < 0 ||
|
|
|
|
(prio < VIR_LOG_DEBUG) || (prio > VIR_LOG_ERROR)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Invalid priority '%s' for output '%s'"),
|
|
|
|
tokens[0], src);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((dest = virLogDestinationTypeFromString(tokens[1])) < 0) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Invalid destination '%s' for output '%s'"),
|
|
|
|
tokens[1], src);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (((dest == VIR_LOG_TO_STDERR ||
|
|
|
|
dest == VIR_LOG_TO_JOURNALD) && count != 2) ||
|
|
|
|
((dest == VIR_LOG_TO_FILE ||
|
|
|
|
dest == VIR_LOG_TO_SYSLOG) && count != 3)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Output '%s' does not meet the format requirements "
|
|
|
|
"for destination type '%s'"), src, tokens[1]);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch ((virLogDestination) dest) {
|
|
|
|
case VIR_LOG_TO_STDERR:
|
|
|
|
ret = virLogNewOutputToStderr(prio);
|
|
|
|
break;
|
|
|
|
case VIR_LOG_TO_SYSLOG:
|
2020-09-01 11:27:44 +00:00
|
|
|
#if WITH_SYSLOG_H
|
2016-08-16 14:27:47 +00:00
|
|
|
ret = virLogNewOutputToSyslog(prio, tokens[2]);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case VIR_LOG_TO_FILE:
|
|
|
|
if (virFileAbsPath(tokens[2], &abspath) < 0)
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-08-16 14:27:47 +00:00
|
|
|
ret = virLogNewOutputToFile(prio, abspath);
|
|
|
|
VIR_FREE(abspath);
|
|
|
|
break;
|
|
|
|
case VIR_LOG_TO_JOURNALD:
|
|
|
|
#if USE_JOURNALD
|
|
|
|
ret = virLogNewOutputToJournald(prio);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case VIR_LOG_TO_OUTPUT_LAST:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2016-10-05 12:41:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogParseFilter:
|
|
|
|
* @src: string defining a single filter
|
|
|
|
*
|
2019-10-02 10:51:51 +00:00
|
|
|
* The format of @src should be:
|
2016-10-05 12:41:51 +00:00
|
|
|
* x:name - filter affecting all modules which match 'name'
|
|
|
|
* 'name' - match string which either matches a name of a directory in
|
|
|
|
* libvirt's source tree which in turn affects all modules in
|
|
|
|
* that directory or it can matches a specific module within a
|
|
|
|
* directory, e.g. 'util.file' will only affect messages from
|
|
|
|
* module virfile.c inside src/util/ directory
|
|
|
|
* 'x' - minimal priority level which acts as a filter meaning that only
|
|
|
|
* messages with priority level greater than or equal to 'x' will be
|
|
|
|
* sent to output; supported values for 'x' are as follows:
|
|
|
|
* 1: DEBUG
|
|
|
|
* 2: INFO
|
|
|
|
* 3: WARNING
|
|
|
|
* 4: ERROR
|
|
|
|
*
|
|
|
|
* Parses @src string into a logging object type.
|
|
|
|
*
|
|
|
|
* Returns a newly created logging object from @src on success or NULL in case
|
|
|
|
* of an error.
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilter *
|
2016-10-05 12:41:51 +00:00
|
|
|
virLogParseFilter(const char *src)
|
|
|
|
{
|
|
|
|
virLogPriority prio;
|
2021-03-22 16:47:41 +00:00
|
|
|
g_auto(GStrv) tokens = NULL;
|
2016-10-05 12:41:51 +00:00
|
|
|
char *match = NULL;
|
|
|
|
|
|
|
|
VIR_DEBUG("filter=%s", src);
|
|
|
|
|
|
|
|
/* split our format prio:match_str to tokens and parse them individually */
|
2021-03-22 16:59:14 +00:00
|
|
|
if (!(tokens = g_strsplit(src, ":", 0)) ||
|
|
|
|
!tokens[0] || !tokens[1]) {
|
2016-10-05 12:41:51 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Malformed format for filter '%s'"), src);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-10-05 12:41:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_uip(tokens[0], NULL, 10, &prio) < 0 ||
|
|
|
|
(prio < VIR_LOG_DEBUG) || (prio > VIR_LOG_ERROR)) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
2018-04-03 07:50:59 +00:00
|
|
|
_("Invalid priority '%s' for filter '%s'"),
|
2016-10-05 12:41:51 +00:00
|
|
|
tokens[0], src);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-10-05 12:41:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
match = tokens[1];
|
|
|
|
if (match[0] == '+') {
|
2019-10-02 10:51:51 +00:00
|
|
|
/* '+' used to indicate printing a stack trace,
|
|
|
|
* but we dropped that feature, so just chomp
|
|
|
|
* that leading '+' */
|
2016-10-05 12:41:51 +00:00
|
|
|
match++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* match string cannot comprise just from a single '+' */
|
|
|
|
if (!*match) {
|
|
|
|
virReportError(VIR_ERR_INVALID_ARG,
|
|
|
|
_("Invalid match string '%s'"), tokens[1]);
|
2021-03-22 16:52:55 +00:00
|
|
|
return NULL;
|
2016-10-05 12:41:51 +00:00
|
|
|
}
|
|
|
|
|
2021-03-22 16:52:55 +00:00
|
|
|
return virLogFilterNew(match, prio);
|
2016-10-05 12:41:51 +00:00
|
|
|
}
|
2016-10-05 14:29:15 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogParseOutputs:
|
|
|
|
* @src: string defining a (set of) output(s)
|
|
|
|
* @outputs: user-supplied list where parsed outputs from @src shall be stored
|
|
|
|
*
|
|
|
|
* Parses a (set of) output(s) into a list of logging objects. Multiple outputs
|
|
|
|
* can be defined within @src string, they just need to be separated by spaces.
|
|
|
|
* If running in setuid mode, then only the 'stderr' output will be allowed.
|
|
|
|
*
|
|
|
|
* Returns the number of outputs parsed or -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogParseOutputs(const char *src, virLogOutput ***outputs)
|
2016-10-05 14:29:15 +00:00
|
|
|
{
|
|
|
|
int at = -1;
|
|
|
|
size_t noutputs = 0;
|
2021-03-22 16:47:41 +00:00
|
|
|
g_auto(GStrv) strings = NULL;
|
2021-03-22 16:38:24 +00:00
|
|
|
GStrv next;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput *output = NULL;
|
|
|
|
virLogOutput **list = NULL;
|
2016-10-05 14:29:15 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("outputs=%s", src);
|
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
if (!(strings = g_strsplit(src, " ", 0)))
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:29:15 +00:00
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
for (next = strings; *next; next++) {
|
|
|
|
/* g_strsplit may return empty strings */
|
|
|
|
if (STREQ(*next, ""))
|
2016-10-05 14:29:15 +00:00
|
|
|
continue;
|
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
if (!(output = virLogParseOutput(*next)))
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:29:15 +00:00
|
|
|
|
|
|
|
/* let's check if a duplicate output does not already exist in which
|
|
|
|
* case we need to replace it with its last occurrence, however, rather
|
|
|
|
* than first deleting the duplicate and then adding the new one, the
|
|
|
|
* new output object is added first so in case of an error we don't
|
|
|
|
* lose the old entry
|
|
|
|
*/
|
|
|
|
at = virLogFindOutput(list, noutputs, output->dest, output->name);
|
|
|
|
if (VIR_APPEND_ELEMENT(list, noutputs, output) < 0) {
|
|
|
|
virLogOutputFree(output);
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:29:15 +00:00
|
|
|
}
|
|
|
|
if (at >= 0) {
|
|
|
|
virLogOutputFree(list[at]);
|
|
|
|
VIR_DELETE_ELEMENT(list, at, noutputs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 13:58:29 +00:00
|
|
|
*outputs = g_steal_pointer(&list);
|
2021-03-22 16:52:55 +00:00
|
|
|
return noutputs;
|
2016-10-05 14:29:15 +00:00
|
|
|
}
|
2016-10-05 14:48:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogParseFilters:
|
|
|
|
* @src: string defining a (set of) filter(s)
|
|
|
|
* @filters: pointer to a list where the individual filters shall be parsed
|
|
|
|
*
|
|
|
|
* This method parses @src and produces a list of individual filters which then
|
|
|
|
* needs to be passed to virLogDefineFilters in order to be set and taken into
|
|
|
|
* effect.
|
|
|
|
* Multiple filters can be defined in a single @src, they just need to be
|
|
|
|
* separated by spaces.
|
|
|
|
*
|
|
|
|
* Returns the number of filter parsed or -1 in case of error.
|
|
|
|
*/
|
|
|
|
int
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogParseFilters(const char *src, virLogFilter ***filters)
|
2016-10-05 14:48:47 +00:00
|
|
|
{
|
|
|
|
size_t nfilters = 0;
|
2021-03-22 16:47:41 +00:00
|
|
|
g_auto(GStrv) strings = NULL;
|
2021-03-22 16:38:24 +00:00
|
|
|
GStrv next;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilter *filter = NULL;
|
|
|
|
virLogFilter **list = NULL;
|
2016-10-05 14:48:47 +00:00
|
|
|
|
|
|
|
VIR_DEBUG("filters=%s", src);
|
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
if (!(strings = g_strsplit(src, " ", 0)))
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:48:47 +00:00
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
for (next = strings; *next; next++) {
|
|
|
|
/* g_strsplit may return empty strings */
|
|
|
|
if (STREQ(*next, ""))
|
2016-10-05 14:48:47 +00:00
|
|
|
continue;
|
|
|
|
|
2021-03-22 16:38:24 +00:00
|
|
|
if (!(filter = virLogParseFilter(*next)))
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:48:47 +00:00
|
|
|
|
|
|
|
if (VIR_APPEND_ELEMENT(list, nfilters, filter)) {
|
|
|
|
virLogFilterFree(filter);
|
2021-03-22 16:52:55 +00:00
|
|
|
return -1;
|
2016-10-05 14:48:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-23 13:58:29 +00:00
|
|
|
*filters = g_steal_pointer(&list);
|
2021-03-22 16:52:55 +00:00
|
|
|
return nfilters;
|
2016-10-05 14:48:47 +00:00
|
|
|
}
|
2016-03-08 12:37:56 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogSetOutputs:
|
|
|
|
* @outputs: string defining a (set of) output(s)
|
|
|
|
*
|
|
|
|
* Replaces the current set of defined outputs with a new set of outputs.
|
2016-11-25 10:50:51 +00:00
|
|
|
* Should the set be empty or NULL, a default output is used according to the
|
|
|
|
* daemon's runtime attributes.
|
2016-03-08 12:37:56 +00:00
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of an error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virLogSetOutputs(const char *src)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int noutputs = 0;
|
2016-11-25 10:50:51 +00:00
|
|
|
const char *outputstr = virLogDefaultOutput;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogOutput **outputs = NULL;
|
2016-03-08 12:37:56 +00:00
|
|
|
|
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
2016-11-25 10:50:51 +00:00
|
|
|
if (src && *src)
|
|
|
|
outputstr = src;
|
|
|
|
|
2017-08-25 14:58:51 +00:00
|
|
|
/* This can only happen during daemon init when the default output is not
|
|
|
|
* determined yet. It's safe to do, since it's the only place setting the
|
|
|
|
* default output.
|
|
|
|
*/
|
|
|
|
if (!outputstr)
|
|
|
|
return 0;
|
|
|
|
|
2016-11-25 10:50:51 +00:00
|
|
|
if ((noutputs = virLogParseOutputs(outputstr, &outputs)) < 0)
|
2016-03-08 12:37:56 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virLogDefineOutputs(outputs, noutputs) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
outputs = NULL;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virLogOutputListFree(outputs, noutputs);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-03-24 13:34:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virLogSetFilters:
|
|
|
|
* @src: string defining a (set of) filter(s)
|
|
|
|
*
|
|
|
|
* Replaces the current set of defined filters with a new set of filters.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of an error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virLogSetFilters(const char *src)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int nfilters = 0;
|
2021-03-11 07:16:13 +00:00
|
|
|
virLogFilter **filters = NULL;
|
2016-03-24 13:34:11 +00:00
|
|
|
|
|
|
|
if (virLogInitialize() < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (src && (nfilters = virLogParseFilters(src, &filters)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virLogDefineFilters(filters, nfilters) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
filters = NULL;
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virLogFilterListFree(filters, nfilters);
|
|
|
|
return ret;
|
|
|
|
}
|