blob: 22d685542532e49733dc59d764c2f6de9788ca13 [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 Krejcib7db73a2018-10-24 14:18:40 +020015#include "common.h"
16
Radek Krejci5aeea3a2018-09-05 13:29:36 +020017#include <assert.h>
Radek Krejcic04f0a22018-09-21 15:49:45 +020018#include <inttypes.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020019#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020020#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020021#include <stdint.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020022#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020023#include <stdlib.h>
24#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020025
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include "log.h"
Radek Krejci0935f412019-08-20 16:15:18 +020027#include "plugins_exts.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020028
Radek Krejci5aeea3a2018-09-05 13:29:36 +020029volatile uint8_t ly_log_level = LY_LLWRN;
30volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
31static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
32static volatile int path_flag = 1;
33#ifndef NDEBUG
34volatile int ly_log_dbg_groups = 0;
35#endif
36
Radek Krejci94aa9942018-09-07 17:12:17 +020037/* how many bytes add when enlarging buffers */
38#define LY_BUF_STEP 128
39
Radek Krejcid33273d2018-10-25 14:55:52 +020040API LY_ERR
41ly_errcode(const struct ly_ctx *ctx)
42{
43 struct ly_err_item *i;
44
45 i = ly_err_first(ctx);
46 if (i) {
47 return i->prev->no;
48 }
49
50 return LY_SUCCESS;
51}
52
Radek Krejci5aeea3a2018-09-05 13:29:36 +020053API LY_VECODE
54ly_vecode(const struct ly_ctx *ctx)
55{
56 struct ly_err_item *i;
57
58 i = ly_err_first(ctx);
59 if (i) {
60 return i->prev->vecode;
61 }
62
63 return LYVE_SUCCESS;
64}
65
66API const char *
67ly_errmsg(const struct ly_ctx *ctx)
68{
69 struct ly_err_item *i;
70
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020071 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020072
73 i = ly_err_first(ctx);
74 if (i) {
75 return i->prev->msg;
76 }
77
78 return NULL;
79}
80
81API const char *
82ly_errpath(const struct ly_ctx *ctx)
83{
84 struct ly_err_item *i;
85
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020086 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020087
88 i = ly_err_first(ctx);
89 if (i) {
90 return i->prev->path;
91 }
92
93 return NULL;
94}
95
96API const char *
97ly_errapptag(const struct ly_ctx *ctx)
98{
99 struct ly_err_item *i;
100
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200101 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200102
103 i = ly_err_first(ctx);
104 if (i) {
105 return i->prev->apptag;
106 }
107
108 return NULL;
109}
110
111API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200112ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
113{
114 struct ly_err_item *eitem;
115
116 eitem = malloc(sizeof *eitem);
117 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
118 eitem->prev = eitem;
119 eitem->next = NULL;
120
121 /* fill in the information */
122 eitem->level = level;
123 eitem->no = no;
124 eitem->vecode = vecode;
125 eitem->msg = msg;
126 eitem->path = path;
127 eitem->apptag = apptag;
128
129 return eitem;
130}
131
132API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200133ly_err_first(const struct ly_ctx *ctx)
134{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200135 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200136
137 return pthread_getspecific(ctx->errlist_key);
138}
139
Radek Krejcie7b95092019-05-15 11:03:07 +0200140API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200141ly_err_free(void *ptr)
142{
143 struct ly_err_item *i, *next;
144
145 /* clean the error list */
146 for (i = (struct ly_err_item *)ptr; i; i = next) {
147 next = i->next;
148 free(i->msg);
149 free(i->path);
150 free(i->apptag);
151 free(i);
152 }
153}
154
155API void
156ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
157{
158 struct ly_err_item *i, *first;
159
160 first = ly_err_first(ctx);
161 if (first == eitem) {
162 eitem = NULL;
163 }
164 if (eitem) {
165 /* disconnect the error */
166 for (i = first; i && (i->next != eitem); i = i->next);
167 assert(i);
168 i->next = NULL;
169 first->prev = i;
170 /* free this err and newer */
171 ly_err_free(eitem);
172 } else {
173 /* free all err */
174 ly_err_free(first);
175 pthread_setspecific(ctx->errlist_key, NULL);
176 }
177}
178
179API LY_LOG_LEVEL
180ly_verb(LY_LOG_LEVEL level)
181{
182 LY_LOG_LEVEL prev = ly_log_level;
183
184 ly_log_level = level;
185 return prev;
186}
187
188API int
189ly_log_options(int opts)
190{
191 uint8_t prev = ly_log_opts;
192
193 ly_log_opts = opts;
194 return prev;
195}
196
197API void
198ly_verb_dbg(int dbg_groups)
199{
200#ifndef NDEBUG
201 ly_log_dbg_groups = dbg_groups;
202#else
203 (void)dbg_groups;
204#endif
205}
206
207API void
208ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
209{
210 ly_log_clb = clb;
211 path_flag = path;
212}
213
214API void
215(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
216{
217 return ly_log_clb;
218}
219
220static LY_ERR
221log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
222{
223 struct ly_err_item *eitem, *last;
224
225 assert(ctx && (level < LY_LLVRB));
226
227 eitem = pthread_getspecific(ctx->errlist_key);
228 if (!eitem) {
229 /* if we are only to fill in path, there must have been an error stored */
230 assert(msg);
231 eitem = malloc(sizeof *eitem);
232 LY_CHECK_GOTO(!eitem, mem_fail);
233 eitem->prev = eitem;
234 eitem->next = NULL;
235
236 pthread_setspecific(ctx->errlist_key, eitem);
237 } else if (!msg) {
238 /* only filling the path */
239 assert(path);
240
241 /* find last error */
242 eitem = eitem->prev;
243 do {
244 if (eitem->level == LY_LLERR) {
245 /* fill the path */
246 free(eitem->path);
247 eitem->path = path;
248 return LY_SUCCESS;
249 }
250 eitem = eitem->prev;
251 } while (eitem->prev->next);
252 /* last error was not found */
253 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100254 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200255 /* overwrite last message */
256 free(eitem->msg);
257 free(eitem->path);
258 free(eitem->apptag);
259 } else {
260 /* store new message */
261 last = eitem->prev;
262 eitem->prev = malloc(sizeof *eitem);
263 LY_CHECK_GOTO(!eitem->prev, mem_fail);
264 eitem = eitem->prev;
265 eitem->prev = last;
266 eitem->next = NULL;
267 last->next = eitem;
268 }
269
270 /* fill in the information */
271 eitem->level = level;
272 eitem->no = no;
273 eitem->vecode = vecode;
274 eitem->msg = msg;
275 eitem->path = path;
276 eitem->apptag = apptag;
277 return LY_SUCCESS;
278
279mem_fail:
280 LOGMEM(NULL);
281 free(msg);
282 free(path);
283 free(apptag);
284 return LY_EMEM;
285}
286
287static void
288log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
289 const char *format, va_list args)
290{
291 char *msg = NULL;
292 int free_strs;
293
Michal Vaskoed94a292019-11-06 15:43:41 +0100294 if (level > ly_log_level) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200295 /* do not print or store the message */
296 free(path);
297 return;
298 }
299
300 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
301 /* assume we are inheriting the error, so inherit vecode as well */
302 vecode = ly_vecode(ctx);
303 }
304
305 /* 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 +0100306 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200307 if (!format) {
308 assert(path);
309 /* postponed print of path related to the previous error, do not rewrite stored original message */
310 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
311 return;
312 }
313 msg = "Path is related to the previous error message.";
314 } else {
315 if (vasprintf(&msg, format, args) == -1) {
316 LOGMEM(ctx);
317 free(path);
318 return;
319 }
320 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
321 return;
322 }
323 }
324 free_strs = 0;
325 } else {
326 if (vasprintf(&msg, format, args) == -1) {
327 LOGMEM(ctx);
328 free(path);
329 return;
330 }
331 free_strs = 1;
332 }
333
334 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100335 if (ly_log_opts & LY_LOLOG) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200336 if (ly_log_clb) {
337 ly_log_clb(level, msg, path);
338 } else {
339 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
340 if (path) {
341 fprintf(stderr, "(path: %s)\n", path);
342 }
343 }
344 }
345
346 if (free_strs) {
347 free(path);
348 free(msg);
349 }
350}
351
Radek Krejci4ab61562018-09-05 15:00:37 +0200352#ifndef NDEBUG
353
354void
355ly_log_dbg(int group, const char *format, ...)
356{
357 char *dbg_format;
358 const char *str_group;
359 va_list ap;
360
361 if (!(ly_log_dbg_groups & group)) {
362 return;
363 }
364
365 switch (group) {
366 case LY_LDGDICT:
367 str_group = "DICT";
368 break;
369 case LY_LDGYANG:
370 str_group = "YANG";
371 break;
372 case LY_LDGYIN:
373 str_group = "YIN";
374 break;
375 case LY_LDGXPATH:
376 str_group = "XPATH";
377 break;
378 case LY_LDGDIFF:
379 str_group = "DIFF";
380 break;
381 default:
382 LOGINT(NULL);
383 return;
384 }
385
386 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
387 LOGMEM(NULL);
388 return;
389 }
390
391 va_start(ap, format);
392 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
393 va_end(ap);
394}
395
396#endif
397
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200398void
399ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
400{
401 va_list ap;
402
403 va_start(ap, format);
404 log_vprintf(ctx, level, no, 0, NULL, format, ap);
405 va_end(ap);
406}
407
Radek Krejci94aa9942018-09-07 17:12:17 +0200408static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200409ly_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 +0200410{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200411 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200412
Radek Krejcic04f0a22018-09-21 15:49:45 +0200413 switch (elem_type) {
414 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100415 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200416 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
417 break;
418 case LY_VLOG_LINE:
419 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
420 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
421 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100422 case LY_VLOG_LYSC:
423 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
424 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
425 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100426 case LY_VLOG_LYD:
427 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
428 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
429 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200430 default:
431 /* shouldn't be here */
432 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200433 }
434
Radek Krejci94aa9942018-09-07 17:12:17 +0200435 return LY_SUCCESS;
436}
437
438void
439ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
440{
441 va_list ap;
442 char* path = NULL;
443 const struct ly_err_item *first;
444
445 if (path_flag && (elem_type != LY_VLOG_NONE)) {
446 if (elem_type == LY_VLOG_PREV) {
447 /* use previous path */
448 first = ly_err_first(ctx);
449 if (first && first->prev->path) {
450 path = strdup(first->prev->path);
451 }
452 } else {
453 /* print path */
454 if (!elem) {
455 /* top-level */
456 path = strdup("/");
457 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200458 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200459 }
460 }
461 }
462
463 va_start(ap, format);
464 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
465 /* path is spent and should not be freed! */
466 va_end(ap);
467}
468
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200469API void
Radek Krejci0935f412019-08-20 16:15:18 +0200470lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
471{
472 va_list ap;
473 char *plugin_msg;
474 int ret;
475
476 if (ly_log_level < level) {
477 return;
478 }
479 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
480 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200481 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200482 return;
483 }
484
485 va_start(ap, format);
Radek Krejci28681fa2019-09-06 13:08:45 +0200486 log_vprintf(ext->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
Radek Krejci0935f412019-08-20 16:15:18 +0200487 va_end(ap);
488
489 free(plugin_msg);
490}
491
492API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200493ly_err_print(struct ly_err_item *eitem)
494{
495 if (ly_log_opts & LY_LOLOG) {
496 if (ly_log_clb) {
497 ly_log_clb(eitem->level, eitem->msg, eitem->path);
498 } else {
499 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
500 if (eitem->path) {
501 fprintf(stderr, "(path: %s)\n", eitem->path);
502 }
503 }
504 }
505}
506
Radek Krejcie7b95092019-05-15 11:03:07 +0200507void
508ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
509{
510 struct ly_err_item *i;
511
Michal Vaskoed94a292019-11-06 15:43:41 +0100512 i = ly_err_first(ctx);
513 if (i) {
514 i = i->prev;
515 i->apptag = strdup(apptag);
Radek Krejcie7b95092019-05-15 11:03:07 +0200516 }
517}