blob: a8d31be288ba1f46fdd29cf521c5e291bccf0b92 [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)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200311 if (!format) {
312 assert(path);
313 /* postponed print of path related to the previous error, do not rewrite stored original message */
314 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
315 return;
316 }
317 msg = "Path is related to the previous error message.";
318 } else {
319 if (vasprintf(&msg, format, args) == -1) {
320 LOGMEM(ctx);
321 free(path);
322 return;
323 }
324 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
325 return;
326 }
327 }
328 free_strs = 0;
329 } else {
330 if (vasprintf(&msg, format, args) == -1) {
331 LOGMEM(ctx);
332 free(path);
333 return;
334 }
335 free_strs = 1;
336 }
337
338 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100339 if (ly_log_opts & LY_LOLOG) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200340 if (ly_log_clb) {
341 ly_log_clb(level, msg, path);
342 } else {
343 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
344 if (path) {
345 fprintf(stderr, "(path: %s)\n", path);
346 }
347 }
348 }
349
350 if (free_strs) {
351 free(path);
352 free(msg);
353 }
354}
355
Radek Krejci4ab61562018-09-05 15:00:37 +0200356#ifndef NDEBUG
357
358void
359ly_log_dbg(int group, const char *format, ...)
360{
361 char *dbg_format;
362 const char *str_group;
363 va_list ap;
364
365 if (!(ly_log_dbg_groups & group)) {
366 return;
367 }
368
369 switch (group) {
370 case LY_LDGDICT:
371 str_group = "DICT";
372 break;
373 case LY_LDGYANG:
374 str_group = "YANG";
375 break;
376 case LY_LDGYIN:
377 str_group = "YIN";
378 break;
379 case LY_LDGXPATH:
380 str_group = "XPATH";
381 break;
382 case LY_LDGDIFF:
383 str_group = "DIFF";
384 break;
385 default:
386 LOGINT(NULL);
387 return;
388 }
389
390 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
391 LOGMEM(NULL);
392 return;
393 }
394
395 va_start(ap, format);
396 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
397 va_end(ap);
398}
399
400#endif
401
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200402void
403ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
404{
405 va_list ap;
406
407 va_start(ap, format);
408 log_vprintf(ctx, level, no, 0, NULL, format, ap);
409 va_end(ap);
410}
411
Radek Krejci94aa9942018-09-07 17:12:17 +0200412static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200413ly_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 +0200414{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200415 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200416
Radek Krejcic04f0a22018-09-21 15:49:45 +0200417 switch (elem_type) {
418 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100419 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200420 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
421 break;
422 case LY_VLOG_LINE:
423 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
424 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
425 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100426 case LY_VLOG_LYSC:
427 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
428 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
429 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100430 case LY_VLOG_LYD:
431 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
432 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
433 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200434 default:
435 /* shouldn't be here */
436 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200437 }
438
Radek Krejci94aa9942018-09-07 17:12:17 +0200439 return LY_SUCCESS;
440}
441
442void
443ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
444{
445 va_list ap;
446 char* path = NULL;
447 const struct ly_err_item *first;
448
449 if (path_flag && (elem_type != LY_VLOG_NONE)) {
450 if (elem_type == LY_VLOG_PREV) {
451 /* use previous path */
452 first = ly_err_first(ctx);
453 if (first && first->prev->path) {
454 path = strdup(first->prev->path);
455 }
456 } else {
457 /* print path */
458 if (!elem) {
459 /* top-level */
460 path = strdup("/");
461 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200462 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200463 }
464 }
465 }
466
467 va_start(ap, format);
468 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
469 /* path is spent and should not be freed! */
470 va_end(ap);
471}
472
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200473API void
Radek Krejci0935f412019-08-20 16:15:18 +0200474lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
475{
476 va_list ap;
477 char *plugin_msg;
478 int ret;
479
480 if (ly_log_level < level) {
481 return;
482 }
483 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
484 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200485 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200486 return;
487 }
488
489 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200490 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 +0200491 va_end(ap);
492
493 free(plugin_msg);
494}
495
496API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200497ly_err_print(struct ly_err_item *eitem)
498{
499 if (ly_log_opts & LY_LOLOG) {
500 if (ly_log_clb) {
501 ly_log_clb(eitem->level, eitem->msg, eitem->path);
502 } else {
503 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
504 if (eitem->path) {
505 fprintf(stderr, "(path: %s)\n", eitem->path);
506 }
507 }
508 }
509}
510
Radek Krejcie7b95092019-05-15 11:03:07 +0200511void
512ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
513{
514 struct ly_err_item *i;
515
Michal Vaskoed94a292019-11-06 15:43:41 +0100516 i = ly_err_first(ctx);
517 if (i) {
518 i = i->prev;
519 i->apptag = strdup(apptag);
Radek Krejcie7b95092019-05-15 11:03:07 +0200520 }
521}