blob: 894bffbd8a7dbf58e305a8d919968de1cba1bd0f [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 Krejci1deb5be2020-08-26 16:43:36 +020034volatile LY_LOG_LEVEL ly_log_level = LY_LLWRN;
35volatile uint32_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
Michal Vaskod8085612020-08-21 12:55:23 +020036static ly_log_clb log_clb;
Radek Krejci857189e2020-09-01 13:26:36 +020037static volatile ly_bool path_flag = 1;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020038#ifndef NDEBUG
Radek Krejci1deb5be2020-08-26 16:43:36 +020039volatile uint32_t ly_log_dbg_groups = 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020040#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
Radek Krejci572ee602020-09-16 14:35:08 +020050 i = ly_err_last(ctx);
Radek Krejcid33273d2018-10-25 14:55:52 +020051 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020052 return i->no;
Radek Krejcid33273d2018-10-25 14:55:52 +020053 }
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
Radek Krejci572ee602020-09-16 14:35:08 +020063 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020064 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020065 return i->vecode;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020066 }
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
Radek Krejci572ee602020-09-16 14:35:08 +020078 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020079 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020080 return i->msg;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020081 }
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
Radek Krejci572ee602020-09-16 14:35:08 +020093 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020094 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020095 return i->path;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020096 }
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
Radek Krejci572ee602020-09-16 14:35:08 +0200108 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200109 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +0200110 return i->apptag;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200111 }
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 Krejci572ee602020-09-16 14:35:08 +0200145API struct ly_err_item *
146ly_err_last(const struct ly_ctx *ctx)
147{
148 const struct ly_err_item *e;
149
150 LY_CHECK_ARG_RET(NULL, ctx, NULL);
151
152 e = pthread_getspecific(ctx->errlist_key);
153 return e ? e->prev : NULL;
154}
155
Radek Krejcie7b95092019-05-15 11:03:07 +0200156API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200157ly_err_free(void *ptr)
158{
159 struct ly_err_item *i, *next;
160
161 /* clean the error list */
162 for (i = (struct ly_err_item *)ptr; i; i = next) {
163 next = i->next;
164 free(i->msg);
165 free(i->path);
166 free(i->apptag);
167 free(i);
168 }
169}
170
171API void
172ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
173{
174 struct ly_err_item *i, *first;
175
176 first = ly_err_first(ctx);
177 if (first == eitem) {
178 eitem = NULL;
179 }
180 if (eitem) {
181 /* disconnect the error */
Radek Krejci1e008d22020-08-17 11:37:37 +0200182 for (i = first; i && (i->next != eitem); i = i->next) {}
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200183 assert(i);
184 i->next = NULL;
185 first->prev = i;
186 /* free this err and newer */
187 ly_err_free(eitem);
188 } else {
189 /* free all err */
190 ly_err_free(first);
191 pthread_setspecific(ctx->errlist_key, NULL);
192 }
193}
194
195API LY_LOG_LEVEL
196ly_verb(LY_LOG_LEVEL level)
197{
198 LY_LOG_LEVEL prev = ly_log_level;
199
200 ly_log_level = level;
201 return prev;
202}
203
Radek Krejci1deb5be2020-08-26 16:43:36 +0200204API uint32_t
205ly_log_options(uint32_t opts)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200206{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200207 uint32_t prev = ly_log_opts;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200208
209 ly_log_opts = opts;
210 return prev;
211}
212
213API void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200214ly_verb_dbg(uint32_t dbg_groups)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200215{
216#ifndef NDEBUG
217 ly_log_dbg_groups = dbg_groups;
218#else
219 (void)dbg_groups;
220#endif
221}
222
223API void
Radek Krejci857189e2020-09-01 13:26:36 +0200224ly_set_log_clb(ly_log_clb clb, ly_bool path)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200225{
Michal Vaskod8085612020-08-21 12:55:23 +0200226 log_clb = clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200227 path_flag = path;
228}
229
Michal Vaskod8085612020-08-21 12:55:23 +0200230API ly_log_clb
231ly_get_log_clb(void)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200232{
Michal Vaskod8085612020-08-21 12:55:23 +0200233 return log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200234}
235
236static LY_ERR
237log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
238{
239 struct ly_err_item *eitem, *last;
240
241 assert(ctx && (level < LY_LLVRB));
242
243 eitem = pthread_getspecific(ctx->errlist_key);
244 if (!eitem) {
245 /* if we are only to fill in path, there must have been an error stored */
246 assert(msg);
247 eitem = malloc(sizeof *eitem);
248 LY_CHECK_GOTO(!eitem, mem_fail);
249 eitem->prev = eitem;
250 eitem->next = NULL;
251
252 pthread_setspecific(ctx->errlist_key, eitem);
253 } else if (!msg) {
254 /* only filling the path */
255 assert(path);
256
257 /* find last error */
258 eitem = eitem->prev;
259 do {
260 if (eitem->level == LY_LLERR) {
261 /* fill the path */
262 free(eitem->path);
263 eitem->path = path;
264 return LY_SUCCESS;
265 }
266 eitem = eitem->prev;
267 } while (eitem->prev->next);
268 /* last error was not found */
269 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100270 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200271 /* overwrite last message */
272 free(eitem->msg);
273 free(eitem->path);
274 free(eitem->apptag);
275 } else {
276 /* store new message */
277 last = eitem->prev;
278 eitem->prev = malloc(sizeof *eitem);
279 LY_CHECK_GOTO(!eitem->prev, mem_fail);
280 eitem = eitem->prev;
281 eitem->prev = last;
282 eitem->next = NULL;
283 last->next = eitem;
284 }
285
286 /* fill in the information */
287 eitem->level = level;
288 eitem->no = no;
289 eitem->vecode = vecode;
290 eitem->msg = msg;
291 eitem->path = path;
292 eitem->apptag = apptag;
293 return LY_SUCCESS;
294
295mem_fail:
296 LOGMEM(NULL);
297 free(msg);
298 free(path);
299 free(apptag);
300 return LY_EMEM;
301}
302
303static void
304log_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 +0200305 const char *format, va_list args)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200306{
307 char *msg = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200308 ly_bool free_strs;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200309
Michal Vaskoed94a292019-11-06 15:43:41 +0100310 if (level > ly_log_level) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200311 /* do not print or store the message */
312 free(path);
313 return;
314 }
315
Radek Krejcia4614e62020-05-15 14:19:28 +0200316 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200317 /* assume we are inheriting the error, so inherit vecode as well */
318 vecode = ly_vecode(ctx);
319 }
320
321 /* 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 +0100322 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200323 assert(format);
324 if (vasprintf(&msg, format, args) == -1) {
325 LOGMEM(ctx);
326 free(path);
327 return;
328 }
329 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
330 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200331 }
332 free_strs = 0;
333 } else {
334 if (vasprintf(&msg, format, args) == -1) {
335 LOGMEM(ctx);
336 free(path);
337 return;
338 }
339 free_strs = 1;
340 }
341
342 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100343 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200344 if (log_clb) {
345 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200346 } else {
347 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
348 if (path) {
349 fprintf(stderr, "(path: %s)\n", path);
350 }
351 }
352 }
353
354 if (free_strs) {
355 free(path);
356 free(msg);
357 }
358}
359
Radek Krejci4ab61562018-09-05 15:00:37 +0200360#ifndef NDEBUG
361
362void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200363ly_log_dbg(uint32_t group, const char *format, ...)
Radek Krejci4ab61562018-09-05 15:00:37 +0200364{
365 char *dbg_format;
366 const char *str_group;
367 va_list ap;
368
369 if (!(ly_log_dbg_groups & group)) {
370 return;
371 }
372
373 switch (group) {
374 case LY_LDGDICT:
375 str_group = "DICT";
376 break;
377 case LY_LDGYANG:
378 str_group = "YANG";
379 break;
380 case LY_LDGYIN:
381 str_group = "YIN";
382 break;
383 case LY_LDGXPATH:
384 str_group = "XPATH";
385 break;
386 case LY_LDGDIFF:
387 str_group = "DIFF";
388 break;
389 default:
390 LOGINT(NULL);
391 return;
392 }
393
394 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
395 LOGMEM(NULL);
396 return;
397 }
398
399 va_start(ap, format);
400 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
401 va_end(ap);
402}
403
404#endif
405
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200406void
407ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
408{
409 va_list ap;
410
411 va_start(ap, format);
412 log_vprintf(ctx, level, no, 0, NULL, format, ap);
413 va_end(ap);
414}
415
Radek Krejci94aa9942018-09-07 17:12:17 +0200416static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200417ly_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 +0200418{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200419 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200420
Radek Krejcic04f0a22018-09-21 15:49:45 +0200421 switch (elem_type) {
422 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100423 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200424 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
425 break;
426 case LY_VLOG_LINE:
Michal Vasko22df3f02020-08-24 13:29:22 +0200427 rc = asprintf(path, "Line number %" PRIu64 ".", *((uint64_t *)elem));
Radek Krejcic04f0a22018-09-21 15:49:45 +0200428 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
429 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100430 case LY_VLOG_LYSC:
431 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
432 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
433 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100434 case LY_VLOG_LYD:
435 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
436 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
437 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200438 default:
439 /* shouldn't be here */
440 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200441 }
442
Radek Krejci94aa9942018-09-07 17:12:17 +0200443 return LY_SUCCESS;
444}
445
446void
447ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
448{
449 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200450 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200451 const struct ly_err_item *first;
452
453 if (path_flag && (elem_type != LY_VLOG_NONE)) {
454 if (elem_type == LY_VLOG_PREV) {
455 /* use previous path */
456 first = ly_err_first(ctx);
457 if (first && first->prev->path) {
458 path = strdup(first->prev->path);
459 }
460 } else {
461 /* print path */
462 if (!elem) {
463 /* top-level */
464 path = strdup("/");
465 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200466 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200467 }
468 }
469 }
470
471 va_start(ap, format);
472 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
473 /* path is spent and should not be freed! */
474 va_end(ap);
475}
476
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200477API void
Radek Krejci0935f412019-08-20 16:15:18 +0200478lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
479{
480 va_list ap;
481 char *plugin_msg;
482 int ret;
483
484 if (ly_log_level < level) {
485 return;
486 }
487 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
488 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200489 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200490 return;
491 }
492
493 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200494 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 +0200495 va_end(ap);
496
497 free(plugin_msg);
498}
499
500API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200501ly_err_print(struct ly_err_item *eitem)
502{
503 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200504 if (log_clb) {
505 log_clb(eitem->level, eitem->msg, eitem->path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200506 } else {
507 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
508 if (eitem->path) {
509 fprintf(stderr, "(path: %s)\n", eitem->path);
510 }
511 }
512 }
513}