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