blob: 5208c945391c65ba708ee5594f566bd566f45f05 [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
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup */
Radek Krejci535ea9f2020-05-29 16:01:05 +020017
18#include "log.h"
Radek Krejcib7db73a2018-10-24 14:18:40 +020019
Radek Krejci5aeea3a2018-09-05 13:29:36 +020020#include <assert.h>
Radek Krejcic04f0a22018-09-21 15:49:45 +020021#include <inttypes.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020023#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include <stdint.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020025#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include <stdlib.h>
27#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020028
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "common.h"
Radek Krejciaa45bda2020-07-20 07:43:38 +020030#include "compat.h"
Radek Krejci0935f412019-08-20 16:15:18 +020031#include "plugins_exts.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020032#include "tree_data.h"
33#include "tree_schema.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020034
Radek Krejci52b6d512020-10-12 12:33:17 +020035volatile LY_LOG_LEVEL ly_ll = LY_LLWRN;
Radek Krejci1deb5be2020-08-26 16:43:36 +020036volatile uint32_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
Michal Vaskod8085612020-08-21 12:55:23 +020037static ly_log_clb log_clb;
Radek Krejci857189e2020-09-01 13:26:36 +020038static volatile ly_bool path_flag = 1;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020039#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +020040volatile uint32_t ly_ldbg_groups = 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020041#endif
42
Radek Krejci94aa9942018-09-07 17:12:17 +020043/* how many bytes add when enlarging buffers */
44#define LY_BUF_STEP 128
45
Radek Krejcid33273d2018-10-25 14:55:52 +020046API LY_ERR
47ly_errcode(const struct ly_ctx *ctx)
48{
49 struct ly_err_item *i;
50
Radek Krejci572ee602020-09-16 14:35:08 +020051 i = ly_err_last(ctx);
Radek Krejcid33273d2018-10-25 14:55:52 +020052 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020053 return i->no;
Radek Krejcid33273d2018-10-25 14:55:52 +020054 }
55
56 return LY_SUCCESS;
57}
58
Radek Krejci5aeea3a2018-09-05 13:29:36 +020059API LY_VECODE
60ly_vecode(const struct ly_ctx *ctx)
61{
62 struct ly_err_item *i;
63
Radek Krejci572ee602020-09-16 14:35:08 +020064 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020065 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020066 return i->vecode;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020067 }
68
69 return LYVE_SUCCESS;
70}
71
72API const char *
73ly_errmsg(const struct ly_ctx *ctx)
74{
75 struct ly_err_item *i;
76
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020077 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020078
Radek Krejci572ee602020-09-16 14:35:08 +020079 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020080 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020081 return i->msg;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020082 }
83
84 return NULL;
85}
86
87API const char *
88ly_errpath(const struct ly_ctx *ctx)
89{
90 struct ly_err_item *i;
91
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020092 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020093
Radek Krejci572ee602020-09-16 14:35:08 +020094 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020095 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020096 return i->path;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020097 }
98
99 return NULL;
100}
101
102API const char *
103ly_errapptag(const struct ly_ctx *ctx)
104{
105 struct ly_err_item *i;
106
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200107 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200108
Radek Krejci572ee602020-09-16 14:35:08 +0200109 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200110 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +0200111 return i->apptag;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200112 }
113
114 return NULL;
115}
116
117API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200118ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
119{
120 struct ly_err_item *eitem;
121
122 eitem = malloc(sizeof *eitem);
123 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
124 eitem->prev = eitem;
125 eitem->next = NULL;
126
127 /* fill in the information */
128 eitem->level = level;
129 eitem->no = no;
130 eitem->vecode = vecode;
131 eitem->msg = msg;
132 eitem->path = path;
133 eitem->apptag = apptag;
134
135 return eitem;
136}
137
138API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200139ly_err_first(const struct ly_ctx *ctx)
140{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200141 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200142
143 return pthread_getspecific(ctx->errlist_key);
144}
145
Radek Krejci572ee602020-09-16 14:35:08 +0200146API struct ly_err_item *
147ly_err_last(const struct ly_ctx *ctx)
148{
149 const struct ly_err_item *e;
150
151 LY_CHECK_ARG_RET(NULL, ctx, NULL);
152
153 e = pthread_getspecific(ctx->errlist_key);
154 return e ? e->prev : NULL;
155}
156
Radek Krejcie7b95092019-05-15 11:03:07 +0200157API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200158ly_err_free(void *ptr)
159{
160 struct ly_err_item *i, *next;
161
162 /* clean the error list */
163 for (i = (struct ly_err_item *)ptr; i; i = next) {
164 next = i->next;
Radek Krejcie2692202020-12-01 14:21:12 +0100165 if (i->msg && strcmp(i->msg, LY_EMEM_MSG)) {
166 free(i->msg);
167 }
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200168 free(i->path);
169 free(i->apptag);
170 free(i);
171 }
172}
173
174API void
175ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
176{
177 struct ly_err_item *i, *first;
178
179 first = ly_err_first(ctx);
180 if (first == eitem) {
181 eitem = NULL;
182 }
183 if (eitem) {
184 /* disconnect the error */
Radek Krejci1e008d22020-08-17 11:37:37 +0200185 for (i = first; i && (i->next != eitem); i = i->next) {}
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200186 assert(i);
187 i->next = NULL;
188 first->prev = i;
189 /* free this err and newer */
190 ly_err_free(eitem);
191 } else {
192 /* free all err */
193 ly_err_free(first);
194 pthread_setspecific(ctx->errlist_key, NULL);
195 }
196}
197
198API LY_LOG_LEVEL
Radek Krejci52b6d512020-10-12 12:33:17 +0200199ly_log_level(LY_LOG_LEVEL level)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200200{
Radek Krejci52b6d512020-10-12 12:33:17 +0200201 LY_LOG_LEVEL prev = ly_ll;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200202
Radek Krejci52b6d512020-10-12 12:33:17 +0200203 ly_ll = level;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200204 return prev;
205}
206
Radek Krejci1deb5be2020-08-26 16:43:36 +0200207API uint32_t
208ly_log_options(uint32_t opts)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200209{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200210 uint32_t prev = ly_log_opts;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200211
212 ly_log_opts = opts;
213 return prev;
214}
215
Radek Krejciebdaed02020-11-09 13:05:06 +0100216API uint32_t
Radek Krejci68433c92020-10-12 17:03:55 +0200217ly_log_dbg_groups(uint32_t dbg_groups)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200218{
219#ifndef NDEBUG
Radek Krejciebdaed02020-11-09 13:05:06 +0100220 uint32_t prev = ly_ldbg_groups;
221
Radek Krejci68433c92020-10-12 17:03:55 +0200222 ly_ldbg_groups = dbg_groups;
Radek Krejciebdaed02020-11-09 13:05:06 +0100223 return prev;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200224#else
225 (void)dbg_groups;
Radek Krejciebdaed02020-11-09 13:05:06 +0100226 return 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200227#endif
228}
229
230API void
Radek Krejci857189e2020-09-01 13:26:36 +0200231ly_set_log_clb(ly_log_clb clb, ly_bool path)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200232{
Michal Vaskod8085612020-08-21 12:55:23 +0200233 log_clb = clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200234 path_flag = path;
235}
236
Michal Vaskod8085612020-08-21 12:55:23 +0200237API ly_log_clb
238ly_get_log_clb(void)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200239{
Michal Vaskod8085612020-08-21 12:55:23 +0200240 return log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200241}
242
243static LY_ERR
244log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
245{
246 struct ly_err_item *eitem, *last;
247
248 assert(ctx && (level < LY_LLVRB));
249
250 eitem = pthread_getspecific(ctx->errlist_key);
251 if (!eitem) {
252 /* if we are only to fill in path, there must have been an error stored */
253 assert(msg);
254 eitem = malloc(sizeof *eitem);
255 LY_CHECK_GOTO(!eitem, mem_fail);
256 eitem->prev = eitem;
257 eitem->next = NULL;
258
259 pthread_setspecific(ctx->errlist_key, eitem);
260 } else if (!msg) {
261 /* only filling the path */
262 assert(path);
263
264 /* find last error */
265 eitem = eitem->prev;
266 do {
267 if (eitem->level == LY_LLERR) {
268 /* fill the path */
269 free(eitem->path);
270 eitem->path = path;
271 return LY_SUCCESS;
272 }
273 eitem = eitem->prev;
274 } while (eitem->prev->next);
275 /* last error was not found */
276 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100277 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200278 /* overwrite last message */
279 free(eitem->msg);
280 free(eitem->path);
281 free(eitem->apptag);
282 } else {
283 /* store new message */
284 last = eitem->prev;
285 eitem->prev = malloc(sizeof *eitem);
286 LY_CHECK_GOTO(!eitem->prev, mem_fail);
287 eitem = eitem->prev;
288 eitem->prev = last;
289 eitem->next = NULL;
290 last->next = eitem;
291 }
292
293 /* fill in the information */
294 eitem->level = level;
295 eitem->no = no;
296 eitem->vecode = vecode;
297 eitem->msg = msg;
298 eitem->path = path;
299 eitem->apptag = apptag;
300 return LY_SUCCESS;
301
302mem_fail:
303 LOGMEM(NULL);
304 free(msg);
305 free(path);
306 free(apptag);
307 return LY_EMEM;
308}
309
310static void
311log_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 +0200312 const char *format, va_list args)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200313{
314 char *msg = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200315 ly_bool free_strs;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200316
Radek Krejci52b6d512020-10-12 12:33:17 +0200317 if (level > ly_ll) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200318 /* do not print or store the message */
319 free(path);
320 return;
321 }
322
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200323 /* 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 +0100324 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200325 assert(format);
326 if (vasprintf(&msg, format, args) == -1) {
327 LOGMEM(ctx);
328 free(path);
329 return;
330 }
Radek Krejcic9e64a62020-09-18 20:08:12 +0200331 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
332 /* assume we are inheriting the error, so inherit vecode as well */
333 vecode = ly_vecode(ctx);
334 }
Michal Vasko004d3152020-06-11 19:59:22 +0200335 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
336 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200337 }
338 free_strs = 0;
339 } else {
340 if (vasprintf(&msg, format, args) == -1) {
341 LOGMEM(ctx);
342 free(path);
343 return;
344 }
345 free_strs = 1;
346 }
347
348 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100349 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200350 if (log_clb) {
351 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200352 } else {
353 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
354 if (path) {
355 fprintf(stderr, "(path: %s)\n", path);
356 }
357 }
358 }
359
360 if (free_strs) {
361 free(path);
362 free(msg);
363 }
364}
365
Radek Krejci4ab61562018-09-05 15:00:37 +0200366#ifndef NDEBUG
367
368void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200369ly_log_dbg(uint32_t group, const char *format, ...)
Radek Krejci4ab61562018-09-05 15:00:37 +0200370{
371 char *dbg_format;
372 const char *str_group;
373 va_list ap;
374
Radek Krejci68433c92020-10-12 17:03:55 +0200375 if (!(ly_ldbg_groups & group)) {
Radek Krejci4ab61562018-09-05 15:00:37 +0200376 return;
377 }
378
379 switch (group) {
380 case LY_LDGDICT:
381 str_group = "DICT";
382 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200383 case LY_LDGXPATH:
384 str_group = "XPATH";
385 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200386 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:
Michal Vaskof6e51882019-12-16 09:59:45 +0100420 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200421 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
422 break;
423 case LY_VLOG_LINE:
Michal Vasko22df3f02020-08-24 13:29:22 +0200424 rc = asprintf(path, "Line number %" PRIu64 ".", *((uint64_t *)elem));
Radek Krejcic04f0a22018-09-21 15:49:45 +0200425 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
426 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100427 case LY_VLOG_LYSC:
428 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
429 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
430 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100431 case LY_VLOG_LYD:
432 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
433 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
434 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200435 default:
436 /* shouldn't be here */
437 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200438 }
439
Radek Krejci94aa9942018-09-07 17:12:17 +0200440 return LY_SUCCESS;
441}
442
443void
444ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
445{
446 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200447 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200448 const struct ly_err_item *first;
449
450 if (path_flag && (elem_type != LY_VLOG_NONE)) {
451 if (elem_type == LY_VLOG_PREV) {
452 /* use previous path */
453 first = ly_err_first(ctx);
454 if (first && first->prev->path) {
455 path = strdup(first->prev->path);
456 }
457 } else {
458 /* print path */
459 if (!elem) {
460 /* top-level */
461 path = strdup("/");
462 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200463 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200464 }
465 }
466 }
467
468 va_start(ap, format);
469 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
470 /* path is spent and should not be freed! */
471 va_end(ap);
472}
473
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200474API void
Radek Krejci0935f412019-08-20 16:15:18 +0200475lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
476{
477 va_list ap;
478 char *plugin_msg;
479 int ret;
480
Radek Krejci52b6d512020-10-12 12:33:17 +0200481 if (ly_ll < level) {
Radek Krejci0935f412019-08-20 16:15:18 +0200482 return;
483 }
484 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
485 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200486 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200487 return;
488 }
489
490 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200491 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 +0200492 va_end(ap);
493
494 free(plugin_msg);
495}
496
Michal Vasko177d0ed2020-11-23 16:43:03 +0100497/**
498 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so va_start() can
499 * be used and an empty va_list created.
500 */
501static void
502_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, ...)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200503{
Michal Vasko177d0ed2020-11-23 16:43:03 +0100504 va_list ap;
505 char *path_dup = NULL;
506
507 LY_CHECK_ARG_RET(ctx, eitem, );
508
509 if (eitem->path) {
510 /* duplicate path because it will be freed */
511 path_dup = strdup(eitem->path);
512 LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200513 }
Michal Vasko177d0ed2020-11-23 16:43:03 +0100514
515 va_start(ap, eitem);
516 log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, eitem->path, eitem->msg, ap);
517 va_end(ap);
518
519 if (path_dup) {
520 eitem->path = path_dup;
521 }
522}
523
524API void
525ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
526{
527 _ly_err_print(ctx, eitem);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200528}