blob: 1b4e2290dfff7f8d4d52d12ea3a37b040ee3f282 [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 Krejciaa45bda2020-07-20 07:43:38 +020029#include "compat.h"
Radek Krejci0935f412019-08-20 16:15:18 +020030#include "plugins_exts.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020031#include "tree_data.h"
32#include "tree_schema.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020033
Radek Krejci5aeea3a2018-09-05 13:29:36 +020034volatile uint8_t ly_log_level = LY_LLWRN;
35volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
Michal Vaskod8085612020-08-21 12:55:23 +020036static ly_log_clb log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020037static volatile int path_flag = 1;
38#ifndef NDEBUG
39volatile int ly_log_dbg_groups = 0;
40#endif
41
Radek Krejci94aa9942018-09-07 17:12:17 +020042/* how many bytes add when enlarging buffers */
43#define LY_BUF_STEP 128
44
Radek Krejcid33273d2018-10-25 14:55:52 +020045API LY_ERR
46ly_errcode(const struct ly_ctx *ctx)
47{
48 struct ly_err_item *i;
49
50 i = ly_err_first(ctx);
51 if (i) {
52 return i->prev->no;
53 }
54
55 return LY_SUCCESS;
56}
57
Radek Krejci5aeea3a2018-09-05 13:29:36 +020058API LY_VECODE
59ly_vecode(const struct ly_ctx *ctx)
60{
61 struct ly_err_item *i;
62
63 i = ly_err_first(ctx);
64 if (i) {
65 return i->prev->vecode;
66 }
67
68 return LYVE_SUCCESS;
69}
70
71API const char *
72ly_errmsg(const struct ly_ctx *ctx)
73{
74 struct ly_err_item *i;
75
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020076 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020077
78 i = ly_err_first(ctx);
79 if (i) {
80 return i->prev->msg;
81 }
82
83 return NULL;
84}
85
86API const char *
87ly_errpath(const struct ly_ctx *ctx)
88{
89 struct ly_err_item *i;
90
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020091 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020092
93 i = ly_err_first(ctx);
94 if (i) {
95 return i->prev->path;
96 }
97
98 return NULL;
99}
100
101API const char *
102ly_errapptag(const struct ly_ctx *ctx)
103{
104 struct ly_err_item *i;
105
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200106 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200107
108 i = ly_err_first(ctx);
109 if (i) {
110 return i->prev->apptag;
111 }
112
113 return NULL;
114}
115
116API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200117ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
118{
119 struct ly_err_item *eitem;
120
121 eitem = malloc(sizeof *eitem);
122 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
123 eitem->prev = eitem;
124 eitem->next = NULL;
125
126 /* fill in the information */
127 eitem->level = level;
128 eitem->no = no;
129 eitem->vecode = vecode;
130 eitem->msg = msg;
131 eitem->path = path;
132 eitem->apptag = apptag;
133
134 return eitem;
135}
136
137API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200138ly_err_first(const struct ly_ctx *ctx)
139{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200140 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200141
142 return pthread_getspecific(ctx->errlist_key);
143}
144
Radek Krejcie7b95092019-05-15 11:03:07 +0200145API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200146ly_err_free(void *ptr)
147{
148 struct ly_err_item *i, *next;
149
150 /* clean the error list */
151 for (i = (struct ly_err_item *)ptr; i; i = next) {
152 next = i->next;
153 free(i->msg);
154 free(i->path);
155 free(i->apptag);
156 free(i);
157 }
158}
159
160API void
161ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
162{
163 struct ly_err_item *i, *first;
164
165 first = ly_err_first(ctx);
166 if (first == eitem) {
167 eitem = NULL;
168 }
169 if (eitem) {
170 /* disconnect the error */
Radek Krejci1e008d22020-08-17 11:37:37 +0200171 for (i = first; i && (i->next != eitem); i = i->next) {}
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200172 assert(i);
173 i->next = NULL;
174 first->prev = i;
175 /* free this err and newer */
176 ly_err_free(eitem);
177 } else {
178 /* free all err */
179 ly_err_free(first);
180 pthread_setspecific(ctx->errlist_key, NULL);
181 }
182}
183
184API LY_LOG_LEVEL
185ly_verb(LY_LOG_LEVEL level)
186{
187 LY_LOG_LEVEL prev = ly_log_level;
188
189 ly_log_level = level;
190 return prev;
191}
192
193API int
194ly_log_options(int opts)
195{
196 uint8_t prev = ly_log_opts;
197
198 ly_log_opts = opts;
199 return prev;
200}
201
202API void
203ly_verb_dbg(int dbg_groups)
204{
205#ifndef NDEBUG
206 ly_log_dbg_groups = dbg_groups;
207#else
208 (void)dbg_groups;
209#endif
210}
211
212API void
Michal Vaskod8085612020-08-21 12:55:23 +0200213ly_set_log_clb(ly_log_clb clb, int path)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200214{
Michal Vaskod8085612020-08-21 12:55:23 +0200215 log_clb = clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200216 path_flag = path;
217}
218
Michal Vaskod8085612020-08-21 12:55:23 +0200219API ly_log_clb
220ly_get_log_clb(void)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200221{
Michal Vaskod8085612020-08-21 12:55:23 +0200222 return log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200223}
224
225static LY_ERR
226log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
227{
228 struct ly_err_item *eitem, *last;
229
230 assert(ctx && (level < LY_LLVRB));
231
232 eitem = pthread_getspecific(ctx->errlist_key);
233 if (!eitem) {
234 /* if we are only to fill in path, there must have been an error stored */
235 assert(msg);
236 eitem = malloc(sizeof *eitem);
237 LY_CHECK_GOTO(!eitem, mem_fail);
238 eitem->prev = eitem;
239 eitem->next = NULL;
240
241 pthread_setspecific(ctx->errlist_key, eitem);
242 } else if (!msg) {
243 /* only filling the path */
244 assert(path);
245
246 /* find last error */
247 eitem = eitem->prev;
248 do {
249 if (eitem->level == LY_LLERR) {
250 /* fill the path */
251 free(eitem->path);
252 eitem->path = path;
253 return LY_SUCCESS;
254 }
255 eitem = eitem->prev;
256 } while (eitem->prev->next);
257 /* last error was not found */
258 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100259 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200260 /* overwrite last message */
261 free(eitem->msg);
262 free(eitem->path);
263 free(eitem->apptag);
264 } else {
265 /* store new message */
266 last = eitem->prev;
267 eitem->prev = malloc(sizeof *eitem);
268 LY_CHECK_GOTO(!eitem->prev, mem_fail);
269 eitem = eitem->prev;
270 eitem->prev = last;
271 eitem->next = NULL;
272 last->next = eitem;
273 }
274
275 /* fill in the information */
276 eitem->level = level;
277 eitem->no = no;
278 eitem->vecode = vecode;
279 eitem->msg = msg;
280 eitem->path = path;
281 eitem->apptag = apptag;
282 return LY_SUCCESS;
283
284mem_fail:
285 LOGMEM(NULL);
286 free(msg);
287 free(path);
288 free(apptag);
289 return LY_EMEM;
290}
291
292static void
293log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
Radek Krejci0f969882020-08-21 16:56:47 +0200294 const char *format, va_list args)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200295{
296 char *msg = NULL;
297 int free_strs;
298
Michal Vaskoed94a292019-11-06 15:43:41 +0100299 if (level > ly_log_level) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200300 /* do not print or store the message */
301 free(path);
302 return;
303 }
304
Radek Krejcia4614e62020-05-15 14:19:28 +0200305 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200306 /* assume we are inheriting the error, so inherit vecode as well */
307 vecode = ly_vecode(ctx);
308 }
309
310 /* 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 +0100311 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200312 assert(format);
313 if (vasprintf(&msg, format, args) == -1) {
314 LOGMEM(ctx);
315 free(path);
316 return;
317 }
318 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
319 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200320 }
321 free_strs = 0;
322 } else {
323 if (vasprintf(&msg, format, args) == -1) {
324 LOGMEM(ctx);
325 free(path);
326 return;
327 }
328 free_strs = 1;
329 }
330
331 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100332 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200333 if (log_clb) {
334 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200335 } else {
336 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
337 if (path) {
338 fprintf(stderr, "(path: %s)\n", path);
339 }
340 }
341 }
342
343 if (free_strs) {
344 free(path);
345 free(msg);
346 }
347}
348
Radek Krejci4ab61562018-09-05 15:00:37 +0200349#ifndef NDEBUG
350
351void
352ly_log_dbg(int group, const char *format, ...)
353{
354 char *dbg_format;
355 const char *str_group;
356 va_list ap;
357
358 if (!(ly_log_dbg_groups & group)) {
359 return;
360 }
361
362 switch (group) {
363 case LY_LDGDICT:
364 str_group = "DICT";
365 break;
366 case LY_LDGYANG:
367 str_group = "YANG";
368 break;
369 case LY_LDGYIN:
370 str_group = "YIN";
371 break;
372 case LY_LDGXPATH:
373 str_group = "XPATH";
374 break;
375 case LY_LDGDIFF:
376 str_group = "DIFF";
377 break;
378 default:
379 LOGINT(NULL);
380 return;
381 }
382
383 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
384 LOGMEM(NULL);
385 return;
386 }
387
388 va_start(ap, format);
389 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
390 va_end(ap);
391}
392
393#endif
394
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200395void
396ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
397{
398 va_list ap;
399
400 va_start(ap, format);
401 log_vprintf(ctx, level, no, 0, NULL, format, ap);
402 va_end(ap);
403}
404
Radek Krejci94aa9942018-09-07 17:12:17 +0200405static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200406ly_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 +0200407{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200408 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200409
Radek Krejcic04f0a22018-09-21 15:49:45 +0200410 switch (elem_type) {
411 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100412 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200413 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
414 break;
415 case LY_VLOG_LINE:
Michal Vasko22df3f02020-08-24 13:29:22 +0200416 rc = asprintf(path, "Line number %" PRIu64 ".", *((uint64_t *)elem));
Radek Krejcic04f0a22018-09-21 15:49:45 +0200417 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
418 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100419 case LY_VLOG_LYSC:
420 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
421 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
422 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100423 case LY_VLOG_LYD:
424 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
425 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
426 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200427 default:
428 /* shouldn't be here */
429 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200430 }
431
Radek Krejci94aa9942018-09-07 17:12:17 +0200432 return LY_SUCCESS;
433}
434
435void
436ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
437{
438 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200439 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200440 const struct ly_err_item *first;
441
442 if (path_flag && (elem_type != LY_VLOG_NONE)) {
443 if (elem_type == LY_VLOG_PREV) {
444 /* use previous path */
445 first = ly_err_first(ctx);
446 if (first && first->prev->path) {
447 path = strdup(first->prev->path);
448 }
449 } else {
450 /* print path */
451 if (!elem) {
452 /* top-level */
453 path = strdup("/");
454 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200455 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200456 }
457 }
458 }
459
460 va_start(ap, format);
461 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
462 /* path is spent and should not be freed! */
463 va_end(ap);
464}
465
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200466API void
Radek Krejci0935f412019-08-20 16:15:18 +0200467lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
468{
469 va_list ap;
470 char *plugin_msg;
471 int ret;
472
473 if (ly_log_level < level) {
474 return;
475 }
476 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
477 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200478 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200479 return;
480 }
481
482 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200483 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 +0200484 va_end(ap);
485
486 free(plugin_msg);
487}
488
489API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200490ly_err_print(struct ly_err_item *eitem)
491{
492 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200493 if (log_clb) {
494 log_clb(eitem->level, eitem->msg, eitem->path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200495 } else {
496 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
497 if (eitem->path) {
498 fprintf(stderr, "(path: %s)\n", eitem->path);
499 }
500 }
501 }
502}
503
Radek Krejcie7b95092019-05-15 11:03:07 +0200504void
505ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
506{
507 struct ly_err_item *i;
508
Michal Vaskoed94a292019-11-06 15:43:41 +0100509 i = ly_err_first(ctx);
510 if (i) {
511 i = i->prev;
512 i->apptag = strdup(apptag);
Radek Krejcie7b95092019-05-15 11:03:07 +0200513 }
514}