blob: b600b62df50d4dea750e5611f9ea4ea4722001cb [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 Krejci5aeea3a2018-09-05 13:29:36 +020027
28THREAD_LOCAL enum int_log_opts log_opt;
29volatile 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);
254 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
255 /* 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
294 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
295 /* change error to warning */
296 level = LY_LLWRN;
297 }
298
299 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
300 /* do not print or store the message */
301 free(path);
302 return;
303 }
304
305 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
306 /* 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) */
311 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
312 if (!format) {
313 assert(path);
314 /* postponed print of path related to the previous error, do not rewrite stored original message */
315 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
316 return;
317 }
318 msg = "Path is related to the previous error message.";
319 } else {
320 if (vasprintf(&msg, format, args) == -1) {
321 LOGMEM(ctx);
322 free(path);
323 return;
324 }
325 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
326 return;
327 }
328 }
329 free_strs = 0;
330 } else {
331 if (vasprintf(&msg, format, args) == -1) {
332 LOGMEM(ctx);
333 free(path);
334 return;
335 }
336 free_strs = 1;
337 }
338
339 /* if we are only storing errors internally, never print the message (yet) */
340 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
341 if (ly_log_clb) {
342 ly_log_clb(level, msg, path);
343 } else {
344 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
345 if (path) {
346 fprintf(stderr, "(path: %s)\n", path);
347 }
348 }
349 }
350
351 if (free_strs) {
352 free(path);
353 free(msg);
354 }
355}
356
Radek Krejci4ab61562018-09-05 15:00:37 +0200357#ifndef NDEBUG
358
359void
360ly_log_dbg(int group, const char *format, ...)
361{
362 char *dbg_format;
363 const char *str_group;
364 va_list ap;
365
366 if (!(ly_log_dbg_groups & group)) {
367 return;
368 }
369
370 switch (group) {
371 case LY_LDGDICT:
372 str_group = "DICT";
373 break;
374 case LY_LDGYANG:
375 str_group = "YANG";
376 break;
377 case LY_LDGYIN:
378 str_group = "YIN";
379 break;
380 case LY_LDGXPATH:
381 str_group = "XPATH";
382 break;
383 case LY_LDGDIFF:
384 str_group = "DIFF";
385 break;
386 default:
387 LOGINT(NULL);
388 return;
389 }
390
391 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
392 LOGMEM(NULL);
393 return;
394 }
395
396 va_start(ap, format);
397 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
398 va_end(ap);
399}
400
401#endif
402
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200403void
404ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
405{
406 va_list ap;
407
408 va_start(ap, format);
409 log_vprintf(ctx, level, no, 0, NULL, format, ap);
410 va_end(ap);
411}
412
Radek Krejci94aa9942018-09-07 17:12:17 +0200413static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200414ly_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 +0200415{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200416 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200417
Radek Krejcic04f0a22018-09-21 15:49:45 +0200418 switch (elem_type) {
419 case LY_VLOG_STR:
420 (*path) = strdup(elem);
421 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
422 break;
423 case LY_VLOG_LINE:
424 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
425 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
426 break;
427 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;
439 char* path = NULL;
440 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
467ly_err_print(struct ly_err_item *eitem)
468{
469 if (ly_log_opts & LY_LOLOG) {
470 if (ly_log_clb) {
471 ly_log_clb(eitem->level, eitem->msg, eitem->path);
472 } else {
473 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
474 if (eitem->path) {
475 fprintf(stderr, "(path: %s)\n", eitem->path);
476 }
477 }
478 }
479}
480
Radek Krejcie7b95092019-05-15 11:03:07 +0200481void
482ly_err_last_set_apptag(const struct ly_ctx *ctx, const char *apptag)
483{
484 struct ly_err_item *i;
485
486 if (log_opt != ILO_IGNORE) {
487 i = ly_err_first(ctx);
488 if (i) {
489 i = i->prev;
490 i->apptag = strdup(apptag);
491 }
492 }
493}