api documentation for ec_parse
[protos/libecoli.git] / src / ecoli_log.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3  */
4
5 #define _GNU_SOURCE /* for vasprintf */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <syslog.h>
11
12 #include <ecoli_malloc.h>
13 #include <ecoli_string.h>
14 #include <ecoli_log.h>
15 #include <ecoli_test.h>
16
17 EC_LOG_TYPE_REGISTER(log);
18
19 static ec_log_t ec_log_fct = ec_log_default_cb;
20 static void *ec_log_opaque;
21
22 struct ec_log_type {
23         char *name;
24         enum ec_log_level level;
25 };
26
27 static struct ec_log_type *log_types;
28 static size_t log_types_len;
29 static enum ec_log_level global_level = EC_LOG_WARNING;
30
31 int ec_log_level_set(enum ec_log_level level)
32 {
33         if (level > EC_LOG_DEBUG)
34                 return -1;
35         global_level = level;
36
37         return 0;
38 }
39
40 enum ec_log_level ec_log_level_get(void)
41 {
42         return global_level;
43 }
44
45 int ec_log_default_cb(int type, enum ec_log_level level, void *opaque,
46                 const char *str)
47 {
48         (void)opaque;
49
50         if (level > ec_log_level_get())
51                 return 0;
52
53         if (fprintf(stderr, "[%d] %-12s %s", level, ec_log_name(type), str) < 0)
54                 return -1;
55
56         return 0;
57 }
58
59 int ec_log_fct_register(ec_log_t usr_log, void *opaque)
60 {
61         if (usr_log == NULL) {
62                 ec_log_fct = ec_log_default_cb;
63                 ec_log_opaque = NULL;
64         } else {
65                 ec_log_fct = usr_log;
66                 ec_log_opaque = opaque;
67         }
68
69         return 0;
70 }
71
72 static int
73 ec_log_lookup(const char *name)
74 {
75         size_t i;
76
77         for (i = 0; i < log_types_len; i++) {
78                 if (log_types[i].name == NULL)
79                         continue;
80                 if (strcmp(name, log_types[i].name) == 0)
81                         return i;
82         }
83
84         return -1;
85 }
86
87 int
88 ec_log_type_register(const char *name)
89 {
90         struct ec_log_type *new_types;
91         char *copy;
92         int id;
93
94         id = ec_log_lookup(name);
95         if (id >= 0)
96                 return id;
97
98         // XXX not that good to allocate in constructor
99         new_types = ec_realloc(log_types,
100                 sizeof(*new_types) * (log_types_len + 1));
101         if (new_types == NULL)
102                 return -1; /* errno is set */
103         log_types = new_types;
104
105         copy = ec_strdup(name);
106         if (copy == NULL)
107                 return -1; /* errno is set */
108
109         id = log_types_len++;
110         log_types[id].name = copy;
111         log_types[id].level = EC_LOG_DEBUG;
112
113         return id;
114 }
115
116 const char *
117 ec_log_name(int type)
118 {
119         if (type < 0 || (unsigned int)type >= log_types_len)
120                 return "unknown";
121         return log_types[type].name;
122 }
123
124 int ec_vlog(int type, enum ec_log_level level, const char *format, va_list ap)
125 {
126         char *s;
127         int ret;
128
129         /* don't use ec_vasprintf here, because it will call
130          * ec_malloc(), then ec_log(), ec_vasprintf()...
131          * -> stack overflow */
132         ret = vasprintf(&s, format, ap);
133         if (ret < 0)
134                 return ret;
135
136         ret = ec_log_fct(type, level, ec_log_opaque, s);
137         free(s);
138
139         return ret;
140 }
141
142 int ec_log(int type, enum ec_log_level level, const char *format, ...)
143 {
144         va_list ap;
145         int ret;
146
147         va_start(ap, format);
148         ret = ec_vlog(type, level, format, ap);
149         va_end(ap);
150
151         return ret;
152 }
153
154 /* LCOV_EXCL_START */
155 static int
156 log_cb(int type, enum ec_log_level level, void *opaque, const char *str)
157 {
158         (void)type;
159         (void)level;
160         (void)str;
161         *(int *)opaque = 1;
162
163         return 0;
164 }
165
166 static int ec_log_testcase(void)
167 {
168         ec_log_t prev_log_cb;
169         void *prev_opaque;
170         const char *logname;
171         int testres = 0;
172         int check_cb = 0;
173         int logtype;
174         int level;
175         int ret;
176
177         prev_log_cb = ec_log_fct;
178         prev_opaque = ec_log_opaque;
179
180         ret = ec_log_fct_register(log_cb, &check_cb);
181         testres |= EC_TEST_CHECK(ret == 0,
182                                 "cannot register log function\n");
183         EC_LOG(LOG_ERR, "test\n");
184         testres |= EC_TEST_CHECK(check_cb == 1,
185                                 "log callback was not invoked\n");
186         logtype = ec_log_lookup("dsdedesdes");
187         testres |= EC_TEST_CHECK(logtype == -1,
188                                 "lookup invalid name should return -1");
189         logtype = ec_log_lookup("log");
190         logname = ec_log_name(logtype);
191         testres |= EC_TEST_CHECK(logname != NULL &&
192                                 !strcmp(logname, "log"),
193                                 "cannot get log name\n");
194         logname = ec_log_name(-1);
195         testres |= EC_TEST_CHECK(logname != NULL &&
196                                 !strcmp(logname, "unknown"),
197                                 "cannot get invalid log name\n");
198         logname = ec_log_name(34324);
199         testres |= EC_TEST_CHECK(logname != NULL &&
200                                 !strcmp(logname, "unknown"),
201                                 "cannot get invalid log name\n");
202         level = ec_log_level_get();
203         ret = ec_log_level_set(2);
204         testres |= EC_TEST_CHECK(ret == 0 && ec_log_level_get() == 2,
205                                 "cannot set log level\n");
206         ret = ec_log_level_set(10);
207         testres |= EC_TEST_CHECK(ret != 0,
208                                 "should not be able to set log level\n");
209
210         ret = ec_log_fct_register(NULL, NULL);
211         ec_log_level_set(LOG_DEBUG);
212         EC_LOG(LOG_DEBUG, "test log\n");
213         ec_log_level_set(LOG_INFO);
214         EC_LOG(LOG_DEBUG, "test log (not displayed)\n");
215         ec_log_level_set(level);
216
217         ec_log_fct = prev_log_cb;
218         ec_log_opaque = prev_opaque;
219
220         return testres;
221 }
222 /* LCOV_EXCL_STOP */
223
224 static struct ec_test ec_log_test = {
225         .name = "log",
226         .test = ec_log_testcase,
227 };
228
229 EC_TEST_REGISTER(ec_log_test);