blob: d61895ee5203c29c754a9d434980eb52c640fb2c [file] [log] [blame]
Radek Krejci5aeea3a2018-09-05 13:29:36 +02001/**
2 * @file log.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Logger routines implementations
5 *
6 * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
15#define _GNU_SOURCE
16#include <assert.h>
17#include <stdarg.h>
18#include <stdio.h>
19
20#include "libyang.h"
21#include "common.h"
22#include "context.h"
23
24THREAD_LOCAL enum int_log_opts log_opt;
25volatile uint8_t ly_log_level = LY_LLWRN;
26volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
27static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
28static volatile int path_flag = 1;
29#ifndef NDEBUG
30volatile int ly_log_dbg_groups = 0;
31#endif
32
33API LY_VECODE
34ly_vecode(const struct ly_ctx *ctx)
35{
36 struct ly_err_item *i;
37
38 i = ly_err_first(ctx);
39 if (i) {
40 return i->prev->vecode;
41 }
42
43 return LYVE_SUCCESS;
44}
45
46API const char *
47ly_errmsg(const struct ly_ctx *ctx)
48{
49 struct ly_err_item *i;
50
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020051 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020052
53 i = ly_err_first(ctx);
54 if (i) {
55 return i->prev->msg;
56 }
57
58 return NULL;
59}
60
61API const char *
62ly_errpath(const struct ly_ctx *ctx)
63{
64 struct ly_err_item *i;
65
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020066 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020067
68 i = ly_err_first(ctx);
69 if (i) {
70 return i->prev->path;
71 }
72
73 return NULL;
74}
75
76API const char *
77ly_errapptag(const struct ly_ctx *ctx)
78{
79 struct ly_err_item *i;
80
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020081 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020082
83 i = ly_err_first(ctx);
84 if (i) {
85 return i->prev->apptag;
86 }
87
88 return NULL;
89}
90
91API struct ly_err_item *
92ly_err_first(const struct ly_ctx *ctx)
93{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020094 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020095
96 return pthread_getspecific(ctx->errlist_key);
97}
98
99void
100ly_err_free(void *ptr)
101{
102 struct ly_err_item *i, *next;
103
104 /* clean the error list */
105 for (i = (struct ly_err_item *)ptr; i; i = next) {
106 next = i->next;
107 free(i->msg);
108 free(i->path);
109 free(i->apptag);
110 free(i);
111 }
112}
113
114API void
115ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
116{
117 struct ly_err_item *i, *first;
118
119 first = ly_err_first(ctx);
120 if (first == eitem) {
121 eitem = NULL;
122 }
123 if (eitem) {
124 /* disconnect the error */
125 for (i = first; i && (i->next != eitem); i = i->next);
126 assert(i);
127 i->next = NULL;
128 first->prev = i;
129 /* free this err and newer */
130 ly_err_free(eitem);
131 } else {
132 /* free all err */
133 ly_err_free(first);
134 pthread_setspecific(ctx->errlist_key, NULL);
135 }
136}
137
138API LY_LOG_LEVEL
139ly_verb(LY_LOG_LEVEL level)
140{
141 LY_LOG_LEVEL prev = ly_log_level;
142
143 ly_log_level = level;
144 return prev;
145}
146
147API int
148ly_log_options(int opts)
149{
150 uint8_t prev = ly_log_opts;
151
152 ly_log_opts = opts;
153 return prev;
154}
155
156API void
157ly_verb_dbg(int dbg_groups)
158{
159#ifndef NDEBUG
160 ly_log_dbg_groups = dbg_groups;
161#else
162 (void)dbg_groups;
163#endif
164}
165
166API void
167ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
168{
169 ly_log_clb = clb;
170 path_flag = path;
171}
172
173API void
174(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
175{
176 return ly_log_clb;
177}
178
179static LY_ERR
180log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
181{
182 struct ly_err_item *eitem, *last;
183
184 assert(ctx && (level < LY_LLVRB));
185
186 eitem = pthread_getspecific(ctx->errlist_key);
187 if (!eitem) {
188 /* if we are only to fill in path, there must have been an error stored */
189 assert(msg);
190 eitem = malloc(sizeof *eitem);
191 LY_CHECK_GOTO(!eitem, mem_fail);
192 eitem->prev = eitem;
193 eitem->next = NULL;
194
195 pthread_setspecific(ctx->errlist_key, eitem);
196 } else if (!msg) {
197 /* only filling the path */
198 assert(path);
199
200 /* find last error */
201 eitem = eitem->prev;
202 do {
203 if (eitem->level == LY_LLERR) {
204 /* fill the path */
205 free(eitem->path);
206 eitem->path = path;
207 return LY_SUCCESS;
208 }
209 eitem = eitem->prev;
210 } while (eitem->prev->next);
211 /* last error was not found */
212 assert(0);
213 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
214 /* overwrite last message */
215 free(eitem->msg);
216 free(eitem->path);
217 free(eitem->apptag);
218 } else {
219 /* store new message */
220 last = eitem->prev;
221 eitem->prev = malloc(sizeof *eitem);
222 LY_CHECK_GOTO(!eitem->prev, mem_fail);
223 eitem = eitem->prev;
224 eitem->prev = last;
225 eitem->next = NULL;
226 last->next = eitem;
227 }
228
229 /* fill in the information */
230 eitem->level = level;
231 eitem->no = no;
232 eitem->vecode = vecode;
233 eitem->msg = msg;
234 eitem->path = path;
235 eitem->apptag = apptag;
236 return LY_SUCCESS;
237
238mem_fail:
239 LOGMEM(NULL);
240 free(msg);
241 free(path);
242 free(apptag);
243 return LY_EMEM;
244}
245
246static void
247log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
248 const char *format, va_list args)
249{
250 char *msg = NULL;
251 int free_strs;
252
253 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
254 /* change error to warning */
255 level = LY_LLWRN;
256 }
257
258 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
259 /* do not print or store the message */
260 free(path);
261 return;
262 }
263
264 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
265 /* assume we are inheriting the error, so inherit vecode as well */
266 vecode = ly_vecode(ctx);
267 }
268
269 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
270 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
271 if (!format) {
272 assert(path);
273 /* postponed print of path related to the previous error, do not rewrite stored original message */
274 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
275 return;
276 }
277 msg = "Path is related to the previous error message.";
278 } else {
279 if (vasprintf(&msg, format, args) == -1) {
280 LOGMEM(ctx);
281 free(path);
282 return;
283 }
284 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
285 return;
286 }
287 }
288 free_strs = 0;
289 } else {
290 if (vasprintf(&msg, format, args) == -1) {
291 LOGMEM(ctx);
292 free(path);
293 return;
294 }
295 free_strs = 1;
296 }
297
298 /* if we are only storing errors internally, never print the message (yet) */
299 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
300 if (ly_log_clb) {
301 ly_log_clb(level, msg, path);
302 } else {
303 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
304 if (path) {
305 fprintf(stderr, "(path: %s)\n", path);
306 }
307 }
308 }
309
310 if (free_strs) {
311 free(path);
312 free(msg);
313 }
314}
315
Radek Krejci4ab61562018-09-05 15:00:37 +0200316#ifndef NDEBUG
317
318void
319ly_log_dbg(int group, const char *format, ...)
320{
321 char *dbg_format;
322 const char *str_group;
323 va_list ap;
324
325 if (!(ly_log_dbg_groups & group)) {
326 return;
327 }
328
329 switch (group) {
330 case LY_LDGDICT:
331 str_group = "DICT";
332 break;
333 case LY_LDGYANG:
334 str_group = "YANG";
335 break;
336 case LY_LDGYIN:
337 str_group = "YIN";
338 break;
339 case LY_LDGXPATH:
340 str_group = "XPATH";
341 break;
342 case LY_LDGDIFF:
343 str_group = "DIFF";
344 break;
345 default:
346 LOGINT(NULL);
347 return;
348 }
349
350 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
351 LOGMEM(NULL);
352 return;
353 }
354
355 va_start(ap, format);
356 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
357 va_end(ap);
358}
359
360#endif
361
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200362void
363ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
364{
365 va_list ap;
366
367 va_start(ap, format);
368 log_vprintf(ctx, level, no, 0, NULL, format, ap);
369 va_end(ap);
370}
371
372API void
373ly_err_print(struct ly_err_item *eitem)
374{
375 if (ly_log_opts & LY_LOLOG) {
376 if (ly_log_clb) {
377 ly_log_clb(eitem->level, eitem->msg, eitem->path);
378 } else {
379 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
380 if (eitem->path) {
381 fprintf(stderr, "(path: %s)\n", eitem->path);
382 }
383 }
384 }
385}
386