blob: 357fb5dd81d5de621d941700258002d889be3dcb [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
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
16
17#include "log.h"
Radek Krejcib7db73a2018-10-24 14:18:40 +020018
Radek Krejci5aeea3a2018-09-05 13:29:36 +020019#include <assert.h>
Radek Krejcic04f0a22018-09-21 15:49:45 +020020#include <inttypes.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020021#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020022#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020023#include <stdint.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020024#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020025#include <stdlib.h>
26#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020027
Radek Krejci535ea9f2020-05-29 16:01:05 +020028#include "common.h"
Radek Krejci0935f412019-08-20 16:15:18 +020029#include "plugins_exts.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020030#include "tree_data.h"
31#include "tree_schema.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020032
Radek Krejci5aeea3a2018-09-05 13:29:36 +020033volatile uint8_t ly_log_level = LY_LLWRN;
34volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
35static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
36static volatile int path_flag = 1;
37#ifndef NDEBUG
38volatile int ly_log_dbg_groups = 0;
39#endif
40
Radek Krejci94aa9942018-09-07 17:12:17 +020041/* how many bytes add when enlarging buffers */
42#define LY_BUF_STEP 128
43
Radek Krejcid33273d2018-10-25 14:55:52 +020044API LY_ERR
45ly_errcode(const struct ly_ctx *ctx)
46{
47 struct ly_err_item *i;
48
49 i = ly_err_first(ctx);
50 if (i) {
51 return i->prev->no;
52 }
53
54 return LY_SUCCESS;
55}
56
Radek Krejci5aeea3a2018-09-05 13:29:36 +020057API LY_VECODE
58ly_vecode(const struct ly_ctx *ctx)
59{
60 struct ly_err_item *i;
61
62 i = ly_err_first(ctx);
63 if (i) {
64 return i->prev->vecode;
65 }
66
67 return LYVE_SUCCESS;
68}
69
70API const char *
71ly_errmsg(const struct ly_ctx *ctx)
72{
73 struct ly_err_item *i;
74
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020075 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020076
77 i = ly_err_first(ctx);
78 if (i) {
79 return i->prev->msg;
80 }
81
82 return NULL;
83}
84
85API const char *
86ly_errpath(const struct ly_ctx *ctx)
87{
88 struct ly_err_item *i;
89
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020090 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020091
92 i = ly_err_first(ctx);
93 if (i) {
94 return i->prev->path;
95 }
96
97 return NULL;
98}
99
100API const char *
101ly_errapptag(const struct ly_ctx *ctx)
102{
103 struct ly_err_item *i;
104
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200105 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200106
107 i = ly_err_first(ctx);
108 if (i) {
109 return i->prev->apptag;
110 }
111
112 return NULL;
113}
114
115API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200116ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
117{
118 struct ly_err_item *eitem;
119
120 eitem = malloc(sizeof *eitem);
121 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
122 eitem->prev = eitem;
123 eitem->next = NULL;
124
125 /* fill in the information */
126 eitem->level = level;
127 eitem->no = no;
128 eitem->vecode = vecode;
129 eitem->msg = msg;
130 eitem->path = path;
131 eitem->apptag = apptag;
132
133 return eitem;
134}
135
136API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200137ly_err_first(const struct ly_ctx *ctx)
138{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200139 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200140
141 return pthread_getspecific(ctx->errlist_key);
142}
143
Radek Krejcie7b95092019-05-15 11:03:07 +0200144API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200145ly_err_free(void *ptr)
146{
147 struct ly_err_item *i, *next;
148
149 /* clean the error list */
150 for (i = (struct ly_err_item *)ptr; i; i = next) {
151 next = i->next;
152 free(i->msg);
153 free(i->path);
154 free(i->apptag);
155 free(i);
156 }
157}
158
159API void
160ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
161{
162 struct ly_err_item *i, *first;
163
164 first = ly_err_first(ctx);
165 if (first == eitem) {
166 eitem = NULL;
167 }
168 if (eitem) {
169 /* disconnect the error */
170 for (i = first; i && (i->next != eitem); i = i->next);
171 assert(i);
172 i->next = NULL;
173 first->prev = i;
174 /* free this err and newer */
175 ly_err_free(eitem);
176 } else {
177 /* free all err */
178 ly_err_free(first);
179 pthread_setspecific(ctx->errlist_key, NULL);
180 }
181}
182
183API LY_LOG_LEVEL
184ly_verb(LY_LOG_LEVEL level)
185{
186 LY_LOG_LEVEL prev = ly_log_level;
187
188 ly_log_level = level;
189 return prev;
190}
191
192API int
193ly_log_options(int opts)
194{
195 uint8_t prev = ly_log_opts;
196
197 ly_log_opts = opts;
198 return prev;
199}
200
201API void
202ly_verb_dbg(int dbg_groups)
203{
204#ifndef NDEBUG
205 ly_log_dbg_groups = dbg_groups;
206#else
207 (void)dbg_groups;
208#endif
209}
210
211API void
212ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
213{
214 ly_log_clb = clb;
215 path_flag = path;
216}
217
218API void
219(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
220{
221 return ly_log_clb;
222}
223
224static LY_ERR
225log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
226{
227 struct ly_err_item *eitem, *last;
228
229 assert(ctx && (level < LY_LLVRB));
230
231 eitem = pthread_getspecific(ctx->errlist_key);
232 if (!eitem) {
233 /* if we are only to fill in path, there must have been an error stored */
234 assert(msg);
235 eitem = malloc(sizeof *eitem);
236 LY_CHECK_GOTO(!eitem, mem_fail);
237 eitem->prev = eitem;
238 eitem->next = NULL;
239
240 pthread_setspecific(ctx->errlist_key, eitem);
241 } else if (!msg) {
242 /* only filling the path */
243 assert(path);
244
245 /* find last error */
246 eitem = eitem->prev;
247 do {
248 if (eitem->level == LY_LLERR) {
249 /* fill the path */
250 free(eitem->path);
251 eitem->path = path;
252 return LY_SUCCESS;
253 }
254 eitem = eitem->prev;
255 } while (eitem->prev->next);
256 /* last error was not found */
257 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100258 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200259 /* overwrite last message */
260 free(eitem->msg);
261 free(eitem->path);
262 free(eitem->apptag);
263 } else {
264 /* store new message */
265 last = eitem->prev;
266 eitem->prev = malloc(sizeof *eitem);
267 LY_CHECK_GOTO(!eitem->prev, mem_fail);
268 eitem = eitem->prev;
269 eitem->prev = last;
270 eitem->next = NULL;
271 last->next = eitem;
272 }
273
274 /* fill in the information */
275 eitem->level = level;
276 eitem->no = no;
277 eitem->vecode = vecode;
278 eitem->msg = msg;
279 eitem->path = path;
280 eitem->apptag = apptag;
281 return LY_SUCCESS;
282
283mem_fail:
284 LOGMEM(NULL);
285 free(msg);
286 free(path);
287 free(apptag);
288 return LY_EMEM;
289}
290
291static void
292log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
293 const char *format, va_list args)
294{
295 char *msg = NULL;
296 int free_strs;
297
Michal Vaskoed94a292019-11-06 15:43:41 +0100298 if (level > ly_log_level) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200299 /* do not print or store the message */
300 free(path);
301 return;
302 }
303
Radek Krejcia4614e62020-05-15 14:19:28 +0200304 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200305 /* assume we are inheriting the error, so inherit vecode as well */
306 vecode = ly_vecode(ctx);
307 }
308
309 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100310 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200311 assert(format);
312 if (vasprintf(&msg, format, args) == -1) {
313 LOGMEM(ctx);
314 free(path);
315 return;
316 }
317 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
318 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200319 }
320 free_strs = 0;
321 } else {
322 if (vasprintf(&msg, format, args) == -1) {
323 LOGMEM(ctx);
324 free(path);
325 return;
326 }
327 free_strs = 1;
328 }
329
330 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100331 if (ly_log_opts & LY_LOLOG) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200332 if (ly_log_clb) {
333 ly_log_clb(level, msg, path);
334 } else {
335 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
336 if (path) {
337 fprintf(stderr, "(path: %s)\n", path);
338 }
339 }
340 }
341
342 if (free_strs) {
343 free(path);
344 free(msg);
345 }
346}
347
Radek Krejci4ab61562018-09-05 15:00:37 +0200348#ifndef NDEBUG
349
350void
351ly_log_dbg(int group, const char *format, ...)
352{
353 char *dbg_format;
354 const char *str_group;
355 va_list ap;
356
357 if (!(ly_log_dbg_groups & group)) {
358 return;
359 }
360
361 switch (group) {
362 case LY_LDGDICT:
363 str_group = "DICT";
364 break;
365 case LY_LDGYANG:
366 str_group = "YANG";
367 break;
368 case LY_LDGYIN:
369 str_group = "YIN";
370 break;
371 case LY_LDGXPATH:
372 str_group = "XPATH";
373 break;
374 case LY_LDGDIFF:
375 str_group = "DIFF";
376 break;
377 default:
378 LOGINT(NULL);
379 return;
380 }
381
382 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
383 LOGMEM(NULL);
384 return;
385 }
386
387 va_start(ap, format);
388 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
389 va_end(ap);
390}
391
392#endif
393
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200394void
395ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
396{
397 va_list ap;
398
399 va_start(ap, format);
400 log_vprintf(ctx, level, no, 0, NULL, format, ap);
401 va_end(ap);
402}
403
Radek Krejci94aa9942018-09-07 17:12:17 +0200404static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200405ly_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 +0200406{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200407 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200408
Radek Krejcic04f0a22018-09-21 15:49:45 +0200409 switch (elem_type) {
410 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100411 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200412 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
413 break;
414 case LY_VLOG_LINE:
415 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
416 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
417 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100418 case LY_VLOG_LYSC:
419 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
420 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
421 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100422 case LY_VLOG_LYD:
423 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
424 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
425 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200426 default:
427 /* shouldn't be here */
428 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200429 }
430
Radek Krejci94aa9942018-09-07 17:12:17 +0200431 return LY_SUCCESS;
432}
433
434void
435ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
436{
437 va_list ap;
438 char* path = NULL;
439 const struct ly_err_item *first;
440
441 if (path_flag && (elem_type != LY_VLOG_NONE)) {
442 if (elem_type == LY_VLOG_PREV) {
443 /* use previous path */
444 first = ly_err_first(ctx);
445 if (first && first->prev->path) {
446 path = strdup(first->prev->path);
447 }
448 } else {
449 /* print path */
450 if (!elem) {
451 /* top-level */
452 path = strdup("/");
453 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200454 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200455 }
456 }
457 }
458
459 va_start(ap, format);
460 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
461 /* path is spent and should not be freed! */
462 va_end(ap);
463}
464
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200465API void
Radek Krejci0935f412019-08-20 16:15:18 +0200466lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
467{
468 va_list ap;
469 char *plugin_msg;
470 int ret;
471
472 if (ly_log_level < level) {
473 return;
474 }
475 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
476 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200477 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200478 return;
479 }
480
481 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200482 log_vprintf(ext->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, plugin_msg, ap);
Radek Krejci0935f412019-08-20 16:15:18 +0200483 va_end(ap);
484
485 free(plugin_msg);
486}
487
488API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200489ly_err_print(struct ly_err_item *eitem)
490{
491 if (ly_log_opts & LY_LOLOG) {
492 if (ly_log_clb) {
493 ly_log_clb(eitem->level, eitem->msg, eitem->path);
494 } else {
495 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
496 if (eitem->path) {
497 fprintf(stderr, "(path: %s)\n", eitem->path);
498 }
499 }
500 }
501}
502
Radek Krejcie7b95092019-05-15 11:03:07 +0200503void
504ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
505{
506 struct ly_err_item *i;
507
Michal Vaskoed94a292019-11-06 15:43:41 +0100508 i = ly_err_first(ctx);
509 if (i) {
510 i = i->prev;
511 i->apptag = strdup(apptag);
Radek Krejcie7b95092019-05-15 11:03:07 +0200512 }
513}