summaryrefslogtreecommitdiffstats
path: root/lib/zlog.h
blob: 27401d51fb13e83034f81299f52c054188ccbd41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// SPDX-License-Identifier: ISC
/*
 * Copyright (c) 2015-19  David Lamparter, for NetDEF, Inc.
 */

#ifndef _FRR_ZLOG_H
#define _FRR_ZLOG_H

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/uio.h>

#include <assert.h>

#include "atomlist.h"
#include "frrcu.h"
#include "memory.h"
#include "hook.h"
#include "printfrr.h"

#ifdef __cplusplus
extern "C" {
#endif

DECLARE_MGROUP(LOG);

extern char zlog_prefix[];
extern size_t zlog_prefixsz;
extern int zlog_tmpdirfd;
extern int zlog_instance;
extern const char *zlog_progname;

struct xref_logmsg {
	struct xref xref;

	const char *fmtstring;
	uint32_t priority;
	uint32_t ec;
	const char *args;
};

/* whether flag was added in config mode or enable mode */
#define LOGMSG_FLAG_EPHEMERAL	(1 << 0)
#define LOGMSG_FLAG_PERSISTENT	(1 << 1)

struct xrefdata_logmsg {
	struct xrefdata xrefdata;

	uint8_t fl_print_bt;
};

/* These functions are set up to write to stdout/stderr without explicit
 * initialization and/or before config load.  There is no need to call e.g.
 * fprintf(stderr, ...) just because it's "too early" at startup.  Depending
 * on context, it may still be the right thing to use fprintf though -- try to
 * determine whether something is a log message or something else.
 */

extern void vzlogx(const struct xref_logmsg *xref, int prio, const char *fmt,
		   va_list ap) PRINTFRR(3, 0);
#define vzlog(prio, ...) vzlogx(NULL, prio, __VA_ARGS__)

PRINTFRR(2, 3)
static inline void zlog(int prio, const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vzlog(prio, fmt, ap);
	va_end(ap);
}

PRINTFRR(2, 3)
static inline void zlog_ref(const struct xref_logmsg *xref,
			    const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vzlogx(xref, xref->priority, fmt, ap);
	va_end(ap);
}

#define _zlog_ecref(ec_, prio, msg, ...)                                       \
	do {                                                                   \
		static struct xrefdata_logmsg _xrefdata = {                    \
			.xrefdata =                                            \
				{                                              \
					.xref = NULL,                          \
					.uid = {},                             \
					.hashstr = (msg),                      \
					.hashu32 = {(prio), (ec_)},            \
				},                                             \
		};                                                             \
		static const struct xref_logmsg _xref __attribute__(           \
			(used)) = {                                            \
			.xref = XREF_INIT(XREFT_LOGMSG, &_xrefdata.xrefdata,   \
					  __func__),                           \
			.fmtstring = (msg),                                    \
			.priority = (prio),                                    \
			.ec = (ec_),                                           \
			.args = (#__VA_ARGS__),                                \
		};                                                             \
		XREF_LINK(_xref.xref);                                         \
		zlog_ref(&_xref, (msg), ##__VA_ARGS__);                        \
	} while (0)

#define zlog_err(...)    _zlog_ecref(0, LOG_ERR, __VA_ARGS__)
#define zlog_warn(...)   _zlog_ecref(0, LOG_WARNING, __VA_ARGS__)
#define zlog_info(...)   _zlog_ecref(0, LOG_INFO, __VA_ARGS__)
#define zlog_notice(...) _zlog_ecref(0, LOG_NOTICE, __VA_ARGS__)
#define zlog_debug(...)  _zlog_ecref(0, LOG_DEBUG, __VA_ARGS__)

#define flog_err(ferr_id, format, ...)                                         \
	_zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__)
#define flog_warn(ferr_id, format, ...)                                        \
	_zlog_ecref(ferr_id, LOG_WARNING, format, ## __VA_ARGS__)

#define flog_err_sys(ferr_id, format, ...)                                     \
	_zlog_ecref(ferr_id, LOG_ERR, format, ## __VA_ARGS__)

extern void zlog_sigsafe(const char *text, size_t len);

/* extra priority value to disable a target without deleting it */
#define ZLOG_DISABLED	(LOG_EMERG-1)

/* zlog_msg encapsulates a particular logging call from somewhere in the code.
 * The same struct is passed around to all zlog_targets.
 *
 * This is used to defer formatting the log message until it is actually
 * requested by one of the targets.  If none of the targets needs the message
 * formatted, the formatting call is avoided entirely.
 *
 * This struct is opaque / private to the core zlog code.  Logging targets
 * should use zlog_msg_* functions to get text / timestamps / ... for a
 * message.
 */

struct zlog_msg;

extern int zlog_msg_prio(struct zlog_msg *msg);
extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg);

/* text is NOT \0 terminated; instead there is a \n after textlen since the
 * logging targets would jump extra hoops otherwise for a single byte.  (the
 * \n is not included in textlen)
 *
 * calling this with NULL textlen is likely wrong.
 * use  "%.*s", (int)textlen, text  when passing to printf-like functions
 */
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);

extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen,
			  size_t *n_argpos, const struct fmt_outpos **argpos);

/* timestamp formatting control flags */

/* sub-second digit count */
#define ZLOG_TS_PREC		0xfU

/* 8601:   0000-00-00T00:00:00Z      (if used with ZLOG_TS_UTC)
 *         0000-00-00T00:00:00+00:00 (otherwise)
 * Legacy: 0000/00/00 00:00:00       (no TZ indicated!)
 */
#define ZLOG_TS_ISO8601		(1 << 8)
#define ZLOG_TS_LEGACY		(1 << 9)

/* default is local time zone */
#define ZLOG_TS_UTC		(1 << 10)

struct timespec;

extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out,
			  uint32_t flags);
