blob: 8efac1aefa827e03c7c6cb0c922cb12a155290a7 [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
29THREAD_LOCAL enum int_log_opts log_opt;
30volatile uint8_t ly_log_level = LY_LLWRN;
31volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
32static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
33static volatile int path_flag = 1;
34#ifndef NDEBUG
35volatile int ly_log_dbg_groups = 0;
36#endif
37
Radek Krejci94aa9942018-09-07 17:12:17 +020038/* how many bytes add when enlarging buffers */
39#define LY_BUF_STEP 128
40
Radek Krejcid33273d2018-10-25 14:55:52 +020041API LY_ERR
42ly_errcode(const struct ly_ctx *ctx)
43{
44 struct ly_err_item *i;
45
46 i = ly_err_first(ctx);
47 if (i) {
48 return i->prev->no;
49 }
50
51 return LY_SUCCESS;
52}
53
Radek Krejci5aeea3a2018-09-05 13:29:36 +020054API LY_VECODE
55ly_vecode(const struct ly_ctx *ctx)
56{
57 struct ly_err_item *i;
58
59 i = ly_err_first(ctx);
60 if (i) {
61 return i->prev->vecode;
62 }
63
64 return LYVE_SUCCESS;
65}
66
67API const char *
68ly_errmsg(const struct ly_ctx *ctx)
69{
70 struct ly_err_item *i;
71
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020072 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020073
74 i = ly_err_first(ctx);
75 if (i) {
76 return i->prev->msg;
77 }
78
79 return NULL;
80}
81
82API const char *
83ly_errpath(const struct ly_ctx *ctx)
84{
85 struct ly_err_item *i;
86
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020087 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020088
89 i = ly_err_first(ctx);
90 if (i) {
91 return i->prev->path;
92 }
93
94 return NULL;
95}
96
97API const char *
98ly_errapptag(const struct ly_ctx *ctx)
99{
100 struct ly_err_item *i;
101
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200102 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200103
104 i = ly_err_first(ctx);
105 if (i) {
106 return i->prev->apptag;
107 }
108
109 return NULL;
110}
111
112API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200113ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
114{
115 struct ly_err_item *eitem;
116
117 eitem = malloc(sizeof *eitem);
118 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
119 eitem->prev = eitem;
120 eitem->next = NULL;
121
122 /* fill in the information */
123 eitem->level = level;
124 eitem->no = no;
125 eitem->vecode = vecode;
126 eitem->msg = msg;
127 eitem->path = path;
128 eitem->apptag = apptag;
129
130 return eitem;
131}
132
133API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200134ly_err_first(const struct ly_ctx *ctx)
135{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200136 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200137
138 return pthread_getspecific(ctx->errlist_key);
139}
140
Radek Krejcie7b95092019-05-15 11:03:07 +0200141API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200142ly_err_free(void *ptr)
143{
144 struct ly_err_item *i, *next;
145
146 /* clean the error list */
147 for (i = (struct ly_err_item *)ptr; i; i = next) {
148 next = i->next;
149 free(i->msg);
150 free(i->path);
151 free(i->apptag);
152 free(i);
153 }
154}
155
156API void
157ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
158{
159 struct ly_err_item *i, *first;
160
161 first = ly_err_first(ctx);
162 if (first == eitem) {
163 eitem = NULL;
164 }
165 if (eitem) {
166 /* disconnect the error */
167 for (i = first; i && (i->next != eitem); i = i->next);
168 assert(i);
169 i->next = NULL;
170 first->prev = i;
171 /* free this err and newer */
172 ly_err_free(eitem);
173 } else {
174 /* free all err */
175 ly_err_free(first);
176 pthread_setspecific(ctx->errlist_key, NULL);
177 }
178}
179
180API LY_LOG_LEVEL
181ly_verb(LY_LOG_LEVEL level)
182{
183 LY_LOG_LEVEL prev = ly_log_level;
184
185 ly_log_level = level;
186 return prev;
187}
188
189API int
190ly_log_options(int opts)
191{
192 uint8_t prev = ly_log_opts;
193
194 ly_log_opts = opts;
195 return prev;
196}
197
198API void
199ly_verb_dbg(int dbg_groups)
200{
201#ifndef NDEBUG
202 ly_log_dbg_groups = dbg_groups;
203#else
204 (void)dbg_groups;
205#endif
206}
207
208API void
209ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
210{
211 ly_log_clb = clb;
212 path_flag = path;
213}
214
215API void
216(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
217{
218 return ly_log_clb;
219}
220
221static LY_ERR
222log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
223{
224 struct ly_err_item *eitem, *last;
225
226 assert(ctx && (level < LY_LLVRB));
227
228 eitem = pthread_getspecific(ctx->errlist_key);
229 if (!eitem) {
230 /* if we are only to fill in path, there must have been an error stored */
231 assert(msg);
232 eitem = malloc(sizeof *eitem);
233 LY_CHECK_GOTO(!eitem, mem_fail);
234 eitem->prev = eitem;
235 eitem->next = NULL;
236
237 pthread_setspecific(ctx->errlist_key, eitem);
238 } else if (!msg) {
239 /* only filling the path */
240 assert(path);
241
242 /* find last error */
243 eitem = eitem->prev;
244 do {
245 if (eitem->level == LY_LLERR) {
246 /* fill the path */
247 free(eitem->path);
248 eitem->path = path;
249 return LY_SUCCESS;
250 }
251 eitem = eitem->prev;
252 } while (eitem->prev->next);
253 /* last error was not found */
254 assert(0);
255 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
256 /* overwrite last message */
257 free(eitem->msg);
258 free(eitem->path);
259 free(eitem->apptag);
260 } else {
261 /* store new message */
262 last = eitem->prev;
263 eitem->prev = malloc(sizeof *eitem);
264 LY_CHECK_GOTO(!eitem->prev, mem_fail);
265 eitem = eitem->prev;
266 eitem->prev = last;
267 eitem->next = NULL;
268 last->next = eitem;
269 }
270
271 /* fill in the information */
272 eitem->level = level;
273 eitem->no = no;
274 eitem->vecode = vecode;
275 eitem->msg = msg;
276 eitem->path = path;
277 eitem->apptag = apptag;
278 return LY_SUCCESS;
279
280mem_fail:
281 LOGMEM(NULL);
282 free(msg);
283 free(path);
284 free(apptag);
285 return LY_EMEM;
286}
287
288static void
289log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
290 const char *format, va_list args)
291{
292 char *msg = NULL;
293 int free_strs;
294
295 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
296 /* change error to warning */
297 level = LY_LLWRN;
298 }
299
300 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
301 /* do not print or store the message */
302 free(path);
303 return;
304 }
305
306 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
307 /* assume we are inheriting the error, so inherit vecode as well */
308 vecode = ly_vecode(ctx);
309 }
310
311 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
312 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
313 if (!format) {
314 assert(path);
315 /* postponed print of path related to the previous error, do not rewrite stored original message */
316 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
317 return;
318 }
319 msg = "Path is related to the previous error message.";
320 } else {
321 if (vasprintf(&msg, format, args) == -1) {
322 LOGMEM(ctx);
323 free(path);
324 return;
325 }
326 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
327 return;
328 }
329 }
330 free_strs = 0;
331 } else {
332 if (vasprintf(&msg, format, args) == -1) {
333 LOGMEM(ctx);
334 free(path);
335 return;
336 }
337 free_strs = 1;
338 }
339
340 /* if we are only storing errors internally, never print the message (yet) */
341 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
342 if (ly_log_clb) {
343 ly_log_clb(level, msg, path);
344 } else {
345 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
346 if (path) {
347 fprintf(stderr, "(path: %s)\n", path);
348 }
349 }
350 }
351
352 if (free_strs) {
353 free(path);
354 free(msg);
355 }
356}
357
Radek Krejci4ab61562018-09-05 15:00:37 +0200358#ifndef NDEBUG
359
360void
361ly_log_dbg(int group, const char *format, ...)
362{
363 char *dbg_format;
364 const char *str_group;
365 va_list ap;
366
367 if (!(ly_log_dbg_groups & group)) {
368 return;
369 }
370
371 switch (group) {
372 case LY_LDGDICT:
373 str_group = "DICT";
374 break;
375 case LY_LDGYANG:
376 str_group = "YANG";
377 break;
378 case LY_LDGYIN:
379 str_group = "YIN";
380 break;
381 case LY_LDGXPATH:
382 str_group = "XPATH";
383 break;
384 case LY_LDGDIFF:
385 str_group = "DIFF";
386 break;
387 default:
388 LOGINT(NULL);
389 return;
390 }
391
392 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
393 LOGMEM(NULL);
394 return;
395 }
396
397 va_start(ap, format);
398 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
399 va_end(ap);
400}
401
402#endif
403
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200404void
405ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
406{
407 va_list ap;
408
409 va_start(ap, format);
410 log_vprintf(ctx, level, no, 0, NULL, format, ap);
411 va_end(ap);
412}
413
Radek Krejci94aa9942018-09-07 17:12:17 +0200414static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200415ly_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 +0200416{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200417 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200418
Radek Krejcic04f0a22018-09-21 15:49:45 +0200419 switch (elem_type) {
420 case LY_VLOG_STR:
421 (*path) = strdup(elem);
422 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
423 break;
424 case LY_VLOG_LINE:
425 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
426 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
427 break;
428 default:
429 /* shouldn't be here */
430 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200431 }
432
Radek Krejci94aa9942018-09-07 17:12:17 +0200433 return LY_SUCCESS;
434}
435
436void
437ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
438{
439 va_list ap;
440 char* path = NULL;
441 const struct ly_err_item *first;
442
443 if (path_flag && (elem_type != LY_VLOG_NONE)) {
444 if (elem_type == LY_VLOG_PREV) {
445 /* use previous path */
446 first = ly_err_first(ctx);
447 if (first && first->prev->path) {
448 path = strdup(first->prev->path);
449 }
450 } else {
451 /* print path */
452 if (!elem) {
453 /* top-level */
454 path = strdup("/");
455 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200456 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200457 }
458 }
459 }
460
461 va_start(ap, format);
462 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
463 /* path is spent and should not be freed! */
464 va_end(ap);
465}
466
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200467API void
Radek Krejci0935f412019-08-20 16:15:18 +0200468lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
469{
470 va_list ap;
471 char *plugin_msg;
472 int ret;
473
474 if (ly_log_level < level) {
475 return;
476 }
477 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
478 if (ret == -1) {
479 LOGMEM(ext->def->module->ctx);
480 return;
481 }
482
483 va_start(ap, format);
484 log_vprintf(ext->def->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
485 va_end(ap);
486
487 free(plugin_msg);
488}
489
490API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200491ly_err_print(struct ly_err_item *eitem)
492{
493 if (ly_log_opts & LY_LOLOG) {
494 if (ly_log_clb) {
495 ly_log_clb(eitem->level, eitem->msg, eitem->path);
496 } else {
497 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
498 if (eitem->path) {
499 fprintf(stderr, "(path: %s)\n", eitem->path);
500 }
501 }
502 }
503}
504
Radek Krejcie7b95092019-05-15 11:03:07 +0200505void
506ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
507{
508 struct ly_err_item *i;
509
510 if (log_opt != ILO_IGNORE) {
511 i = ly_err_first(ctx);
512 if (i) {
513 i = i->prev;
514 i->apptag = strdup(apptag);
515 }
516 }
517}