2 * UCW Library -- Logging: Configuration of Log Streams
4 * (c) 2009 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
12 #include <ucw/log-internal.h>
14 #include <ucw/simple-lists.h>
16 #include <ucw/threads.h>
23 /*** Configuration of streams ***/
25 struct stream_config {
30 char *syslog_facility;
32 clist types; // simple_list of names
33 clist substreams; // simple_list of names
34 clist limits; // of struct limit_config's
35 int microseconds; // Enable logging of precise timestamps
40 struct log_stream *ls;
41 int mark; // Used temporarily in log_config_commit()
46 clist types; // simple_list of names
52 stream_init(void *ptr)
54 struct stream_config *c = ptr;
62 stream_commit(void *ptr)
64 struct stream_config *c = ptr;
66 if (c->syslog_facility)
68 if (!log_syslog_facility_exists(c->syslog_facility))
69 return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility);
71 return "Both FileName and SyslogFacility selected";
73 return "Syslog streams do not support microsecond precision";
75 if (c->stderr_follows && !c->file_name)
76 return "StdErrFollows requires a file-based stream";
80 static const char * const level_names[] = {
87 static struct cf_section limit_config = {
88 CF_TYPE(struct limit_config),
90 #define P(x) PTR_TO(struct limit_config, x)
91 CF_LIST("Types", P(types), &cf_string_list_config),
92 CF_DOUBLE("Rate", P(rate)),
93 CF_UNS("Burst", P(burst)),
99 static struct cf_section stream_config = {
100 CF_TYPE(struct stream_config),
101 CF_INIT(stream_init),
102 CF_COMMIT(stream_commit),
104 #define P(x) PTR_TO(struct stream_config, x)
105 CF_STRING("Name", P(name)),
106 CF_STRING("FileName", P(file_name)),
107 CF_INT("FileDesc", P(file_desc)),
108 CF_STRING("SyslogFacility", P(syslog_facility)),
109 CF_BITMAP_LOOKUP("Levels", P(levels), level_names),
110 CF_LIST("Types", P(types), &cf_string_list_config),
111 CF_LIST("Substream", P(substreams), &cf_string_list_config),
112 CF_LIST("Limit", P(limits), &limit_config),
113 CF_INT("Microseconds", P(microseconds)),
114 CF_INT("ShowTypes", P(show_types)),
115 CF_INT("SyslogPID", P(syslog_pids)),
116 CF_INT("ErrorsFatal", P(errors_fatal)),
117 CF_INT("StdErrFollows", P(stderr_follows)),
123 static clist log_stream_confs;
125 static struct stream_config *
126 stream_find(const char *name)
128 CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
129 if (!strcmp(c->name, name))
135 stream_resolve(struct stream_config *c)
140 return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
144 CLIST_FOR_EACH(simp_node *, s, c->substreams)
146 struct stream_config *d = stream_find(s->s);
148 return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
149 if (err = stream_resolve(d))
157 log_config_commit(void *ptr UNUSED)
159 // Verify uniqueness of names
160 CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
161 if (stream_find(c->name) != c)
162 return cf_printf("Log stream `%s' defined twice", c->name);
164 // Check that all substreams resolve and that there are no cycles
166 CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
167 if (err = stream_resolve(c))
173 static struct cf_section log_config = {
174 CF_COMMIT(log_config_commit),
176 CF_LIST("Stream", &log_stream_confs, &stream_config),
181 static void CONSTRUCTOR
182 log_config_init(void)
184 cf_declare_section("Logging", &log_config, 0);
190 log_type_mask(clist *l)
196 CLIST_FOR_EACH(simp_node *, s, *l)
197 if (!strcmp(s->s, "all"))
202 * We intentionally ignore unknown types as not all types are known
203 * to all programs sharing a common configuration file. This is also
204 * the reason why Types is a list and not a bitmap.
206 int type = log_find_type(s->s);
208 types |= 1 << LS_GET_TYPE(type);
213 /*** Generating limiters ***/
216 * When limiting is enabled, we let log_stream->filter point to this function
217 * and log_stream->user_data point to an array of pointers to token bucket
218 * filters for individual message types.
221 log_limiter(struct log_stream *ls, struct log_msg *m)
223 struct token_bucket_filter **limits = ls->user_data;
226 struct token_bucket_filter *tbf = limits[LS_GET_TYPE(m->flags)];
230 ASSERT(!(m->flags & L_SIGHANDLER));
231 if (m->flags & L_LOGGER_ERR)
234 timestamp_t now = ((timestamp_t) m->tv->tv_sec * 1000) + (m->tv->tv_usec / 1000);
236 int res = tbf_limit(tbf, now);
243 struct log_msg mm = *m;
244 mm.flags |= L_LOGGER_ERR;
245 mm.raw_msg = "(maximum logging rate exceeded, some messages will be suppressed)";
246 log_pass_msg(0, ls, &mm);
255 log_apply_limits(struct log_stream *ls, struct limit_config *lim)
257 uns mask = log_type_mask(&lim->types);
263 ls->user_data = cf_malloc_zero(LS_NUM_TYPES * sizeof(struct token_bucket_filter *));
264 ls->filter = log_limiter;
266 struct token_bucket_filter **limits = ls->user_data;
267 struct token_bucket_filter *tbf = cf_malloc_zero(sizeof(*lim));
268 tbf->rate = lim->rate;
269 tbf->burst = lim->burst;
272 for (uns i=0; i < LS_NUM_TYPES; i++)
277 /*** Generating streams ***/
280 log_check_configured(const char *name)
282 if (stream_find(name))
285 return cf_printf("Log stream `%s' not found", name);
288 static struct log_stream *
289 do_new_configured(struct stream_config *c)
291 struct log_stream *ls;
298 ls = log_new_file(c->file_name, (c->stderr_follows ? FF_FD2_FOLLOWS : 0));
299 else if (c->file_desc >= 0)
300 ls = log_new_fd(c->file_desc, (c->stderr_follows ? FF_FD2_FOLLOWS : 0));
301 else if (c->syslog_facility)
302 ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0));
304 ls = log_new_stream(sizeof(*ls));
306 CLIST_FOR_EACH(simp_node *, s, c->substreams)
307 log_add_substream(ls, do_new_configured(stream_find(s->s)));
309 ls->levels = c->levels;
311 ls->msgfmt |= LSFMT_USEC;
313 ls->msgfmt |= LSFMT_TYPE;
315 ls->stream_flags |= LSFLAG_ERR_IS_FATAL;
316 ls->types = log_type_mask(&c->types);
318 CLIST_FOR_EACH(struct limit_config *, lim, c->limits)
319 log_apply_limits(ls, lim);
326 log_new_configured(const char *name)
328 struct stream_config *c = stream_find(name);
330 die("Unable to find log stream %s", name);
332 return log_ref_stream(c->ls);
333 return do_new_configured(c);
337 log_configured(const char *name)
339 log_set_default_stream(log_new_configured(name));
345 #include <ucw/getopt.h>
347 int main(int argc, char **argv)
351 while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
352 die("No options here.");
354 int type = log_register_type("foo");
355 struct log_stream *ls = log_new_configured("combined");
356 for (uns i=0; i<10; i++)
358 msg(L_INFO | ls->regnum | type, "Hello, universe!");
361 fprintf(stderr, "Alas, this was printed to stderr.\n");