extern void zlog_msg_tsraw(struct zlog_msg *msg, struct timespec *ts);

/* "mmm dd hh:mm:ss" for RFC3164 syslog.  Only ZLOG_TS_UTC for flags. */
extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out,
			       uint32_t flags);

/* currently just returns the current PID/TID since we never write another
 * thread's messages
 */
extern void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid);

/* This list & struct implements the actual logging targets.  It is accessed
 * lock-free from all threads, and thus MUST only be changed atomically, i.e.
 * RCU.
 *
 * Since there's no atomic replace, the replacement action is an add followed
 * by a delete.  This means that during logging config changes, log messages
 * may be duplicated in the log target that is being changed.  The old entry
 * being changed MUST also at the very least not crash or do other stupid
 * things.
 *
 * This list and struct are NOT related to config.  Logging config is kept
 * separately, and results in creating appropriate zlog_target(s) to realize
 * the config.  Log targets may also be created from varying sources, e.g.
 * command line options, or VTY commands ("log monitor").
 *
 * struct zlog_target is intended to be embedded into a larger structure that
 * contains additional field for the specific logging target, e.g. an fd or
 * additional options.  It MUST be the first field in that larger struct.
 */

PREDECL_ATOMLIST(zlog_targets);
struct zlog_target {
	struct zlog_targets_item head;

	int prio_min;

	void (*logfn)(struct zlog_target *zt, struct zlog_msg *msg[],
		      size_t nmsgs);

	/* for crash handlers, set to NULL if log target can't write crash logs
	 * without possibly deadlocking (AS-Safe)
	 *
	 * text is not \0 terminated & split up into lines (e.g. no \n)
	 */
	void (*logfn_sigsafe)(struct zlog_target *zt, const char *text,
			      size_t len);

	struct rcu_head rcu_head;
};

/* make a copy for RCUpdating.  oldzt may be NULL to allocate a fresh one. */
extern struct zlog_target *zlog_target_clone(struct memtype *mt,
					     struct zlog_target *oldzt,
					     size_t size);

/* update the zlog_targets list;  both oldzt and newzt may be NULL.  You
 * still need to zlog_target_free() the old target afterwards if it wasn't
 * NULL.
 *
 * Returns oldzt so you can zlog_target_free(zlog_target_replace(old, new));
 * (Some log targets may need extra cleanup inbetween, but remember the old
 * target MUST remain functional until the end of the current RCU cycle.)
 */
extern struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
					       struct zlog_target *newzt);

/* Mostly for symmetry for zlog_target_clone(), just rcu_free() internally. */
#define zlog_target_free(mt, zt) \
	rcu_free(mt, zt, rcu_head)

extern void zlog_init(const char *progname, const char *protoname,
		      unsigned short instance, uid_t uid, gid_t gid);
DECLARE_HOOK(zlog_init, (const char *progname, const char *protoname,
			 unsigned short instance, uid_t uid, gid_t gid),
			(progname, protoname, instance, uid, gid));

extern void zlog_fini(void);
DECLARE_KOOH(zlog_fini, (), ());

extern void zlog_set_prefix_ec(bool enable);
extern bool zlog_get_prefix_ec(void);
extern void zlog_set_prefix_xid(bool enable);
extern bool zlog_get_prefix_xid(void);

/* for tools & test programs, i.e. anything not a daemon.
 * (no cleanup needed at exit)
 */
extern void zlog_aux_init(const char *prefix, int prio_min);
DECLARE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
			    (prefix, prio_min));

extern void zlog_startup_end(void);

extern void zlog_tls_buffer_init(void);
extern void zlog_tls_buffer_flush(void);
extern void zlog_tls_buffer_fini(void);

/* Enable or disable 'immediate' output - default is to buffer messages. */
extern void zlog_set_immediate(bool set_p);
bool zlog_get_immediate_mode(void);

extern const char *zlog_priority_str(int priority);

#ifdef __cplusplus
}
#endif

#endif /* _FRR_ZLOG_H */