]> mj.ucw.cz Git - libucw.git/blob - ucw/log-conf.c
Config: Added support for terabyte values, for example "123T".
[libucw.git] / ucw / log-conf.c
1 /*
2  *      UCW Library -- Logging: Configuration of Log Streams
3  *
4  *      (c) 2009 Martin Mares <mj@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/log.h>
12 #include <ucw/log-internal.h>
13 #include <ucw/conf.h>
14 #include <ucw/simple-lists.h>
15 #include <ucw/tbf.h>
16 #include <ucw/threads.h>
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <syslog.h>
21 #include <sys/time.h>
22
23 #ifndef TEST
24
25 /*** Configuration of streams ***/
26
27 struct stream_config {
28   cnode n;
29   char *name;
30   char *file_name;
31   int file_desc;
32   char *syslog_facility;
33   u32 levels;
34   clist types;                          // simple_list of names
35   clist substreams;                     // simple_list of names
36   clist limits;                         // of struct limit_config's
37   int microseconds;                     // Enable logging of precise timestamps
38   int show_types;
39   int syslog_pids;
40   int errors_fatal;
41   int stderr_follows;
42   struct log_stream *ls;
43   int mark;                             // Used temporarily in log_config_commit()
44 };
45
46 struct limit_config {
47   cnode n;
48   clist types;                          // simple_list of names
49   double rate;
50   uint burst;
51 };
52
53 static char *
54 stream_init(void *ptr)
55 {
56   struct stream_config *c = ptr;
57
58   c->levels = ~0U;
59   c->file_desc = -1;
60   return NULL;
61 }
62
63 static char *
64 stream_commit(void *ptr)
65 {
66   struct stream_config *c = ptr;
67
68   if (c->syslog_facility)
69     {
70       if (!log_syslog_facility_exists(c->syslog_facility))
71         return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility);
72       if (c->file_name)
73         return "Both FileName and SyslogFacility selected";
74       if (c->microseconds)
75         return "Syslog streams do not support microsecond precision";
76     }
77   if (c->stderr_follows && !c->file_name)
78     return "StdErrFollows requires a file-based stream";
79   return NULL;
80 }
81
82 static const char * const level_names[] = {
83 #define P(x) #x,
84   LOG_LEVEL_NAMES
85 #undef P
86   NULL
87 };
88
89 static struct cf_section limit_config = {
90   CF_TYPE(struct limit_config),
91   CF_ITEMS {
92 #define P(x) PTR_TO(struct limit_config, x)
93     CF_LIST("Types", P(types), &cf_string_list_config),
94     CF_DOUBLE("Rate", P(rate)),
95     CF_UINT("Burst", P(burst)),
96 #undef P
97     CF_END
98   }
99 };
100
101 static struct cf_section stream_config = {
102   CF_TYPE(struct stream_config),
103   CF_INIT(stream_init),
104   CF_COMMIT(stream_commit),
105   CF_ITEMS {
106 #define P(x) PTR_TO(struct stream_config, x)
107     CF_STRING("Name", P(name)),
108     CF_STRING("FileName", P(file_name)),
109     CF_INT("FileDesc", P(file_desc)),
110     CF_STRING("SyslogFacility", P(syslog_facility)),
111     CF_BITMAP_LOOKUP("Levels", P(levels), level_names),
112     CF_LIST("Types", P(types), &cf_string_list_config),
113     CF_LIST("Substream", P(substreams), &cf_string_list_config),
114     CF_LIST("Limit", P(limits), &limit_config),
115     CF_INT("Microseconds", P(microseconds)),
116     CF_INT("ShowTypes", P(show_types)),
117     CF_INT("SyslogPID", P(syslog_pids)),
118     CF_INT("ErrorsFatal", P(errors_fatal)),
119     CF_INT("StdErrFollows", P(stderr_follows)),
120 #undef P
121     CF_END
122   }
123 };
124
125 static clist log_stream_confs;
126
127 static struct stream_config *
128 stream_find(const char *name)
129 {
130   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
131     if (!strcmp(c->name, name))
132       return c;
133   return NULL;
134 }
135
136 static char *
137 stream_resolve(struct stream_config *c)
138 {
139   if (c->mark == 2)
140     return NULL;
141   if (c->mark == 1)
142     return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
143
144   c->mark = 1;
145   char *err;
146   CLIST_FOR_EACH(simp_node *, s, c->substreams)
147     {
148       struct stream_config *d = stream_find(s->s);
149       if (!d)
150         return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
151       if (err = stream_resolve(d))
152         return err;
153     }
154   c->mark = 2;
155   return NULL;
156 }
157
158 static char *
159 log_config_commit(void *ptr UNUSED)
160 {
161   // Verify uniqueness of names
162   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
163     if (stream_find(c->name) != c)
164       return cf_printf("Log stream `%s' defined twice", c->name);
165
166   // Check that all substreams resolve and that there are no cycles
167   char *err;
168   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
169     if (err = stream_resolve(c))
170       return err;
171
172   return NULL;
173 }
174
175 static struct cf_section log_config = {
176   CF_COMMIT(log_config_commit),
177   CF_ITEMS {
178     CF_LIST("Stream", &log_stream_confs, &stream_config),
179     CF_END
180   }
181 };
182
183 static void CONSTRUCTOR
184 log_config_init(void)
185 {
186   cf_declare_section("Logging", &log_config, 0);
187 }
188
189 /*** Type sets ***/
190
191 static uint
192 log_type_mask(clist *l)
193 {
194   if (clist_empty(l))
195     return ~0U;
196
197   uint types = 0;
198   CLIST_FOR_EACH(simp_node *, s, *l)
199     if (!strcmp(s->s, "all"))
200       return ~0U;
201     else
202       {
203         /*
204          *  We intentionally ignore unknown types as not all types are known
205          *  to all programs sharing a common configuration file. This is also
206          *  the reason why Types is a list and not a bitmap.
207          */
208         int type = log_find_type(s->s);
209         if (type >= 0)
210           types |= 1 << LS_GET_TYPE(type);
211       }
212   return types;
213 }
214
215 /*** Generating limiters ***/
216
217 /*
218  *  When limiting is enabled, we let log_stream->filter point to this function
219  *  and log_stream->user_data point to an array of pointers to token bucket
220  *  filters for individual message types.
221  */
222 static int
223 log_limiter(struct log_stream *ls, struct log_msg *m)
224 {
225   struct token_bucket_filter **limits = ls->user_data;
226   if (!limits)
227     return 0;
228   struct token_bucket_filter *tbf = limits[LS_GET_TYPE(m->flags)];
229   if (!tbf)
230     return 0;
231
232   ASSERT(!(m->flags & L_SIGHANDLER));
233   if (m->flags & L_LOGGER_ERR)
234     return 0;
235
236   timestamp_t now = ((timestamp_t) m->tv->tv_sec * 1000) + (m->tv->tv_usec / 1000);
237   ucwlib_lock();
238   int res = tbf_limit(tbf, now);
239   ucwlib_unlock();
240
241   if (res < 0)
242     {
243       if (res == -1)
244         {
245           struct log_msg mm = *m;
246           mm.flags |= L_LOGGER_ERR;
247           mm.raw_msg = "(maximum logging rate exceeded, some messages will be suppressed)";
248           log_pass_msg(ls, &mm);
249         }
250       return 1;
251     }
252   else
253     return 0;
254 }
255
256 static void
257 log_apply_limits(struct log_stream *ls, struct limit_config *lim)
258 {
259   uint mask = log_type_mask(&lim->types);
260   if (!mask)
261     return;
262
263   if (!ls->user_data)
264     {
265       ls->user_data = cf_malloc_zero(LS_NUM_TYPES * sizeof(struct token_bucket_filter *));
266       ls->filter = log_limiter;
267     }
268   struct token_bucket_filter **limits = ls->user_data;
269   struct token_bucket_filter *tbf = cf_malloc_zero(sizeof(*lim));
270   tbf->rate = lim->rate;
271   tbf->burst = lim->burst;
272   tbf_init(tbf);
273
274   for (uint i=0; i < LS_NUM_TYPES; i++)
275     if (mask & (1 << i))
276       limits[i] = tbf;
277 }
278
279 /*** Generating streams ***/
280
281 char *
282 log_check_configured(const char *name)
283 {
284   if (stream_find(name))
285     return NULL;
286   else
287     return cf_printf("Log stream `%s' not found", name);
288 }
289
290 static struct log_stream *
291 do_new_configured(struct stream_config *c)
292 {
293   struct log_stream *ls;
294   ASSERT(c);
295
296   if (c->ls)
297     return c->ls;
298
299   if (c->file_name)
300     ls = log_new_file(c->file_name, (c->stderr_follows ? FF_FD2_FOLLOWS : 0));
301   else if (c->file_desc >= 0)
302     ls = log_new_fd(c->file_desc, (c->stderr_follows ? FF_FD2_FOLLOWS : 0));
303   else if (c->syslog_facility)
304     ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0));
305   else
306     ls = log_new_stream(sizeof(*ls));
307
308   CLIST_FOR_EACH(simp_node *, s, c->substreams)
309     log_add_substream(ls, do_new_configured(stream_find(s->s)));
310
311   ls->levels = c->levels;
312   if (c->microseconds)
313     ls->msgfmt |= LSFMT_USEC;
314   if (c->show_types)
315     ls->msgfmt |= LSFMT_TYPE;
316   if (c->errors_fatal)
317     ls->stream_flags |= LSFLAG_ERR_IS_FATAL;
318   ls->types = log_type_mask(&c->types);
319
320   CLIST_FOR_EACH(struct limit_config *, lim, c->limits)
321     log_apply_limits(ls, lim);
322
323   c->ls = ls;
324   return ls;
325 }
326
327 struct log_stream *
328 log_new_configured(const char *name)
329 {
330   struct stream_config *c = stream_find(name);
331   if (!c)
332     die("Unable to find log stream %s", name);
333   if (c->ls)
334     return log_ref_stream(c->ls);
335   return do_new_configured(c);
336 }
337
338 void
339 log_configured(const char *name)
340 {
341   log_set_default_stream(log_new_configured(name));
342 }
343
344 #else /* TEST */
345
346 #include <unistd.h>
347 #include <ucw/getopt.h>
348
349 int main(int argc, char **argv)
350 {
351   log_init(argv[0]);
352   int c;
353   while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
354     die("No options here.");
355
356   int type = log_register_type("foo");
357   struct log_stream *ls = log_new_configured("combined");
358   log_drop_stderr();
359   for (uint i=0; i<10; i++)
360     {
361       msg(L_INFO | ls->regnum | type, "Hello, universe!");
362       usleep(200000);
363     }
364   fprintf(stderr, "Alas, this was printed to stderr.\n");
365
366   log_close_all();
367   return 0;
368 }
369
370 #endif