blob: b5764374281638a0f48e8b6d79efc927b2e1d211 [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 Krejci5aeea3a2018-09-05 13:29:36 +020019#include <stdarg.h>
20#include <stdio.h>
21
22#include "libyang.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020023#include "context.h"
24
25THREAD_LOCAL enum int_log_opts log_opt;
26volatile uint8_t ly_log_level = LY_LLWRN;
27volatile uint8_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
28static void (*ly_log_clb)(LY_LOG_LEVEL level, const char *msg, const char *path);
29static volatile int path_flag = 1;
30#ifndef NDEBUG
31volatile int ly_log_dbg_groups = 0;
32#endif
33
Radek Krejci94aa9942018-09-07 17:12:17 +020034/* how many bytes add when enlarging buffers */
35#define LY_BUF_STEP 128
36
Radek Krejcid33273d2018-10-25 14:55:52 +020037API LY_ERR
38ly_errcode(const struct ly_ctx *ctx)
39{
40 struct ly_err_item *i;
41
42 i = ly_err_first(ctx);
43 if (i) {
44 return i->prev->no;
45 }
46
47 return LY_SUCCESS;
48}
49
Radek Krejci5aeea3a2018-09-05 13:29:36 +020050API LY_VECODE
51ly_vecode(const struct ly_ctx *ctx)
52{
53 struct ly_err_item *i;
54
55 i = ly_err_first(ctx);
56 if (i) {
57 return i->prev->vecode;
58 }
59
60 return LYVE_SUCCESS;
61}
62
63API const char *
64ly_errmsg(const struct ly_ctx *ctx)
65{
66 struct ly_err_item *i;
67
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020068 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020069
70 i = ly_err_first(ctx);
71 if (i) {
72 return i->prev->msg;
73 }
74
75 return NULL;
76}
77
78API const char *
79ly_errpath(const struct ly_ctx *ctx)
80{
81 struct ly_err_item *i;
82
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020083 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020084
85 i = ly_err_first(ctx);
86 if (i) {
87 return i->prev->path;
88 }
89
90 return NULL;
91}
92
93API const char *
94ly_errapptag(const struct ly_ctx *ctx)
95{
96 struct ly_err_item *i;
97
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020098 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020099
100 i = ly_err_first(ctx);
101 if (i) {
102 return i->prev->apptag;
103 }
104
105 return NULL;
106}
107
108API struct ly_err_item *
109ly_err_first(const struct ly_ctx *ctx)
110{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200111 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200112
113 return pthread_getspecific(ctx->errlist_key);
114}
115
116void
117ly_err_free(void *ptr)
118{
119 struct ly_err_item *i, *next;
120
121 /* clean the error list */
122 for (i = (struct ly_err_item *)ptr; i; i = next) {
123 next = i->next;
124 free(i->msg);
125 free(i->path);
126 free(i->apptag);
127 free(i);
128 }
129}
130
131API void
132ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
133{
134 struct ly_err_item *i, *first;
135
136 first = ly_err_first(ctx);
137 if (first == eitem) {
138 eitem = NULL;
139 }
140 if (eitem) {
141 /* disconnect the error */
142 for (i = first; i && (i->next != eitem); i = i->next);
143 assert(i);
144 i->next = NULL;
145 first->prev = i;
146 /* free this err and newer */
147 ly_err_free(eitem);
148 } else {
149 /* free all err */
150 ly_err_free(first);
151 pthread_setspecific(ctx->errlist_key, NULL);
152 }
153}
154
155API LY_LOG_LEVEL
156ly_verb(LY_LOG_LEVEL level)
157{
158 LY_LOG_LEVEL prev = ly_log_level;
159
160 ly_log_level = level;
161 return prev;
162}
163
164API int
165ly_log_options(int opts)
166{
167 uint8_t prev = ly_log_opts;
168
169 ly_log_opts = opts;
170 return prev;
171}
172
173API void
174ly_verb_dbg(int dbg_groups)
175{
176#ifndef NDEBUG
177 ly_log_dbg_groups = dbg_groups;
178#else
179 (void)dbg_groups;
180#endif
181}
182
183API void
184ly_set_log_clb(void (*clb)(LY_LOG_LEVEL level, const char *msg, const char *path), int path)
185{
186 ly_log_clb = clb;
187 path_flag = path;
188}
189
190API void
191(*ly_get_log_clb(void))(LY_LOG_LEVEL, const char *, const char *)
192{
193 return ly_log_clb;
194}
195
196static LY_ERR
197log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
198{
199 struct ly_err_item *eitem, *last;
200
201 assert(ctx && (level < LY_LLVRB));
202
203 eitem = pthread_getspecific(ctx->errlist_key);
204 if (!eitem) {
205 /* if we are only to fill in path, there must have been an error stored */
206 assert(msg);
207 eitem = malloc(sizeof *eitem);
208 LY_CHECK_GOTO(!eitem, mem_fail);
209 eitem->prev = eitem;
210 eitem->next = NULL;
211
212 pthread_setspecific(ctx->errlist_key, eitem);
213 } else if (!msg) {
214 /* only filling the path */
215 assert(path);
216
217 /* find last error */
218 eitem = eitem->prev;
219 do {
220 if (eitem->level == LY_LLERR) {
221 /* fill the path */
222 free(eitem->path);
223 eitem->path = path;
224 return LY_SUCCESS;
225 }
226 eitem = eitem->prev;
227 } while (eitem->prev->next);
228 /* last error was not found */
229 assert(0);
230 } else if ((log_opt != ILO_STORE) && ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST)) {
231 /* overwrite last message */
232 free(eitem->msg);
233 free(eitem->path);
234 free(eitem->apptag);
235 } else {
236 /* store new message */
237 last = eitem->prev;
238 eitem->prev = malloc(sizeof *eitem);
239 LY_CHECK_GOTO(!eitem->prev, mem_fail);
240 eitem = eitem->prev;
241 eitem->prev = last;
242 eitem->next = NULL;
243 last->next = eitem;
244 }
245
246 /* fill in the information */
247 eitem->level = level;
248 eitem->no = no;
249 eitem->vecode = vecode;
250 eitem->msg = msg;
251 eitem->path = path;
252 eitem->apptag = apptag;
253 return LY_SUCCESS;
254
255mem_fail:
256 LOGMEM(NULL);
257 free(msg);
258 free(path);
259 free(apptag);
260 return LY_EMEM;
261}
262
263static void
264log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
265 const char *format, va_list args)
266{
267 char *msg = NULL;
268 int free_strs;
269
270 if ((log_opt == ILO_ERR2WRN) && (level == LY_LLERR)) {
271 /* change error to warning */
272 level = LY_LLWRN;
273 }
274
275 if ((log_opt == ILO_IGNORE) || (level > ly_log_level)) {
276 /* do not print or store the message */
277 free(path);
278 return;
279 }
280
281 if ((no == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
282 /* assume we are inheriting the error, so inherit vecode as well */
283 vecode = ly_vecode(ctx);
284 }
285
286 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
287 if ((level < LY_LLVRB) && ctx && ((ly_log_opts & LY_LOSTORE) || (log_opt == ILO_STORE))) {
288 if (!format) {
289 assert(path);
290 /* postponed print of path related to the previous error, do not rewrite stored original message */
291 if (log_store(ctx, level, no, vecode, NULL, path, NULL)) {
292 return;
293 }
294 msg = "Path is related to the previous error message.";
295 } else {
296 if (vasprintf(&msg, format, args) == -1) {
297 LOGMEM(ctx);
298 free(path);
299 return;
300 }
301 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
302 return;
303 }
304 }
305 free_strs = 0;
306 } else {
307 if (vasprintf(&msg, format, args) == -1) {
308 LOGMEM(ctx);
309 free(path);
310 return;
311 }
312 free_strs = 1;
313 }
314
315 /* if we are only storing errors internally, never print the message (yet) */
316 if ((ly_log_opts & LY_LOLOG) && (log_opt != ILO_STORE)) {
317 if (ly_log_clb) {
318 ly_log_clb(level, msg, path);
319 } else {
320 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
321 if (path) {
322 fprintf(stderr, "(path: %s)\n", path);
323 }
324 }
325 }
326
327 if (free_strs) {
328 free(path);
329 free(msg);
330 }
331}
332
Radek Krejci4ab61562018-09-05 15:00:37 +0200333#ifndef NDEBUG
334
335void
336ly_log_dbg(int group, const char *format, ...)
337{
338 char *dbg_format;
339 const char *str_group;
340 va_list ap;
341
342 if (!(ly_log_dbg_groups & group)) {
343 return;
344 }
345
346 switch (group) {
347 case LY_LDGDICT:
348 str_group = "DICT";
349 break;
350 case LY_LDGYANG:
351 str_group = "YANG";
352 break;
353 case LY_LDGYIN:
354 str_group = "YIN";
355 break;
356 case LY_LDGXPATH:
357 str_group = "XPATH";
358 break;
359 case LY_LDGDIFF:
360 str_group = "DIFF";
361 break;
362 default:
363 LOGINT(NULL);
364 return;
365 }
366
367 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
368 LOGMEM(NULL);
369 return;
370 }
371
372 va_start(ap, format);
373 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
374 va_end(ap);
375}
376
377#endif
378
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200379void
380ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
381{
382 va_list ap;
383
384 va_start(ap, format);
385 log_vprintf(ctx, level, no, 0, NULL, format, ap);
386 va_end(ap);
387}
388
Radek Krejci94aa9942018-09-07 17:12:17 +0200389static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200390ly_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 +0200391{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200392 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200393
Radek Krejcic04f0a22018-09-21 15:49:45 +0200394 switch (elem_type) {
395 case LY_VLOG_STR:
396 (*path) = strdup(elem);
397 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
398 break;
399 case LY_VLOG_LINE:
400 rc = asprintf(path, "Line number %"PRIu64".", *((uint64_t*)elem));
401 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
402 break;
403 default:
404 /* shouldn't be here */
405 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200406 }
407
Radek Krejci94aa9942018-09-07 17:12:17 +0200408 return LY_SUCCESS;
409}
410
411void
412ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
413{
414 va_list ap;
415 char* path = NULL;
416 const struct ly_err_item *first;
417
418 if (path_flag && (elem_type != LY_VLOG_NONE)) {
419 if (elem_type == LY_VLOG_PREV) {
420 /* use previous path */
421 first = ly_err_first(ctx);
422 if (first && first->prev->path) {
423 path = strdup(first->prev->path);
424 }
425 } else {
426 /* print path */
427 if (!elem) {
428 /* top-level */
429 path = strdup("/");
430 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200431 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200432 }
433 }
434 }
435
436 va_start(ap, format);
437 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
438 /* path is spent and should not be freed! */
439 va_end(ap);
440}
441
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200442API void
443ly_err_print(struct ly_err_item *eitem)
444{
445 if (ly_log_opts & LY_LOLOG) {
446 if (ly_log_clb) {
447 ly_log_clb(eitem->level, eitem->msg, eitem->path);
448 } else {
449 fprintf(stderr, "libyang[%d]: %s%s", eitem->level, eitem->msg, eitem->path ? " " : "\n");
450 if (eitem->path) {
451 fprintf(stderr, "(path: %s)\n", eitem->path);
452 }
453 }
454 }
455}
456