X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=ucw%2Flog-conf.c;h=26d7d5c46d106eb8bb136b0275e9a8c959c9e44e;hb=305df0095d05525e1324cbc7c74d764535338b07;hp=b6fbbb509325fc85f9e022dce72deb8ce3624af2;hpb=369e3caec4caee65ae4c16d5c04b0f43a50a3150;p=libucw.git diff --git a/ucw/log-conf.c b/ucw/log-conf.c index b6fbbb50..26d7d5c4 100644 --- a/ucw/log-conf.c +++ b/ucw/log-conf.c @@ -9,34 +9,52 @@ #include "ucw/lib.h" #include "ucw/log.h" +#include "ucw/log-internal.h" #include "ucw/conf.h" #include "ucw/simple-lists.h" +#include "ucw/tbf.h" +#include "ucw/threads.h" +#include #include #include +#include + +/*** Configuration of streams ***/ struct stream_config { cnode n; char *name; char *file_name; + int file_desc; char *syslog_facility; u32 levels; clist types; // simple_list of names clist substreams; // simple_list of names + clist limits; // of struct limit_config's int microseconds; // Enable logging of precise timestamps int show_types; int syslog_pids; int errors_fatal; + int stderr_follows; struct log_stream *ls; int mark; // Used temporarily in log_config_commit() }; +struct limit_config { + cnode n; + clist types; // simple_list of names + double rate; + uns burst; +}; + static char * stream_init(void *ptr) { struct stream_config *c = ptr; c->levels = ~0U; + c->file_desc = -1; return NULL; } @@ -45,12 +63,17 @@ stream_commit(void *ptr) { struct stream_config *c = ptr; - if (c->file_name && c->syslog_facility) - return "Both FileName and SyslogFacility selected"; - if (c->syslog_facility && !log_syslog_facility_exists(c->syslog_facility)) - return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility); - if (c->syslog_facility && c->microseconds) - return "Syslog streams do not support microsecond precision"; + if (c->syslog_facility) + { + if (!log_syslog_facility_exists(c->syslog_facility)) + return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility); + if (c->file_name) + return "Both FileName and SyslogFacility selected"; + if (c->microseconds) + return "Syslog streams do not support microsecond precision"; + } + if (c->stderr_follows && !c->file_name) + return "StdErrFollows requires a file-based stream"; return NULL; } @@ -61,6 +84,18 @@ static const char * const level_names[] = { NULL }; +static struct cf_section limit_config = { + CF_TYPE(struct limit_config), + CF_ITEMS { +#define P(x) PTR_TO(struct limit_config, x) + CF_LIST("Types", P(types), &cf_string_list_config), + CF_DOUBLE("Rate", P(rate)), + CF_UNS("Burst", P(burst)), +#undef P + CF_END + } +}; + static struct cf_section stream_config = { CF_TYPE(struct stream_config), CF_INIT(stream_init), @@ -69,14 +104,17 @@ static struct cf_section stream_config = { #define P(x) PTR_TO(struct stream_config, x) CF_STRING("Name", P(name)), CF_STRING("FileName", P(file_name)), + CF_INT("FileDesc", P(file_desc)), CF_STRING("SyslogFacility", P(syslog_facility)), CF_BITMAP_LOOKUP("Levels", P(levels), level_names), CF_LIST("Types", P(types), &cf_string_list_config), CF_LIST("Substream", P(substreams), &cf_string_list_config), + CF_LIST("Limit", P(limits), &limit_config), CF_INT("Microseconds", P(microseconds)), CF_INT("ShowTypes", P(show_types)), CF_INT("SyslogPID", P(syslog_pids)), CF_INT("ErrorsFatal", P(errors_fatal)), + CF_INT("StdErrFollows", P(stderr_follows)), #undef P CF_END } @@ -146,6 +184,98 @@ log_config_init(void) cf_declare_section("Logging", &log_config, 0); } +/*** Type sets ***/ + +static uns +log_type_mask(clist *l) +{ + if (clist_empty(l)) + return ~0U; + + uns types = 0; + CLIST_FOR_EACH(simp_node *, s, *l) + if (!strcmp(s->s, "all")) + return ~0U; + else + { + /* + * We intentionally ignore unknown types as not all types are known + * to all programs sharing a common configuration file. This is also + * the reason why Types is a list and not a bitmap. + */ + int type = log_find_type(s->s); + if (type >= 0) + types |= 1 << LS_GET_TYPE(type); + } + return types; +} + +/*** Generating limiters ***/ + +/* + * When limiting is enabled, we let log_stream->filter point to this function + * and log_stream->user_data point to an array of pointers to token bucket + * filters for individual message types. + */ +static int +log_limiter(struct log_stream *ls, struct log_msg *m) +{ + struct token_bucket_filter **limits = ls->user_data; + if (!limits) + return 0; + struct token_bucket_filter *tbf = limits[LS_GET_TYPE(m->flags)]; + if (!tbf) + return 0; + + ASSERT(!(m->flags & L_SIGHANDLER)); + if (m->flags & L_LOGGER_ERR) + return 0; + + timestamp_t now = ((timestamp_t) m->tv->tv_sec * 1000) + (m->tv->tv_usec / 1000); + ucwlib_lock(); + int res = tbf_limit(tbf, now); + ucwlib_unlock(); + + if (res < 0) + { + if (res == -1) + { + struct log_msg mm = *m; + mm.flags |= L_LOGGER_ERR; + mm.raw_msg = "(maximum logging rate exceeded, some messages will be suppressed)"; + log_pass_msg(0, ls, &mm); + } + return 1; + } + else + return 0; +} + +static void +log_apply_limits(struct log_stream *ls, struct limit_config *lim) +{ + uns mask = log_type_mask(&lim->types); + if (!mask) + return; + + if (!ls->user_data) + { + ls->user_data = cf_malloc_zero(LS_NUM_TYPES * sizeof(struct token_bucket_filter *)); + ls->filter = log_limiter; + } + struct token_bucket_filter **limits = ls->user_data; + struct token_bucket_filter *tbf = cf_malloc_zero(sizeof(*lim)); + tbf->rate = lim->rate; + tbf->burst = lim->burst; + tbf_init(tbf); + + for (uns i=0; i < LS_NUM_TYPES; i++) + if (mask & (1 << i)) + limits[i] = tbf; +} + +/*** Generating streams ***/ + char * log_check_configured(const char *name) { @@ -165,7 +295,9 @@ do_new_configured(struct stream_config *c) return c->ls; if (c->file_name) - ls = log_new_file(c->file_name); + ls = log_new_file(c->file_name, (c->stderr_follows ? FF_FD2_FOLLOWS : 0)); + else if (c->file_desc >= 0) + ls = log_new_fd(c->file_desc, (c->stderr_follows ? FF_FD2_FOLLOWS : 0)); else if (c->syslog_facility) ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0)); else @@ -181,25 +313,10 @@ do_new_configured(struct stream_config *c) ls->msgfmt |= LSFMT_TYPE; if (c->errors_fatal) ls->stream_flags |= LSFLAG_ERR_IS_FATAL; + ls->types = log_type_mask(&c->types); - if (!clist_empty(&c->types)) - { - ls->types = 0; - CLIST_FOR_EACH(simp_node *, s, c->types) - if (!strcmp(s->s, "all")) - ls->types = ~0U; - else - { - /* - * We intentionally ignore unknown types as not all types are known - * to all programs sharing a common configuration file. This is also - * the reason why Types is a list and not a bitmap. - */ - int type = log_find_type(s->s); - if (type >= 0) - ls->types |= 1 << LS_GET_TYPE(type); - } - } + CLIST_FOR_EACH(struct limit_config *, lim, c->limits) + log_apply_limits(ls, lim); c->ls = ls; return ls; @@ -228,6 +345,7 @@ log_configured(const char *name) #ifdef TEST +#include #include "ucw/getopt.h" int main(int argc, char **argv) @@ -239,7 +357,12 @@ int main(int argc, char **argv) int type = log_register_type("foo"); struct log_stream *ls = log_new_configured("combined"); - msg(L_INFO | ls->regnum | type, "Hello, universe!"); + for (uns i=0; i<10; i++) + { + msg(L_INFO | ls->regnum | type, "Hello, universe!"); + usleep(200000); + } + fprintf(stderr, "Alas, this was printed to stderr.\n"); log_close_all(); return 0;