blob: 18d0b38128c1e9513dcf6d1e9ece6044b9f1623f [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>
Radek Krejcic04f0a22018-09-21 15:49:45 +020017#include <inttypes.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020018#include <stdarg.h>
19#include <stdio.h>
20
21#include "libyang.h"
22#include "common.h"
23#include "context.h"
24
25THREAD_LOCAL enum int_log_opts log_opt;
26volatile uint8_t ly_log_level = LY_LLWRN;
27volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
28static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
29static volatile int path_flag = 1;
30#ifndef NDEBUG
31volatile int ly_log_dbg_groups = 0;
32#endif
33
Radek Krejci94aa9942018-09-07 17:12:17 +020034/* how many bytes add when enlarging buffers */
35#define LY_BUF_STEP 128
36
Radek Krejci5aeea3a2018-09-05 13:29:36 +020037API LY_VECODE
38ly_vecode(const struct ly_ctx *ctx)
39{
40 struct ly_err_item *i;
41
42 i = ly_err_first(ctx);
43 if (i) {
44 return i->prev->vecode;
45 }
46
47 return LYVE_SUCCESS;
48}
49
50API const char *
51ly_errmsg(const struct ly_ctx *ctx)
52{
53 struct ly_err_item *i;
54
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020055 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020056
57 i = ly_err_first(ctx);
58 if (i) {
59 return i->prev->msg;
60 }
61
62 return NULL;
63}
64
65API const char *
66ly_errpath(const struct ly_ctx *ctx)
67{
68 struct ly_err_item *i;
69
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020070 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020071
72 i = ly_err_first(ctx);
73 if (i) {
74 return i->prev->path;
75 }
76
77 return NULL;
78}
79
80API const char *
81ly_errapptag(const struct ly_ctx *ctx)
82{
83 struct ly_err_item *i;
84
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020085 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020086
87 i = ly_err_first(ctx);
88 if (i) {
89 return i->prev->apptag;
90 }
91
92 return NULL;
93}
94
95API struct ly_err_item *
96ly_err_first(const struct ly_ctx *ctx)
97{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020098 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020099
100 return pthread_getspecific(ctx->errlist_key);
101}
102
103void
104ly_err_free(void *ptr)
105{
106 struct ly_err_item *i, *next;
107
108 /* clean the error list */
109 for (i = (struct ly_err_item *)ptr; i; i = next) {
110 next = i->next;
111 free(i->msg);
112 free(i->path);
113 free(i->apptag);
114 free(i);
115 }
116}
117
118API void
119ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
120{
121 struct ly_err_item *i, *first;
122
123 first = ly_err_first(ctx);
124 if (first == eitem) {
125 eitem = NULL;
126 }
127 if (eitem) {
128 /* disconnect the error */
129 for (i = first; i && (i->next != eitem); i = i->next);
130 assert(i);
131 i->next = NULL;
132 first->prev = i;
133 /* free this err and newer */
134 ly_err_free(eitem);
135 } else {
136 /* free all err */
137 ly_err_free(first);
138 pthread_setspecific(ctx->errlist_key, NULL);
139 }
140}
141
142API LY_LOG_LEVEL
143ly_verb(LY_LOG_LEVEL level)
144{
145 LY_LOG_LEVEL prev = ly_log_level;
146
147 ly_log_level = level;
148 return prev;
149}
150
151API int
152ly_log_options(int opts)
153{
154 uint8_t prev = ly_log_opts;
155
156 ly_log_opts = opts;
157 return prev;
158}
159
160API void
161ly_verb_dbg(int dbg_groups)
162{
163#ifndef NDEBUG
164 ly_log_dbg_groups = dbg_groups;
165#else
166 (void)dbg_groups;
167#endif
168}
169
170API void
171ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
172{
173 ly_log_clb = clb;
174 path_flag = path;
175}
176
177API void
178(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
179{
180 return ly_log_clb;
181}
182
183static LY_ERR
184log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
185{
186 struct ly_err_item *eitem, *last;
187
188 assert(ctx && (level < LY_LLVRB));
189
190 eitem = pthread_getspecific(ctx->errlist_key);
191 if (!eitem) {
192 /* if we are only to fill in path, there must have been an error stored */
193 assert(msg);
194 eitem = malloc(sizeof *eitem);
195 LY_CHECK_GOTO(!eitem, mem_fail);
196 eitem->prev = eitem;
197 eitem->next = NULL;
198
199 pthread_setspecific(ctx->errlist_key, eitem);
200 } else if (!msg) {
201 /* only filling the path */
202 assert(path);
203
204 /* find last error */
205 eitem = eitem->prev;
206 do {
207 if (eitem->level == LY_LLERR) {
208 /* fill the path */
209 free(eitem->path);
210 eitem->path = path;
211 return LY_SUCCESS;
212 }
213 eitem = eitem->prev;
214 } while (eitem->prev->next);
215 /* last error was not found */
216 assert(0);
217 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
218 /* overwrite last message */
219 free(eitem->msg);
220 free(eitem->path);
221 free(eitem->apptag);
222 } else {
223 /* store new message */
224 last = eitem->prev;
225 eitem->prev = malloc(sizeof *eitem);
226 LY_CHECK_GOTO(!eitem->prev, mem_fail);
227 eitem = eitem->prev;
228 eitem->prev = last;
229 eitem->next = NULL;
230 last->next = eitem;
231 }
232
233 /* fill in the information */
234 eitem->level = level;
235 eitem->no = no;
236 eitem->vecode = vecode;
237 eitem->msg = msg;
238 eitem->path = path;
239 eitem->apptag = apptag;
240 return LY_SUCCESS;
241
242mem_fail:
243 LOGMEM(NULL);
244 free(msg);
245 free(path);
246 free(apptag);
247 return LY_EMEM;
248}
249
250static void
251log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
252 const char *format, va_list args)
253{
254 char *msg = NULL;
255 int free_strs;
256
257 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
258 /* change error to warning */
259 level = LY_LLWRN;
260 }
261
262 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
263 /* do not print or store the message */
264 free(path);
265 return;
266 }
267
268 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
269 /* assume we are inheriting the error, so inherit vecode as well */
270 vecode = ly_vecode(ctx);
271 }
272
273 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
274 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
275 if (!format) {
276 assert(path);
277 /* postponed print of path related to the previous error, do not rewrite stored original message */
278 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
279 return;
280 }
281 msg = "Path is related to the previous error message.";
282 } else {
283 if (vasprintf(&msg, format, args) == -1) {
284 LOGMEM(ctx);
285 free(path);
286 return;
287 }
288 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
289 return;
290 }
291 }
292 free_strs = 0;
293 } else {
294 if (vasprintf(&msg, format, args) == -1) {
295 LOGMEM(ctx);
296 free(path);
297 return;
298 }
299 free_strs = 1;
300 }
301
302 /* if we are only storing errors internally, never print the message (yet) */
303 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
304 if (ly_log_clb) {
305 ly_log_clb(level, msg, path);
306 } else {
307 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
308 if (path) {
309 fprintf(stderr, "(path: %s)\n", path);
310 }
311 }
312 }
313
314 if (free_strs) {
315 free(path);
316 free(msg);
317 }
318}
319
Radek Krejci4ab61562018-09-05 15:00:37 +0200320#ifndef NDEBUG
321
322void
323ly_log_dbg(int group, const char *format, ...)
324{
325 char *dbg_format;
326 const char *str_group;
327 va_list ap;
328
329 if (!(ly_log_dbg_groups & group)) {
330 return;
331 }
332
333 switch (group) {
334 case LY_LDGDICT:
335 str_group = "DICT";
336 break;
337 case LY_LDGYANG:
338 str_group = "YANG";
339 break;
340 case LY_LDGYIN:
341 str_group = "YIN";
342 break;
343 case LY_LDGXPATH:
344 str_group = "XPATH";
345 break;
346 case LY_LDGDIFF:
347 str_group = "DIFF";
348 break;
349 default:
350 LOGINT(NULL);
351 return;
352 }
353
354 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
355 LOGMEM(NULL);
356 return;
357 }
358
359 va_start(ap, format);
360 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
361 va_end(ap);
362}
363
364#endif
365
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200366void
367ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
368{
369 va_list ap;
370
371 va_start(ap, format);
372 log_vprintf(ctx, level, no, 0, NULL, format, ap);
373 va_end(ap);
374}
375
Radek Krejci94aa9942018-09-07 17:12:17 +0200376static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200377ly_vlog_build_path(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, char **path)
Radek Krejci94aa9942018-09-07 17:12:17 +0200378{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200379 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200380
Radek Krejcic04f0a22018-09-21 15:49:45 +0200381 switch (elem_type) {
382 case LY_VLOG_STR:
383 (*path) = strdup(elem);
384 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
385 break;
386 case LY_VLOG_LINE:
387 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
388 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
389 break;
390 default:
391 /* shouldn't be here */
392 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200393 }
394
Radek Krejci94aa9942018-09-07 17:12:17 +0200395 return LY_SUCCESS;
396}
397
398void
399ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
400{
401 va_list ap;
402 char* path = NULL;
403 const struct ly_err_item *first;
404
405 if (path_flag && (elem_type != LY_VLOG_NONE)) {
406 if (elem_type == LY_VLOG_PREV) {
407 /* use previous path */
408 first = ly_err_first(ctx);
409 if (first && first->prev->path) {
410 path = strdup(first->prev->path);
411 }
412 } else {
413 /* print path */
414 if (!elem) {
415 /* top-level */
416 path = strdup("/");
417 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200418 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200419 }
420 }
421 }
422
423 va_start(ap, format);
424 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
425 /* path is spent and should not be freed! */
426 va_end(ap);
427}
428
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200429API void
430ly_err_print(struct ly_err_item *eitem)
431{
432 if (ly_log_opts & LY_LOLOG) {
433 if (ly_log_clb) {
434 ly_log_clb(eitem->level, eitem->msg, eitem->path);
435 } else {
436 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
437 if (eitem->path) {
438 fprintf(stderr, "(path: %s)\n", eitem->path);
439 }
440 }
441 }
442}
443