1
0
mirror of https://gitlab.xiph.org/xiph/icecast-common.git synced 2024-12-04 14:46:31 -05:00

Feature: Initial code for new logging API

This commit is contained in:
Philipp Schafft 2019-07-06 17:40:25 +00:00
parent 334e84fac3
commit de2712ab48
7 changed files with 568 additions and 216 deletions

View File

@ -25,11 +25,13 @@ pkginclude_HEADERS = \
include/igloo/stdio.h \
include/igloo/filter.h \
include/igloo/objecthandler.h \
include/igloo/logmsg.h \
include/igloo/buffer.h \
include/igloo/list.h \
include/igloo/reportxml.h
libigloo_la_SOURCES = \
src/private.c \
src/libigloo.c \
src/interface.c \
src/ro.c \
@ -37,6 +39,7 @@ libigloo_la_SOURCES = \
src/stdio.c \
src/filter.c \
src/objecthandler.c \
src/logmsg.c \
src/buffer.c \
src/list.c \
src/reportxml.c

122
include/igloo/logmsg.h Normal file
View File

@ -0,0 +1,122 @@
/* Copyright (C) 2019 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef _LIBIGLOO__LOGMSG_H_
#define _LIBIGLOO__LOGMSG_H_
/**
* @file
* Put a good description of this file here
*/
#ifdef __cplusplus
extern "C" {
#endif
#include <igloo/config.h>
#ifdef IGLOO_CTC_HAVE_STDINT_H
#include <stdint.h>
#endif
#include <time.h>
#include <igloo/config.h>
#include "ro.h"
#include "list.h"
/* About thread safety:
* This set of functions is thread safe.
*/
igloo_RO_FORWARD_TYPE(igloo_logmsg_t);
/* Log level for log messages */
typedef enum {
/* Used to report errors with function calls. */
IGLOO_LOGLEVEL__ERROR = -1,
/* Unset log level. */
IGLOO_LOGLEVEL__NONE = 0,
/* Logmsg reports an error. */
IGLOO_LOGLEVEL_ERROR,
/* Logmsg reports a warning. */
IGLOO_LOGLEVEL_WARN,
/* Logmsg is of information level. */
IGLOO_LOGLEVEL_INFO,
/* Logmsg is for debugging only. */
IGLOO_LOGLEVEL_DEBUG
} igloo_loglevel_t;
/* Type for logmsg options.
*/
#ifdef IGLOO_CTC_HAVE_STDINT_H
typedef uint_least32_t igloo_logmsg_opt_t;
#else
typedef unsigned long int igloo_logmsg_opt_t;
#endif
/* No options set. */
#define igloo_LOGMSG_OPT_NONE ((igloo_logmsg_opt_t)0x000)
/* Logmsg is only useful for developing the software itself. */
#define igloo_LOGMSG_OPT_DEVEL ((igloo_logmsg_opt_t)0x001)
/* Logmsg should be acknowledged by the user. */
#define igloo_LOGMSG_OPT_ASKACK ((igloo_logmsg_opt_t)0x002)
/* This creates a new log message.
* Parameters:
* name, associated
* See refobject_new().
* msgid
* Message ID used to correlate messages of the same type.
* Used e.g. with igloo_LOGMSG_OPT_ASKACK.
* Must be globally unique. Could be URI (e.g. UUID based URN)
* or message@domain.
* cat
* Message category/module.
* func
* Function generating the log message.
* codefile
* Code file generating the message.
* codeline
* Code line generating the message.
* ts
* Timestamp of the message. If NULL a timestamp is generated.
* level
* Loglevel of the message.
* options
* Message and delivery options.
* referenced
* List of objects relevant to the context generating the message.
* format
* Format string for the log message.
* ...
* Parameters according to the format string.
*/
igloo_logmsg_t * igloo_logmsg_new(const char *name, igloo_ro_t associated, const char *msgid, const char *cat, const char *func, const char *codefile, const ssize_t codeline, const struct timespec * ts, igloo_loglevel_t level, igloo_logmsg_opt_t options, igloo_list_t *referenced, const char *format, ...);
int igloo_logmsg_get_context(igloo_logmsg_t *msg, const char **msgid, const char **cat, const char **func, const char **codefile, const ssize_t *codeline, struct timespec **ts);
int igloo_logmsg_get_message(igloo_logmsg_t *msg, igloo_loglevel_t *level, const char **string);
int igloo_logmsg_get_extra(igloo_logmsg_t *msg, igloo_logmsg_opt_t *options, igloo_list_t **list);
igloo_objecthandler_t * igloo_logmsg_formarter(igloo_ro_t backend, const char *subformat, const char *name, igloo_ro_t associated);
igloo_filter_t * igloo_logmsg_filter(igloo_loglevel_t level_min, igloo_loglevel_t level_max, igloo_logmsg_opt_t options_required, igloo_logmsg_opt_t options_absent, const struct timespec * ts_min, const struct timespec * ts_max, const char *cat, const char *name, igloo_ro_t associated);
#ifdef __cplusplus
}
#endif
#endif /* ! _LIBIGLOO__LOGMSG_H_ */

