blob: 4ce8d1ecfc61f8c8deee894649b3d2e4480e62ab [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 Krejci52b6d512020-10-12 12:33:17 +020034volatile LY_LOG_LEVEL ly_ll = LY_LLWRN;
Radek Krejci1deb5be2020-08-26 16:43:36 +020035volatile 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 Krejci68433c92020-10-12 17:03:55 +020039volatile uint32_t ly_ldbg_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
Radek Krejci52b6d512020-10-12 12:33:17 +0200196ly_log_level(LY_LOG_LEVEL level)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200197{
Radek Krejci52b6d512020-10-12 12:33:17 +0200198 LY_LOG_LEVEL prev = ly_ll;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200199
Radek Krejci52b6d512020-10-12 12:33:17 +0200200 ly_ll = level;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200201 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 Krejci68433c92020-10-12 17:03:55 +0200214ly_log_dbg_groups(uint32_t dbg_groups)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200215{
216#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +0200217 ly_ldbg_groups = dbg_groups;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200218#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
Radek Krejci52b6d512020-10-12 12:33:17 +0200310 if (level > ly_ll) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200311 /* do not print or store the message */
312 free(path);
313 return;
314 }
315
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200316 /* 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 +0100317 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200318 assert(format);
319 if (vasprintf(&msg, format, args) == -1) {
320 LOGMEM(ctx);
321 free(path);
322 return;
323 }
Radek Krejcic9e64a62020-09-18 20:08:12 +0200324 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
325 /* assume we are inheriting the error, so inherit vecode as well */
326 vecode = ly_vecode(ctx);
327 }
Michal Vasko004d3152020-06-11 19:59:22 +0200328 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
329 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200330 }
331 free_strs = 0;
332 } else {
333 if (vasprintf(&msg, format, args) == -1) {
334 LOGMEM(ctx);
335 free(path);
336 return;
337 }
338 free_strs = 1;
339 }
340
341 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100342 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200343 if (log_clb) {
344 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200345 } else {
346 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
347 if (path) {
348 fprintf(stderr, "(path: %s)\n", path);
349 }
350 }
351 }
352
353 if (free_strs) {
354 free(path);
355 free(msg);
356 }
357}
358
Radek Krejci4ab61562018-09-05 15:00:37 +0200359#ifndef NDEBUG
360
361void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200362ly_log_dbg(uint32_t group, const char *format, ...)
Radek Krejci4ab61562018-09-05 15:00:37 +0200363{
364 char *dbg_format;
365 const char *str_group;
366 va_list ap;
367
Radek Krejci68433c92020-10-12 17:03:55 +0200368 if (!(ly_ldbg_groups & group)) {
Radek Krejci4ab61562018-09-05 15:00:37 +0200369 return;
370 }
371
372 switch (group) {
373 case LY_LDGDICT:
374 str_group = "DICT";
375 break;
376 case LY_LDGYANG:
377 str_group = "YANG";
378 break;
379 case LY_LDGYIN:
380 str_group = "YIN";
381 break;
382 case LY_LDGXPATH:
383 str_group = "XPATH";
384 break;
385 case LY_LDGDIFF:
386 str_group = "DIFF";
387 break;
388 default:
389 LOGINT(NULL);
390 return;
391 }
392
393 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
394 LOGMEM(NULL);
395 return;
396 }
397
398 va_start(ap, format);
399 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
400 va_end(ap);
401}
402
403#endif
404
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200405void
406ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
407{
408 va_list ap;
409
410 va_start(ap, format);
411 log_vprintf(ctx, level, no, 0, NULL, format, ap);
412 va_end(ap);
413}
414
Radek Krejci94aa9942018-09-07 17:12:17 +0200415static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200416ly_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 +0200417{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200418 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200419
Radek Krejcic04f0a22018-09-21 15:49:45 +0200420 switch (elem_type) {
421 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100422 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200423 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
424 break;
425 case LY_VLOG_LINE:
Michal Vasko22df3f02020-08-24 13:29:22 +0200426 rc = asprintf(path, "Line number %" PRIu64 ".", *((uint64_t *)elem));
Radek Krejcic04f0a22018-09-21 15:49:45 +0200427 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
428 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100429 case LY_VLOG_LYSC:
430 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
431 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
432 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100433 case LY_VLOG_LYD:
434 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
435 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
436 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200437 default:
438 /* shouldn't be here */
439 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200440 }
441
Radek Krejci94aa9942018-09-07 17:12:17 +0200442 return LY_SUCCESS;
443}
444
445void
446ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
447{
448 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200449 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200450 const struct ly_err_item *first;
451
452 if (path_flag && (elem_type != LY_VLOG_NONE)) {
453 if (elem_type == LY_VLOG_PREV) {
454 /* use previous path */
455 first = ly_err_first(ctx);
456 if (first && first->prev->path) {
457 path = strdup(first->prev->path);
458 }
459 } else {
460 /* print path */
461 if (!elem) {
462 /* top-level */
463 path = strdup("/");
464 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200465 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200466 }
467 }
468 }
469
470 va_start(ap, format);
471 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
472 /* path is spent and should not be freed! */
473 va_end(ap);
474}
475
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200476API void
Radek Krejci0935f412019-08-20 16:15:18 +0200477lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
478{
479 va_list ap;
480 char *plugin_msg;
481 int ret;
482
Radek Krejci52b6d512020-10-12 12:33:17 +0200483 if (ly_ll < level) {
Radek Krejci0935f412019-08-20 16:15:18 +0200484 return;
485 }
486 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
487 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200488 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200489 return;
490 }
491
492 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200493 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 +0200494 va_end(ap);
495
496 free(plugin_msg);
497}
498
499API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200500ly_err_print(struct ly_err_item *eitem)
501{
502 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200503 if (log_clb) {
504 log_clb(eitem->level, eitem->msg, eitem->path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200505 } else {
506 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
507 if (eitem->path) {
508 fprintf(stderr, "(path: %s)\n", eitem->path);
509 }
510 }
511 }
512}