]> mj.ucw.cz Git - libucw.git/blob - ucw/log-conf.c
Logging: Type-based filters and logging of type names are configurable.
[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/conf.h"
13 #include "ucw/simple-lists.h"
14
15 #include <string.h>
16 #include <syslog.h>
17
18 struct stream_config {
19   cnode n;
20   char *name;
21   char *file_name;
22   char *syslog_facility;
23   u32 levels;
24   clist types;                          // simple_list of names
25   clist substreams;                     // simple_list of names
26   int microseconds;                     // Enable logging of precise timestamps
27   int show_types;
28   int syslog_pids;
29   int errors_fatal;
30   struct log_stream *ls;
31   int mark;                             // Used temporarily in log_config_commit()
32 };
33
34 static char *
35 stream_init(void *ptr)
36 {
37   struct stream_config *c = ptr;
38
39   c->levels = ~0U;
40   return NULL;
41 }
42
43 static char *
44 stream_commit(void *ptr)
45 {
46   struct stream_config *c = ptr;
47
48   if (c->file_name && c->syslog_facility)
49     return "Both FileName and SyslogFacility selected";
50   if (c->syslog_facility && !log_syslog_facility_exists(c->syslog_facility))
51     return cf_printf("SyslogFacility `%s' is not recognized", c->syslog_facility);
52   if (c->syslog_facility && c->microseconds)
53     return "Syslog streams do not support microsecond precision";
54   return NULL;
55 }
56
57 static const char * const level_names[] = {
58 #define P(x) #x,
59   LOG_LEVEL_NAMES
60 #undef P
61   NULL
62 };
63
64 static struct cf_section stream_config = {
65   CF_TYPE(struct stream_config),
66   CF_INIT(stream_init),
67   CF_COMMIT(stream_commit),
68   CF_ITEMS {
69 #define P(x) PTR_TO(struct stream_config, x)
70     CF_STRING("Name", P(name)),
71     CF_STRING("FileName", P(file_name)),
72     CF_STRING("SyslogFacility", P(syslog_facility)),
73     CF_BITMAP_LOOKUP("Levels", P(levels), level_names),
74     CF_LIST("Types", P(types), &cf_string_list_config),
75     CF_LIST("Substream", P(substreams), &cf_string_list_config),
76     CF_INT("Microseconds", P(microseconds)),
77     CF_INT("ShowTypes", P(show_types)),
78     CF_INT("SyslogPID", P(syslog_pids)),
79     CF_INT("ErrorsFatal", P(errors_fatal)),
80 #undef P
81     CF_END
82   }
83 };
84
85 static clist log_stream_confs;
86
87 static struct stream_config *
88 stream_find(const char *name)
89 {
90   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
91     if (!strcmp(c->name, name))
92       return c;
93   return NULL;
94 }
95
96 static char *
97 stream_resolve(struct stream_config *c)
98 {
99   if (c->mark == 2)
100     return NULL;
101   if (c->mark == 1)
102     return cf_printf("Log stream `%s' has substreams which refer to itself", c->name);
103
104   c->mark = 1;
105   char *err;
106   CLIST_FOR_EACH(simp_node *, s, c->substreams)
107     {
108       struct stream_config *d = stream_find(s->s);
109       if (!d)
110         return cf_printf("Log stream `%s' refers to unknown substream `%s'", c->name, s->s);
111       if (err = stream_resolve(d))
112         return err;
113     }
114   c->mark = 2;
115   return NULL;
116 }
117
118 static char *
119 log_config_commit(void *ptr UNUSED)
120 {
121   // Verify uniqueness of names
122   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
123     if (stream_find(c->name) != c)
124       return cf_printf("Log stream `%s' defined twice", c->name);
125
126   // Check that all substreams resolve and that there are no cycles
127   char *err;
128   CLIST_FOR_EACH(struct stream_config *, c, log_stream_confs)
129     if (err = stream_resolve(c))
130       return err;
131
132   return NULL;
133 }
134
135 static struct cf_section log_config = {
136   CF_COMMIT(log_config_commit),
137   CF_ITEMS {
138     CF_LIST("Stream", &log_stream_confs, &stream_config),
139     CF_END
140   }
141 };
142
143 static void CONSTRUCTOR
144 log_config_init(void)
145 {
146   cf_declare_section("Logging", &log_config, 0);
147 }
148
149 char *
150 log_check_configured(const char *name)
151 {
152   if (stream_find(name))
153     return NULL;
154   else
155     return cf_printf("Log stream `%s' not found", name);
156 }
157
158 static struct log_stream *
159 do_new_configured(struct stream_config *c)
160 {
161   struct log_stream *ls;
162   ASSERT(c);
163
164   if (c->ls)
165     return c->ls;
166
167   if (c->file_name)
168     ls = log_new_file(c->file_name);
169   else if (c->syslog_facility)
170     ls = log_new_syslog(c->syslog_facility, (c->syslog_pids ? LOG_PID : 0));
171   else
172     ls = log_new_stream(sizeof(*ls));
173
174   CLIST_FOR_EACH(simp_node *, s, c->substreams)
175     log_add_substream(ls, do_new_configured(stream_find(s->s)));
176
177   ls->levels = c->levels;
178   if (c->microseconds)
179     ls->msgfmt |= LSFMT_USEC;
180   if (c->show_types)
181     ls->msgfmt |= LSFMT_TYPE;
182   if (c->errors_fatal)
183     ls->stream_flags |= LSFLAG_ERR_IS_FATAL;
184
185   if (!clist_empty(&c->types))
186     {
187       ls->types = 0;
188       CLIST_FOR_EACH(simp_node *, s, c->types)
189         if (!strcmp(s->s, "all"))
190           ls->types = ~0U;
191         else
192           {
193             /*
194              *  We intentionally ignore unknown types as not all types are known
195              *  to all programs sharing a common configuration file. This is also
196              *  the reason why Types is a list and not a bitmap.
197              */
198             int type = log_find_type(s->s);
199             if (type >= 0)
200               ls->types |= 1 << LS_GET_TYPE(type);
201           }
202     }
203
204   c->ls = ls;
205   return ls;
206 }
207
208 struct log_stream *
209 log_new_configured(const char *name)
210 {
211   struct stream_config *c = stream_find(name);
212   if (!c)
213     die("Unable to find log stream %s", name);
214   if (c->ls)
215     return log_ref_stream(c->ls);
216   return do_new_configured(c);
217 }
218
219 void
220 log_configured(const char *name)
221 {
222   struct log_stream *ls = log_new_configured(name);
223   struct log_stream *def = log_stream_by_flags(0);
224   log_rm_substream(def, NULL);
225   log_add_substream(def, ls);
226   log_close_stream(ls);
227 }
228
229 #ifdef TEST
230
231 #include "ucw/getopt.h"
232
233 int main(int argc, char **argv)
234 {
235   log_init(argv[0]);
236   int c;
237   while ((c = cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL)) >= 0)
238     die("No options here.");
239
240   int type = log_register_type("foo");
241   struct log_stream *ls = log_new_configured("combined");
242   msg(L_INFO | ls->regnum | type, "Hello, universe!");
243
244   log_close_all();
245   return 0;
246 }
247
248 #endif