View File

@ -37,6 +37,7 @@ extern "C" {
typedef struct igloo_io_tag igloo_io_t;
typedef struct igloo_filter_tag igloo_filter_t;
typedef struct igloo_objecthandler_tag igloo_objecthandler_t;
typedef struct igloo_logmsg_tag igloo_logmsg_t;
typedef struct igloo_buffer_tag igloo_buffer_t;
typedef struct igloo_list_tag igloo_list_t;
@ -58,6 +59,7 @@ typedef union __attribute__ ((__transparent_union__)) {
igloo_RO_TYPE(igloo_io_t)
igloo_RO_TYPE(igloo_filter_t)
igloo_RO_TYPE(igloo_objecthandler_t)
igloo_RO_TYPE(igloo_logmsg_t)
igloo_RO_TYPE(igloo_buffer_t)
igloo_RO_TYPE(igloo_list_t)
igloo_RO_TYPE(igloo_reportxml_t)

220
log/log.c
View File

@ -53,6 +53,8 @@
#include <igloo/log.h>
#include "../src/private.h"
#define LOG_MAXLOGS 25
#define LOG_MAXLINELEN 1024
@ -439,220 +441,6 @@ void igloo_log_contents (int log_id, char **_contents, unsigned int *_len)
_unlock_logger ();
}
static inline int __vsnprintf__is_print(int c, int allow_space)
{
if ((c <= '"' || c == '`' || c == '\\') && !(allow_space && c == ' ')) {
return 0;
} else {
return 1;
}
}
static inline size_t __vsnprintf__strlen(const char *str, int is_alt, int allow_space)
{
size_t ret = 0;
if (!str) {
if (is_alt) {
return strlen("-");
} else {
return strlen("(null)");
}
}
for (; *str; str++) {
if (__vsnprintf__is_print(*str, allow_space)) {
ret += 1;
} else {
ret += 4;
}
}
if (is_alt) {
ret += 2;
}
return ret;
}
static void __vsnprintf(char *str, size_t size, const char *format, va_list ap) {
static const char hextable[] = "0123456789abcdef";
int in_block = 0;
int block_size = 0;
int block_len = 0;
int block_space = 0;
int block_alt = 0;
const char * arg;
char buf[80];
for (; *format && size; format++)
{
if ( !in_block )
{
if ( *format == '%' ) {
in_block = 1;
block_size = 0;
block_len = 0;
block_space = 0;
block_alt = 0;
}
else
{
*(str++) = *format;
size--;
}
}
else
{
// TODO: %l*[sdupi] as well as %.4080s and "%.*s
arg = NULL;
switch (*format)
{
case 'l':
block_size++;
break;
case 'z':
block_size = 'z';
break;
case '.':
// just ignore '.'. If somebody cares: fix it.
break;
case '*':
block_len = va_arg(ap, int);
break;
case ' ':
block_space = 1;
break;
case '#':
block_alt = 1;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
block_len = atoi(format);
for (; *format >= '0' && *format <= '9'; format++);
format--;
break;
case 'p':
snprintf(buf, sizeof(buf), "%p", (void*)va_arg(ap, void *));
arg = buf;
case 'd':
case 'i':
case 'u':
if (!arg)
{
switch (block_size)
{
case 0:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%u", (unsigned int)va_arg(ap, unsigned int));
else
snprintf(buf, sizeof(buf), "%i", (int)va_arg(ap, int));
break;
case 1:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%lu", (unsigned long int)va_arg(ap, unsigned long int));
else
snprintf(buf, sizeof(buf), "%li", (long int)va_arg(ap, long int));
break;
case 2:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%llu", (unsigned long long int)va_arg(ap, unsigned long long int));
else
snprintf(buf, sizeof(buf), "%lli", (long long int)va_arg(ap, long long int));
break;
case 'z':
/* We do not use 'z' type of snprintf() here as it is not safe to use on a few outdated platforms. */
if (*format == 'u')
snprintf(buf, sizeof(buf), "%llu", (unsigned long long int)va_arg(ap, size_t));
else
snprintf(buf, sizeof(buf), "%lli", (long long int)va_arg(ap, ssize_t));
break;
default:
snprintf(buf, sizeof(buf), "<<<invalid>>>");
break;
}
arg = buf;
}
case 's':
case 'H':
// TODO.
if (!arg)
arg = va_arg(ap, const char *);
if (*format != 'H') {
block_alt = 0;
}
if (!arg && !block_alt)
arg = "(null)";
if (!block_len) {
block_len = __vsnprintf__strlen(arg, block_alt, block_space);
}
// the if() is the outer structure so the inner for()
// is branch optimized.
if (*format == 'H' && !arg)
{
if (size && block_len) {
*(str++) = '-';
size--;
block_len--;
}
}
else if (*format == 'H')
{
if (block_alt && size && block_len) {
*(str++) = '"';
size--;
block_len--;
}
for (; *arg && block_len && size; arg++, size--, block_len--)
{
if (!__vsnprintf__is_print(*arg, block_space)) {
if (size < 4 || block_len < 4) {
/* Use old system if we do not have space for new one */
*(str++) = '.';
} else {
*(str++) = '\\';
*(str++) = 'x';
*(str++) = hextable[(*arg >> 0) & 0x0F];
*(str++) = hextable[(*arg >> 4) & 0x0F];
/* Also count the additional chars for string size and block length */
size -= 3;
block_len -= 3;
}
} else {
*(str++) = *arg;
}
}
if (block_alt && size && block_len) {
*(str++) = '"';
size--;
block_len--;
}
}
else
{
for (; *arg && block_len && size; arg++, size--, block_len--)
*(str++) = *arg;
}
in_block = 0;
break;
}
}
}
if ( !size )
str--;
*str = 0;
}
void igloo_log_write(int log_id, unsigned priority, const char *cat, const char *func,
const char *fmt, ...)
{
@ -669,7 +457,7 @@ void igloo_log_write(int log_id, unsigned priority, const char *cat, const char
va_start(ap, fmt);
__vsnprintf(line, sizeof(line), fmt, ap);
igloo_private__vsnprintf(line, sizeof(line), fmt, ap);
va_end(ap);
now = time(NULL);
@ -696,7 +484,7 @@ void igloo_log_write_direct(int log_id, const char *fmt, ...)
va_start(ap, fmt);
_lock_logger();
__vsnprintf(line, LOG_MAXLINELEN, fmt, ap);
igloo_private__vsnprintf(line, LOG_MAXLINELEN, fmt, ap);
if (_log_open (log_id))
{
int len = igloo_create_log_entry (log_id, "", line);

198
src/logmsg.c Normal file
View File

@ -0,0 +1,198 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2019, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <igloo/logmsg.h>
#include <igloo/interface.h>
#include <igloo/objecthandler.h>
#include <igloo/io.h>
#include "private.h"
#define LOG_MAXLINELEN 1024
struct igloo_logmsg_tag {
igloo_ro_base_t __base;
char *msgid;
char *cat;
char *func;
char *codefile;
ssize_t codeline;
struct timespec ts;
igloo_loglevel_t level;
igloo_logmsg_opt_t options;
igloo_list_t *referenced;
char *string;
};
static void __free(igloo_ro_t self)
{
igloo_logmsg_t *logmsg = igloo_RO_TO_TYPE(self, igloo_logmsg_t);
free(logmsg->msgid);
free(logmsg->cat);
free(logmsg->func);
free(logmsg->codefile);
free(logmsg->string);
igloo_ro_unref(logmsg->referenced);
}
igloo_RO_PUBLIC_TYPE(igloo_logmsg_t,
igloo_RO_TYPEDECL_FREE(__free)
);
igloo_logmsg_t * igloo_logmsg_new(const char *name, igloo_ro_t associated,
const char *msgid,
const char *cat,
const char *func, const char *codefile, const ssize_t codeline,
const struct timespec * ts,
igloo_loglevel_t level, igloo_logmsg_opt_t options,
igloo_list_t *referenced,
const char *format, ...)
{
igloo_logmsg_t *logmsg = igloo_ro_new_raw(igloo_logmsg_t, name, associated);
va_list ap;
char string[LOG_MAXLINELEN];
if (!logmsg)
return NULL;
logmsg->codeline = codeline;
logmsg->level = level;
logmsg->options = options;
va_start(ap, format);
igloo_private__vsnprintf(string, sizeof(string), format, ap);
va_end(ap);
do {
#define __set_str(x) \
if (x) { \
logmsg->x = strdup((x)); \
if (!logmsg->x) \
break; \
}
__set_str(msgid);
__set_str(cat);
__set_str(func);
__set_str(codefile);
logmsg->string = strdup(string);
if (!logmsg->string)
break;
if (ts) {
logmsg->ts = *ts;
} else {
if (clock_gettime(CLOCK_REALTIME, &(logmsg->ts)) != 0)
break;
}
if (referenced) {
if (igloo_ro_ref(referenced) != 0)
break;
logmsg->referenced = referenced;
}
return logmsg;
} while (0);
igloo_ro_unref(logmsg);
return NULL;
}
int igloo_logmsg_get_context(igloo_logmsg_t *msg, const char **msgid, const char **cat, const char **func, const char **codefile, const ssize_t *codeline, struct timespec **ts);
int igloo_logmsg_get_message(igloo_logmsg_t *msg, igloo_loglevel_t *level, const char **string);
int igloo_logmsg_get_extra(igloo_logmsg_t *msg, igloo_logmsg_opt_t *options, igloo_list_t **list);
static const char * __level2str(igloo_loglevel_t level)
{
switch (level) {
case IGLOO_LOGLEVEL__ERROR: return "<<<ERROR>>>"; break;
case IGLOO_LOGLEVEL__NONE: return "NONE"; break;
case IGLOO_LOGLEVEL_ERROR: return "EROR"; break;
case IGLOO_LOGLEVEL_WARN: return "WARN"; break;
case IGLOO_LOGLEVEL_INFO: return "INFO"; break;
case IGLOO_LOGLEVEL_DEBUG: return "DBUG"; break;
}
return "<<<unknowm log level>>>";
}
static igloo_filter_result_t __handle(igloo_INTERFACE_BASIC_ARGS, igloo_ro_t object)
{
igloo_logmsg_t *msg = igloo_RO_TO_TYPE(object, igloo_logmsg_t);
const char *level = NULL;
time_t now;
char pre[256+LOG_MAXLINELEN];
int datelen;
if (!msg)
return igloo_FILTER_RESULT_DROP;
level = __level2str(msg->level);
now = msg->ts.tv_sec;
datelen = strftime(pre, sizeof(pre), "[%Y-%m-%d %H:%M:%S]", localtime(&now));
snprintf(pre+datelen, sizeof(pre)-datelen, " %s %s/%s(%s:%zi) %s\n", level, msg->cat, msg->func, msg->codefile, msg->codeline, msg->string);
igloo_io_write(igloo_RO_TO_TYPE(*backend_object, igloo_io_t), pre, strlen(pre));
return igloo_FILTER_RESULT_PASS;
}
static const igloo_objecthandler_ifdesc_t igloo_logmsg_formarter_ifdesc = {
igloo_INTERFACE_DESCRIPTION_BASE(igloo_objecthandler_ifdesc_t),
.is_thread_safe = 1,
.handle = __handle
};
typedef enum {
igloo_FST_NORMAL
} igloo_logmsg_formarter_subtype_t;
igloo_objecthandler_t * igloo_logmsg_formarter(igloo_ro_t backend, const char *subformat, const char *name, igloo_ro_t associated)
{
igloo_logmsg_formarter_subtype_t *sf = NULL;
igloo_objecthandler_t *objecthandler;
if (!igloo_RO_IS_VALID(backend, igloo_io_t))
return NULL;
if (!subformat || strcmp(subformat, "default") == 0)
subformat = "normal";
if (strcmp(subformat, "normal") == 0) {
sf = malloc(sizeof(*sf));
if (!sf)
return NULL;
*sf = igloo_FST_NORMAL;
} else {
return NULL;
}
objecthandler = igloo_objecthandler_new(&igloo_logmsg_formarter_ifdesc, backend, sf, name, associated);
if (!objecthandler) {
free(sf);
}
return objecthandler;
}
igloo_filter_t * igloo_logmsg_filter(igloo_loglevel_t level_min, igloo_loglevel_t level_max, igloo_logmsg_opt_t options_required, igloo_logmsg_opt_t options_absent, const struct timespec * ts_min, const struct timespec * ts_max, const char *cat, const char *name, igloo_ro_t associated);

233
src/private.c Normal file
View File

@ -0,0 +1,233 @@
/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2019, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "private.h"
static inline int __vsnprintf__is_print(int c, int allow_space)
{
if ((c <= '"' || c == '`' || c == '\\') && !(allow_space && c == ' ')) {
return 0;
} else {
return 1;
}
}
static inline size_t __vsnprintf__strlen(const char *str, int is_alt, int allow_space)
{
size_t ret = 0;
if (!str) {
if (is_alt) {
return strlen("-");
} else {
return strlen("(null)");
}
}
for (; *str; str++) {
if (__vsnprintf__is_print(*str, allow_space)) {
ret += 1;
} else {
ret += 4;
}
}
if (is_alt) {
ret += 2;
}
return ret;
}
void igloo_private__vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
static const char hextable[] = "0123456789abcdef";
int in_block = 0;
int block_size = 0;
int block_len = 0;
int block_space = 0;
int block_alt = 0;
const char * arg;
char buf[80];
for (; *format && size; format++)
{
if ( !in_block )
{
if ( *format == '%' ) {
in_block = 1;
block_size = 0;
block_len = 0;
block_space = 0;
block_alt = 0;
}
else
{
*(str++) = *format;
size--;
}
}
else
{
// TODO: %l*[sdupi] as well as %.4080s and "%.*s
arg = NULL;
switch (*format)
{
case 'l':
block_size++;
break;
case 'z':
block_size = 'z';
break;
case '.':
// just ignore '.'. If somebody cares: fix it.
break;
case '*':
block_len = va_arg(ap, int);
break;
case ' ':
block_space = 1;
break;
case '#':
block_alt = 1;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
block_len = atoi(format);
for (; *format >= '0' && *format <= '9'; format++);
format--;
break;
case 'p':
snprintf(buf, sizeof(buf), "%p", (void*)va_arg(ap, void *));
arg = buf;
case 'd':
case 'i':
case 'u':
if (!arg)
{
switch (block_size)
{
case 0:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%u", (unsigned int)va_arg(ap, unsigned int));
else
snprintf(buf, sizeof(buf), "%i", (int)va_arg(ap, int));
break;
case 1:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%lu", (unsigned long int)va_arg(ap, unsigned long int));
else
snprintf(buf, sizeof(buf), "%li", (long int)va_arg(ap, long int));
break;
case 2:
if (*format == 'u')
snprintf(buf, sizeof(buf), "%llu", (unsigned long long int)va_arg(ap, unsigned long long int));
else
snprintf(buf, sizeof(buf), "%lli", (long long int)va_arg(ap, long long int));
break;
case 'z':
/* We do not use 'z' type of snprintf() here as it is not safe to use on a few outdated platforms. */
if (*format == 'u')
snprintf(buf, sizeof(buf), "%llu", (unsigned long long int)va_arg(ap, size_t));
else
snprintf(buf, sizeof(buf), "%lli", (long long int)va_arg(ap, ssize_t));
break;
default:
snprintf(buf, sizeof(buf), "<<<invalid>>>");
break;
}
arg = buf;
}
case 's':
case 'H':
// TODO.
if (!arg)
arg = va_arg(ap, const char *);
if (*format != 'H') {
block_alt = 0;
}
if (!arg && !block_alt)
arg = "(null)";
if (!block_len) {
block_len = __vsnprintf__strlen(arg, block_alt, block_space);
}
// the if() is the outer structure so the inner for()
// is branch optimized.
if (*format == 'H' && !arg)
{
if (size && block_len) {
*(str++) = '-';
size--;
block_len--;
}
}
else if (*format == 'H')
{
if (block_alt && size && block_len) {
*(str++) = '"';
size--;
block_len--;
}
for (; *arg && block_len && size; arg++, size--, block_len--)
{
if (!__vsnprintf__is_print(*arg, block_space)) {
if (size < 4 || block_len < 4) {
/* Use old system if we do not have space for new one */
*(str++) = '.';
} else {
*(str++) = '\\';
*(str++) = 'x';
*(str++) = hextable[(*arg >> 0) & 0x0F];
*(str++) = hextable[(*arg >> 4) & 0x0F];
/* Also count the additional chars for string size and block length */
size -= 3;
block_len -= 3;
}
} else {
*(str++) = *arg;
}
}
if (block_alt && size && block_len) {
*(str++) = '"';
size--;
block_len--;
}
}
else
{
for (; *arg && block_len && size; arg++, size--, block_len--)
*(str++) = *arg;
}
in_block = 0;
break;
}
}
}
if ( !size )
str--;
*str = 0;
}

View File

@ -19,6 +19,10 @@
#ifndef _LIBIGLOO__PRIVATE_H_
#define _LIBIGLOO__PRIVATE_H_
#ifdef STDC_HEADERS
#include <stdarg.h>
#endif
#include <igloo/interface.h>
/* init/shutdown of the library */
@ -55,4 +59,6 @@ void igloo_interface_base_free(igloo_ro_t self);
igloo_ro_t igloo_interface_base_new_real(const igloo_ro_type_t *type, size_t description_length, const igloo_interface_base_ifdesc_t *ifdesc, igloo_ro_t backend_object, void *backend_userdata, const char *name, igloo_ro_t associated);
#define igloo_interface_base_new(type, ifdesc, backend_object, backend_userdata, name, associated) igloo_RO_TO_TYPE(igloo_interface_base_new_real(igloo_ro__type__ ## type, sizeof(*(ifdesc)), (const igloo_interface_base_ifdesc_t*)(ifdesc), (backend_object), (backend_userdata), (name), (associated)), type)
void igloo_private__vsnprintf(char *str, size_t size, const char *format, va_list ap);
#endif