/** * virLogStartup: * * Initialize the logging module * * Returns 0 if successful, and -1 in case or error */ int virLogStartup(void) { const char *pbm = NULL; if (virLogInitialized) return -1; if (virMutexInit(&virLogMutex) < 0) return -1; virLogInitialized = 1; virLogLock(); if (VIR_ALLOC_N(virLogBuffer, virLogSize + 1) < 0) { /* * The debug buffer is not a critical component, allow startup * even in case of failure to allocate it in case of a * configuration mistake. */ virLogSize = 64 * 1024; if (VIR_ALLOC_N(virLogBuffer, virLogSize + 1) < 0) { pbm = "Failed to allocate debug buffer: deactivating debug log\n"; virLogSize = 0; } else { pbm = "Failed to allocate debug buffer: reduced to 64 kB\n"; } } virLogLen = 0; virLogStart = 0; virLogEnd = 0; virLogDefaultPriority = VIR_LOG_DEFAULT; virLogUnlock(); if (pbm) VIR_WARN("%s", pbm); return 0; }
/** * virLogDefineFilter: * @match: the pattern to match * @priority: the priority to give to messages matching the pattern * @flags: extra flag, currently unused * * Defines a pattern used for log filtering, it allow to select or * reject messages independently of the default priority. * 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 -1 in case of failure or the filter number if successful */ int virLogDefineFilter(const char *match, int priority, int flags ATTRIBUTE_UNUSED) { int i; char *mdup = NULL; if ((match == NULL) || (priority < VIR_LOG_DEBUG) || (priority > VIR_LOG_ERROR)) return(-1); virLogLock(); for (i = 0;i < virLogNbFilters;i++) { if (STREQ(virLogFilters[i].match, match)) { virLogFilters[i].priority = priority; goto cleanup; } } mdup = strdup(match); if (mdup == NULL) { i = -1; goto cleanup; } i = virLogNbFilters; if (VIR_REALLOC_N(virLogFilters, virLogNbFilters + 1)) { i = -1; VIR_FREE(mdup); goto cleanup; } virLogFilters[i].match = mdup; virLogFilters[i].priority = priority; virLogNbFilters++; cleanup: virLogUnlock(); return(i); }
/** * virLogDefineOutput: * @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 the function * @priority: minimal priority for this filter, use 0 for none * @dest: where to send output of this priority * @name: optional name data associated with an output * @flags: extra flag, currently unused * * Defines an output function for log messages. Each message once * gone though filtering is emitted through each registered output. * * Returns -1 in case of failure or the output number if successful */ int virLogDefineOutput(virLogOutputFunc f, virLogCloseFunc c, void *data, int priority, int dest, const char *name, int flags ATTRIBUTE_UNUSED) { int ret = -1; char *ndup = NULL; if (f == NULL) return(-1); if (dest == VIR_LOG_TO_SYSLOG || dest == VIR_LOG_TO_FILE) { if (name == NULL) return(-1); ndup = strdup(name); if (ndup == NULL) return(-1); } virLogLock(); if (VIR_REALLOC_N(virLogOutputs, virLogNbOutputs + 1)) { VIR_FREE(ndup); goto cleanup; } ret = virLogNbOutputs++; virLogOutputs[ret].f = f; virLogOutputs[ret].c = c; virLogOutputs[ret].data = data; virLogOutputs[ret].priority = priority; virLogOutputs[ret].dest = dest; virLogOutputs[ret].name = ndup; cleanup: virLogUnlock(); return(ret); }
/* * Store a string in the ring buffer */ static void virLogStr(const char *str, int len) { int tmp; if (str == NULL) return; if (len <= 0) len = strlen(str); if (len > LOG_BUFFER_SIZE) return; virLogLock(); /* * copy the data and reset the end, we cycle over the end of the buffer */ if (virLogEnd + len >= LOG_BUFFER_SIZE) { tmp = LOG_BUFFER_SIZE - virLogEnd; memcpy(&virLogBuffer[virLogEnd], str, tmp); virLogBuffer[LOG_BUFFER_SIZE] = 0; memcpy(&virLogBuffer[0], &str[tmp], len - tmp); virLogEnd = len - tmp; } else { memcpy(&virLogBuffer[virLogEnd], str, len); virLogEnd += len; } /* * Update the log length, and if full move the start index */ virLogLen += len; if (virLogLen > LOG_BUFFER_SIZE) { tmp = virLogLen - LOG_BUFFER_SIZE; virLogLen = LOG_BUFFER_SIZE; virLogStart += tmp; } virLogUnlock(); }
/** * virLogShutdown: * * Shutdown the logging module */ void virLogShutdown(void) { if (!virLogInitialized) return; virLogLock(); virLogResetFilters(); virLogResetOutputs(); virLogLen = 0; virLogStart = 0; virLogEnd = 0; virLogUnlock(); virMutexDestroy(&virLogMutex); virLogInitialized = 0; }
/** * virLogReset: * * Reset the logging module to its default initial state * * Returns 0 if successful, and -1 in case or error */ int virLogReset(void) { if (virLogInitialize() < 0) return -1; virLogLock(); virLogResetFilters(); virLogResetOutputs(); virLogDefaultPriority = VIR_LOG_DEFAULT; virLogUnlock(); return 0; }
/** * virLogReset: * * Reset the logging module to its default initial state * * Returns 0 if successful, and -1 in case or error */ int virLogReset(void) { if (!virLogInitialized) return(virLogStartup()); virLogLock(); virLogResetFilters(); virLogResetOutputs(); virLogLen = 0; virLogStart = 0; virLogEnd = 0; virLogDefaultPriority = VIR_LOG_DEFAULT; virLogUnlock(); return(0); }
/** * virLogFiltersCheck: * @input: the input string * * Check the input of the message against the existing filters. Currently * the match is just a substring check of the category used as the input * string, a more subtle approach could be used instead * * Returns 0 if not matched or the new priority if found. */ static int virLogFiltersCheck(const char *input) { int ret = 0; int i; virLogLock(); for (i = 0;i < virLogNbFilters;i++) { if (strstr(input, virLogFilters[i].match)) { ret = virLogFilters[i].priority; break; } } virLogUnlock(); return(ret); }
/** * virLogStartup: * * Initialize the logging module * * Returns 0 if successful, and -1 in case or error */ int virLogStartup(void) { if (virLogInitialized) return(-1); if (virMutexInit(&virLogMutex) < 0) return -1; virLogInitialized = 1; virLogLock(); virLogLen = 0; virLogStart = 0; virLogEnd = 0; virLogDefaultPriority = VIR_LOG_DEFAULT; virLogUnlock(); return(0); }
/** * virLogSetBufferSize: * @size: size of the buffer in kilobytes or <= 0 to deactivate * * Dynamically set the size or deactivate the logging buffer used to keep * a trace of all recent debug output. Note that the content of the buffer * is lost if it gets reallocated. * * Return -1 in case of failure or 0 in case of success */ int virLogSetBufferSize(int size) { int ret = 0; int oldsize; char *oldLogBuffer; const char *pbm = NULL; if (size < 0) size = 0; if (virLogInitialize() < 0) return -1; if (size * 1024 == virLogSize) return ret; virLogLock(); oldsize = virLogSize; oldLogBuffer = virLogBuffer; if (INT_MAX / 1024 <= size) { pbm = "Requested log size of %d kB too large\n"; ret = -1; goto error; } virLogSize = size * 1024; if (VIR_ALLOC_N_QUIET(virLogBuffer, virLogSize + 1) < 0) { pbm = "Failed to allocate debug buffer of %d kB\n"; virLogBuffer = oldLogBuffer; virLogSize = oldsize; ret = -1; goto error; } VIR_FREE(oldLogBuffer); virLogLen = 0; virLogStart = 0; virLogEnd = 0; error: virLogUnlock(); if (pbm) VIR_ERROR(pbm, size); return ret; }
/** * virLogDefineOutput: * @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 the function * @priority: minimal priority for this filter, use 0 for none * @dest: where to send output of this priority * @name: optional name data associated with an output * @flags: extra flag, currently unused * * Defines an output function for log messages. Each message once * gone though filtering is emitted through each registered output. * * Returns -1 in case of failure or the output number if successful */ int virLogDefineOutput(virLogOutputFunc f, virLogCloseFunc c, void *data, virLogPriority priority, virLogDestination dest, const char *name, unsigned int flags) { int ret = -1; char *ndup = NULL; virCheckFlags(0, -1); if (virLogInitialize() < 0) return -1; if (f == NULL) return -1; if (dest == VIR_LOG_TO_SYSLOG || dest == VIR_LOG_TO_FILE) { if (!name) { virReportOOMError(); return -1; } if (VIR_STRDUP(ndup, name) < 0) return -1; } virLogLock(); if (VIR_REALLOC_N_QUIET(virLogOutputs, virLogNbOutputs + 1)) { VIR_FREE(ndup); goto cleanup; } ret = virLogNbOutputs++; virLogOutputs[ret].logVersion = true; virLogOutputs[ret].f = f; virLogOutputs[ret].c = c; virLogOutputs[ret].data = data; virLogOutputs[ret].priority = priority; virLogOutputs[ret].dest = dest; virLogOutputs[ret].name = ndup; cleanup: virLogUnlock(); return ret; }
static int virLogOnceInit(void) { if (virMutexInit(&virLogMutex) < 0) return -1; virLogLock(); virLogDefaultPriority = VIR_LOG_DEFAULT; if (VIR_ALLOC_QUIET(virLogRegex) >= 0) { if (regcomp(virLogRegex, VIR_LOG_REGEX, REG_EXTENDED) != 0) VIR_FREE(virLogRegex); } virLogUnlock(); return 0; }
/** * virLogDefineFilter: * @match: the pattern to match * @priority: the priority to give to messages matching the pattern * @flags: extra flags, see virLogFilterFlags enum * * Defines a pattern used for log filtering, it allow to select or * reject messages independently of the default priority. * 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 -1 in case of failure or the filter number if successful */ int virLogDefineFilter(const char *match, virLogPriority priority, unsigned int flags) { size_t i; int ret = -1; char *mdup = NULL; virCheckFlags(VIR_LOG_STACK_TRACE, -1); if (virLogInitialize() < 0) return -1; if ((match == NULL) || (priority < VIR_LOG_DEBUG) || (priority > VIR_LOG_ERROR)) return -1; virLogLock(); for (i = 0; i < virLogNbFilters; i++) { if (STREQ(virLogFilters[i].match, match)) { virLogFilters[i].priority = priority; ret = i; goto cleanup; } } if (VIR_STRDUP_QUIET(mdup, match) < 0) goto cleanup; if (VIR_REALLOC_N_QUIET(virLogFilters, virLogNbFilters + 1)) { VIR_FREE(mdup); goto cleanup; } ret = virLogNbFilters; virLogFilters[i].match = mdup; virLogFilters[i].priority = priority; virLogFilters[i].flags = flags; virLogNbFilters++; virLogFiltersSerial++; cleanup: virLogUnlock(); if (ret < 0) virReportOOMError(); return ret; }
/** * virLogFiltersCheck: * @input: the input string * * Check the input of the message against the existing filters. Currently * the match is just a substring check of the category used as the input * string, a more subtle approach could be used instead * * Returns 0 if not matched or the new priority if found. */ static int virLogFiltersCheck(const char *input, unsigned int *flags) { int ret = 0; size_t i; virLogLock(); for (i = 0; i < virLogNbFilters; i++) { if (strstr(input, virLogFilters[i].match)) { ret = virLogFilters[i].priority; *flags = virLogFilters[i].flags; break; } } virLogUnlock(); return ret; }
/* * Output the ring buffer */ static int virLogDump(void *data, virLogOutputFunc f) { int ret = 0, tmp; if ((virLogLen == 0) || (f == NULL)) return(0); virLogLock(); if (virLogStart + virLogLen < LOG_BUFFER_SIZE) { push_end: virLogBuffer[virLogStart + virLogLen] = 0; tmp = f(data, &virLogBuffer[virLogStart], virLogLen); if (tmp < 0) { ret = -1; goto error; } ret += tmp; virLogStart += tmp; virLogLen -= tmp; } else { tmp = LOG_BUFFER_SIZE - virLogStart; ret = f(data, &virLogBuffer[virLogStart], tmp); if (ret < 0) { ret = -1; goto error; } if (ret < tmp) { virLogStart += ret; virLogLen -= ret; } else { virLogStart = 0; virLogLen -= tmp; /* dump the second part */ if (virLogLen > 0) goto push_end; } } error: virLogUnlock(); return(ret); }
static int virLogOnceInit(void) { const char *pbm = NULL; if (virMutexInit(&virLogMutex) < 0) return -1; virLogLock(); if (VIR_ALLOC_N_QUIET(virLogBuffer, virLogSize + 1) < 0) { /* * The debug buffer is not a critical component, allow startup * even in case of failure to allocate it in case of a * configuration mistake. */ virLogSize = 64 * 1024; if (VIR_ALLOC_N_QUIET(virLogBuffer, virLogSize + 1) < 0) { pbm = "Failed to allocate debug buffer: deactivating debug log\n"; virLogSize = 0; } else { pbm = "Failed to allocate debug buffer: reduced to 64 kB\n"; } } virLogLen = 0; virLogStart = 0; virLogEnd = 0; virLogDefaultPriority = VIR_LOG_DEFAULT; if (VIR_ALLOC_QUIET(virLogRegex) >= 0) { if (regcomp(virLogRegex, VIR_LOG_REGEX, REG_EXTENDED) != 0) VIR_FREE(virLogRegex); } virLogUnlock(); if (pbm) VIR_WARN("%s", pbm); return 0; }
static void virLogSourceUpdate(virLogSourcePtr source) { virLogLock(); if (source->serial < virLogFiltersSerial) { unsigned int priority = virLogDefaultPriority; unsigned int flags = 0; size_t i; for (i = 0; i < virLogNbFilters; i++) { if (strstr(source->name, virLogFilters[i].match)) { priority = virLogFilters[i].priority; flags = virLogFilters[i].flags; break; } } source->priority = priority; source->flags = flags; source->serial = virLogFiltersSerial; } virLogUnlock(); }
/** * virLogDefineOutput: * @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 the function * @priority: minimal priority for this filter, use 0 for none * @dest: where to send output of this priority * @name: optional name data associated with an output * @flags: extra flag, currently unused * * Defines an output function for log messages. Each message once * gone though filtering is emitted through each registered output. * * Returns -1 in case of failure or the output number if successful */ int virLogDefineOutput(virLogOutputFunc f, virLogCloseFunc c, void *data, int priority, int dest, const char *name, unsigned int flags) { int ret = -1; char *ndup = NULL; virCheckFlags(0, -1); if (f == NULL) return -1; if (dest == VIR_LOG_TO_SYSLOG || dest == VIR_LOG_TO_FILE) { if (name == NULL) return -1; ndup = strdup(name); if (ndup == NULL) return -1; } virLogLock(); if (VIR_REALLOC_N(virLogOutputs, virLogNbOutputs + 1)) { VIR_FREE(ndup); goto cleanup; } ret = virLogNbOutputs++; virLogOutputs[ret].logVersion = true; virLogOutputs[ret].f = f; virLogOutputs[ret].c = c; virLogOutputs[ret].data = data; virLogOutputs[ret].priority = priority; virLogOutputs[ret].dest = dest; virLogOutputs[ret].name = ndup; cleanup: virLogUnlock(); return ret; }
/** * virLogMessage: * @category: where is that message coming from * @priority: the priority level * @funcname: the function emitting the (debug) message * @linenr: line where the message was emitted * @flags: extra flags, 1 if coming from the error handler * @fmt: the string format * @...: the arguments * * Call the libvirt logger with some informations. Based on the configuration * the message may be stored, sent to output or just discarded */ void virLogMessage(const char *category, int priority, const char *funcname, long long linenr, int flags, const char *fmt, ...) { char *str = NULL; char *msg; struct timeval cur_time; struct tm time_info; int len, fprio, i, ret; if (!virLogInitialized) virLogStartup(); if (fmt == NULL) return; /* * check against list of specific logging patterns */ fprio = virLogFiltersCheck(category); if (fprio == 0) { if (priority < virLogDefaultPriority) return; } else if (priority < fprio) return; /* * serialize the error message, add level and timestamp */ VIR_GET_VAR_STR(fmt, str); if (str == NULL) return; gettimeofday(&cur_time, NULL); localtime_r(&cur_time.tv_sec, &time_info); if ((funcname != NULL)) { ret = virAsprintf(&msg, "%02d:%02d:%02d.%03d: %s : %s:%lld : %s\n", time_info.tm_hour, time_info.tm_min, time_info.tm_sec, (int) cur_time.tv_usec / 1000, virLogPriorityString(priority), funcname, linenr, str); } else { ret = virAsprintf(&msg, "%02d:%02d:%02d.%03d: %s : %s\n", time_info.tm_hour, time_info.tm_min, time_info.tm_sec, (int) cur_time.tv_usec / 1000, virLogPriorityString(priority), str); } VIR_FREE(str); if (ret < 0) { /* apparently we're running out of memory */ return; } /* * Log based on defaults, first store in the history buffer * then push the message on the outputs defined, if none * use stderr. * NOTE: the locking is a single point of contention for multiple * threads, but avoid intermixing. Maybe set up locks per output * to improve paralellism. */ len = strlen(msg); virLogStr(msg, len); virLogLock(); for (i = 0; i < virLogNbOutputs;i++) { if (priority >= virLogOutputs[i].priority) virLogOutputs[i].f(category, priority, funcname, linenr, msg, len, virLogOutputs[i].data); } if ((virLogNbOutputs == 0) && (flags != 1)) ignore_value (safewrite(STDERR_FILENO, msg, len)); virLogUnlock(); VIR_FREE(msg); }
/** * virLogVMessage: * @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 * @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 */ void virLogVMessage(virLogSource source, virLogPriority priority, const char *filename, int linenr, const char *funcname, virLogMetadataPtr metadata, const char *fmt, va_list vargs) { static bool logVersionStderr = true; char *str = NULL; char *msg = NULL; char timestamp[VIR_TIME_STRING_BUFLEN]; int fprio, ret; size_t i; int saved_errno = errno; bool emit = true; unsigned int filterflags = 0; if (virLogInitialize() < 0) return; if (fmt == NULL) goto cleanup; /* * check against list of specific logging patterns */ fprio = virLogFiltersCheck(filename, &filterflags); if (fprio == 0) { if (priority < virLogDefaultPriority) emit = false; } else if (priority < fprio) { emit = false; } if (!emit && ((virLogBuffer == NULL) || (virLogSize <= 0))) goto cleanup; /* * serialize the error message, add level and timestamp */ if (virVasprintfQuiet(&str, fmt, vargs) < 0) { goto cleanup; } ret = virLogFormatString(&msg, linenr, funcname, priority, str); if (ret < 0) goto cleanup; if (virTimeStringNowRaw(timestamp) < 0) timestamp[0] = '\0'; /* * Log based on defaults, first store in the history buffer, * then if emit push the message on the outputs defined, if none * use stderr. * NOTE: the locking is a single point of contention for multiple * threads, but avoid intermixing. Maybe set up locks per output * to improve paralellism. */ virLogLock(); virLogStr(timestamp); virLogStr(": "); virLogStr(msg); virLogUnlock(); if (!emit) goto cleanup; virLogLock(); for (i = 0; i < virLogNbOutputs; i++) { if (priority >= virLogOutputs[i].priority) { if (virLogOutputs[i].logVersion) { const char *rawver; char *ver = NULL; if (virLogVersionString(&rawver, &ver) >= 0) virLogOutputs[i].f(VIR_LOG_FROM_FILE, VIR_LOG_INFO, __FILE__, __LINE__, __func__, timestamp, NULL, 0, rawver, ver, virLogOutputs[i].data); VIR_FREE(ver); virLogOutputs[i].logVersion = false; } virLogOutputs[i].f(source, priority, filename, linenr, funcname, timestamp, metadata, filterflags, str, msg, virLogOutputs[i].data); } } if ((virLogNbOutputs == 0) && (source != VIR_LOG_FROM_ERROR)) { if (logVersionStderr) { const char *rawver; char *ver = NULL; if (virLogVersionString(&rawver, &ver) >= 0) virLogOutputToFd(VIR_LOG_FROM_FILE, VIR_LOG_INFO, __FILE__, __LINE__, __func__, timestamp, NULL, 0, rawver, ver, (void *) STDERR_FILENO); VIR_FREE(ver); logVersionStderr = false; } virLogOutputToFd(source, priority, filename, linenr, funcname, timestamp, metadata, filterflags, str, msg, (void *) STDERR_FILENO); } virLogUnlock(); cleanup: VIR_FREE(str); VIR_FREE(msg); errno = saved_errno; }
/** * virLogMessage: * @category: where is that message coming from * @priority: the priority level * @funcname: the function emitting the (debug) message * @linenr: line where the message was emitted * @flags: extra flags, 1 if coming from the error handler * @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 virLogMessage(const char *category, int priority, const char *funcname, long long linenr, unsigned int flags, const char *fmt, ...) { static bool logVersionStderr = true; char *str = NULL; char *msg = NULL; char timestamp[VIR_TIME_STRING_BUFLEN]; int fprio, i, ret; int saved_errno = errno; int emit = 1; va_list ap; if (!virLogInitialized) virLogStartup(); if (fmt == NULL) goto cleanup; /* * check against list of specific logging patterns */ fprio = virLogFiltersCheck(category); if (fprio == 0) { if (priority < virLogDefaultPriority) emit = 0; } else if (priority < fprio) { emit = 0; } if ((emit == 0) && ((virLogBuffer == NULL) || (virLogSize <= 0))) goto cleanup; /* * serialize the error message, add level and timestamp */ va_start(ap, fmt); if (virVasprintf(&str, fmt, ap) < 0) { va_end(ap); goto cleanup; } va_end(ap); ret = virLogFormatString(&msg, funcname, linenr, priority, str); VIR_FREE(str); if (ret < 0) goto cleanup; if (virTimeStringNowRaw(timestamp) < 0) timestamp[0] = '\0'; /* * Log based on defaults, first store in the history buffer, * then if emit push the message on the outputs defined, if none * use stderr. * NOTE: the locking is a single point of contention for multiple * threads, but avoid intermixing. Maybe set up locks per output * to improve paralellism. */ virLogLock(); virLogStr(timestamp); virLogStr(msg); virLogUnlock(); if (emit == 0) goto cleanup; virLogLock(); for (i = 0; i < virLogNbOutputs; i++) { if (priority >= virLogOutputs[i].priority) { if (virLogOutputs[i].logVersion) { char *ver = NULL; if (virLogVersionString(&ver) >= 0) virLogOutputs[i].f(category, VIR_LOG_INFO, __func__, __LINE__, timestamp, ver, virLogOutputs[i].data); VIR_FREE(ver); virLogOutputs[i].logVersion = false; } virLogOutputs[i].f(category, priority, funcname, linenr, timestamp, msg, virLogOutputs[i].data); } } if ((virLogNbOutputs == 0) && (flags != 1)) { if (logVersionStderr) { char *ver = NULL; if (virLogVersionString(&ver) >= 0) virLogOutputToFd(category, VIR_LOG_INFO, __func__, __LINE__, timestamp, ver, (void *) STDERR_FILENO); VIR_FREE(ver); logVersionStderr = false; } virLogOutputToFd(category, priority, funcname, linenr, timestamp, msg, (void *) STDERR_FILENO); } virLogUnlock(); cleanup: VIR_FREE(msg); errno = saved_errno; }
/** * virLogVMessage: * @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 * @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 */ void virLogVMessage(virLogSourcePtr source, virLogPriority priority, const char *filename, int linenr, const char *funcname, virLogMetadataPtr metadata, const char *fmt, va_list vargs) { static bool logVersionStderr = true; char *str = NULL; char *msg = NULL; char timestamp[VIR_TIME_STRING_BUFLEN]; int ret; size_t i; int saved_errno = errno; unsigned int filterflags = 0; if (virLogInitialize() < 0) return; if (fmt == NULL) return; /* * 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. */ if (source->serial < virLogFiltersSerial) virLogSourceUpdate(source); if (priority < source->priority) goto cleanup; filterflags = source->flags; /* * serialize the error message, add level and timestamp */ if (virVasprintfQuiet(&str, fmt, vargs) < 0) { goto cleanup; } ret = virLogFormatString(&msg, linenr, funcname, priority, str); if (ret < 0) goto cleanup; if (virTimeStringNowRaw(timestamp) < 0) timestamp[0] = '\0'; virLogLock(); /* * Push the message to the outputs defined, if none exist then * use stderr. */ for (i = 0; i < virLogNbOutputs; i++) { if (priority >= virLogOutputs[i].priority) { if (virLogOutputs[i].logVersion) { const char *rawver; char *ver = NULL; if (virLogVersionString(&rawver, &ver) >= 0) virLogOutputs[i].f(&virLogSelf, VIR_LOG_INFO, __FILE__, __LINE__, __func__, timestamp, NULL, 0, rawver, ver, virLogOutputs[i].data); VIR_FREE(ver); virLogOutputs[i].logVersion = false; } virLogOutputs[i].f(source, priority, filename, linenr, funcname, timestamp, metadata, filterflags, str, msg, virLogOutputs[i].data); } } if (virLogNbOutputs == 0) { if (logVersionStderr) { const char *rawver; char *ver = NULL; if (virLogVersionString(&rawver, &ver) >= 0) virLogOutputToFd(&virLogSelf, VIR_LOG_INFO, __FILE__, __LINE__, __func__, timestamp, NULL, 0, rawver, ver, (void *) STDERR_FILENO); VIR_FREE(ver); logVersionStderr = false; } virLogOutputToFd(source, priority, filename, linenr, funcname, timestamp, metadata, filterflags, str, msg, (void *) STDERR_FILENO); } virLogUnlock(); cleanup: VIR_FREE(str); VIR_FREE(msg); errno = saved_errno; }