blob: 15595cd271a07af81eb894cd79a96228c82222f4 [file] [log] [blame]
Willy Tarreau67b5a162019-08-11 16:38:56 +02001/*
2 * Event sink management
3 *
4 * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation, version 2.1
9 * exclusively.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
Willy Tarreau67b5a162019-08-11 16:38:56 +020021#include <common/compat.h>
22#include <common/config.h>
23#include <common/ist.h>
24#include <common/mini-clist.h>
Willy Tarreau53ba9d92019-09-26 08:03:58 +020025#include <common/time.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020026#include <proto/cli.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020027#include <proto/log.h>
Willy Tarreau4ed23ca2019-08-23 15:47:49 +020028#include <proto/ring.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020029#include <proto/sink.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020030#include <proto/stream_interface.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020031
32struct list sink_list = LIST_HEAD_INIT(sink_list);
33
34struct sink *sink_find(const char *name)
35{
36 struct sink *sink;
37
38 list_for_each_entry(sink, &sink_list, sink_list)
39 if (strcmp(sink->name, name) == 0)
40 return sink;
41 return NULL;
42}
43
44/* creates a new sink and adds it to the list, it's still generic and not fully
45 * initialized. Returns NULL on allocation failure. If another one already
46 * exists with the same name, it will be returned. The caller can detect it as
47 * a newly created one has type SINK_TYPE_NEW.
48 */
Willy Tarreau973e6622019-08-20 11:57:52 +020049static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
Willy Tarreau67b5a162019-08-11 16:38:56 +020050{
51 struct sink *sink;
52
53 sink = sink_find(name);
54 if (sink)
55 goto end;
56
57 sink = malloc(sizeof(*sink));
58 if (!sink)
59 goto end;
60
61 sink->name = name;
62 sink->desc = desc;
63 sink->fmt = fmt;
64 sink->type = SINK_TYPE_NEW;
Christopher Fauleta63a5c22019-11-15 15:10:12 +010065 sink->maxlen = BUFSIZE;
Willy Tarreau67b5a162019-08-11 16:38:56 +020066 /* address will be filled by the caller if needed */
Willy Tarreau973e6622019-08-20 11:57:52 +020067 sink->ctx.fd = -1;
Willy Tarreau67b5a162019-08-11 16:38:56 +020068 sink->ctx.dropped = 0;
69 HA_RWLOCK_INIT(&sink->ctx.lock);
70 LIST_ADDQ(&sink_list, &sink->sink_list);
71 end:
72 return sink;
73}
74
Willy Tarreau973e6622019-08-20 11:57:52 +020075/* creates a sink called <name> of type FD associated to fd <fd>, format <fmt>,
76 * and description <desc>. Returns NULL on allocation failure or conflict.
77 * Perfect duplicates are merged (same type, fd, and name).
78 */
79struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
80{
81 struct sink *sink;
82
83 sink = __sink_new(name, desc, fmt);
84 if (!sink || (sink->type == SINK_TYPE_FD && sink->ctx.fd == fd))
85 goto end;
86
87 if (sink->type != SINK_TYPE_NEW) {
88 sink = NULL;
89 goto end;
90 }
91
92 sink->type = SINK_TYPE_FD;
93 sink->ctx.fd = fd;
94 end:
95 return sink;
96}
97
Willy Tarreau4ed23ca2019-08-23 15:47:49 +020098/* creates a sink called <name> of type BUF of size <size>, format <fmt>,
99 * and description <desc>. Returns NULL on allocation failure or conflict.
100 * Perfect duplicates are merged (same type and name). If sizes differ, the
101 * largest one is kept.
102 */
103struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, size_t size)
104{
105 struct sink *sink;
106
107 sink = __sink_new(name, desc, fmt);
108 if (!sink)
109 goto fail;
110
111 if (sink->type == SINK_TYPE_BUFFER) {
112 /* such a buffer already exists, we may have to resize it */
113 if (!ring_resize(sink->ctx.ring, size))
114 goto fail;
115 goto end;
116 }
117
118 if (sink->type != SINK_TYPE_NEW) {
119 /* already exists of another type */
120 goto fail;
121 }
122
123 sink->ctx.ring = ring_new(size);
124 if (!sink->ctx.ring) {
125 LIST_DEL(&sink->sink_list);
126 free(sink);
127 goto fail;
128 }
129
130 sink->type = SINK_TYPE_BUFFER;
131 end:
132 return sink;
133 fail:
134 return NULL;
135}
136
Willy Tarreau67b5a162019-08-11 16:38:56 +0200137/* tries to send <nmsg> message parts (up to 8, ignored above) from message
138 * array <msg> to sink <sink>. Formating according to the sink's preference is
Willy Tarreau8f240232019-08-27 16:41:06 +0200139 * done here. Lost messages are NOT accounted for. It is preferable to call
140 * sink_write() instead which will also try to emit the number of dropped
141 * messages when there are any. It returns >0 if it could write anything,
142 * <=0 otherwise.
Willy Tarreau67b5a162019-08-11 16:38:56 +0200143 */
Emeric Brunbd163812020-05-06 14:33:46 +0200144ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
145 int level, int facility, struct ist *tag,
146 struct ist *pid, struct ist *sd)
Willy Tarreau67b5a162019-08-11 16:38:56 +0200147{
Emeric Brunbd163812020-05-06 14:33:46 +0200148 int log_format;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200149 char short_hdr[4];
Emeric Brunbd163812020-05-06 14:33:46 +0200150 struct ist pfx[6];
Willy Tarreaua1426de2019-08-27 14:21:02 +0200151 size_t npfx = 0;
Emeric Brunbd163812020-05-06 14:33:46 +0200152 char *hdr_ptr;
153 int fac_level;
154
155 if (sink->fmt == SINK_FMT_RAW)
156 goto send;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200157
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200158 if (sink->fmt == SINK_FMT_SHORT || sink->fmt == SINK_FMT_TIMED) {
Willy Tarreau67b5a162019-08-11 16:38:56 +0200159 short_hdr[0] = '<';
Emeric Brunbd163812020-05-06 14:33:46 +0200160 short_hdr[1] = '0' + level;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200161 short_hdr[2] = '>';
162
Willy Tarreaua1426de2019-08-27 14:21:02 +0200163 pfx[npfx].ptr = short_hdr;
164 pfx[npfx].len = 3;
165 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200166 if (sink->fmt == SINK_FMT_SHORT)
167 goto send;
Willy Tarreaua1426de2019-08-27 14:21:02 +0200168 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200169
Emeric Brunbd163812020-05-06 14:33:46 +0200170
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200171 if (sink->fmt == SINK_FMT_ISO || sink->fmt == SINK_FMT_TIMED) {
172 pfx[npfx].ptr = timeofday_as_iso_us(1);
173 pfx[npfx].len = 27;
174 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200175 goto send;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200176 }
Emeric Brunbd163812020-05-06 14:33:46 +0200177 else if (sink->fmt == SINK_FMT_RFC5424) {
178 pfx[npfx].ptr = logheader_rfc5424;
179 pfx[npfx].len = update_log_hdr_rfc5424(date.tv_sec) - pfx[npfx].ptr;
180 log_format = LOG_FORMAT_RFC5424;
181 }
182 else {
183 pfx[npfx].ptr = logheader;
184 pfx[npfx].len = update_log_hdr(date.tv_sec) - pfx[npfx].ptr;
185 log_format = LOG_FORMAT_RFC3164;
186 sd = NULL;
187 }
188
189 fac_level = (facility << 3) + level;
190 hdr_ptr = pfx[npfx].ptr + 3; /* last digit of the log level */
191 do {
192 *hdr_ptr = '0' + fac_level % 10;
193 fac_level /= 10;
194 hdr_ptr--;
195 } while (fac_level && hdr_ptr > pfx[npfx].ptr);
196 *hdr_ptr = '<';
197 pfx[npfx].len -= hdr_ptr - pfx[npfx].ptr;
198 pfx[npfx].ptr = hdr_ptr;
199 npfx++;
200
201 if (tag && tag->len) {
202 pfx[npfx].ptr = tag->ptr;
203 pfx[npfx].len = tag->len;
204 npfx++;
205 }
206 pfx[npfx].ptr = get_format_pid_sep1(log_format, &pfx[npfx].len);
207 if (pfx[npfx].len)
208 npfx++;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200209
Emeric Brunbd163812020-05-06 14:33:46 +0200210 if (pid && pid->len) {
211 pfx[npfx].ptr = pid->ptr;
212 pfx[npfx].len = pid->len;
213 npfx++;
214 }
215
216 pfx[npfx].ptr = get_format_pid_sep2(log_format, &pfx[npfx].len);
217 if (pfx[npfx].len)
218 npfx++;
219
220 if (sd && sd->len) {
221 pfx[npfx].ptr = sd->ptr;
222 pfx[npfx].len = sd->len;
223 npfx++;
224 }
225
226send:
Willy Tarreau973e6622019-08-20 11:57:52 +0200227 if (sink->type == SINK_TYPE_FD) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200228 return fd_write_frag_line(sink->ctx.fd, sink->maxlen, pfx, npfx, msg, nmsg, 1);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200229 }
230 else if (sink->type == SINK_TYPE_BUFFER) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200231 return ring_write(sink->ctx.ring, sink->maxlen, pfx, npfx, msg, nmsg);
Willy Tarreau973e6622019-08-20 11:57:52 +0200232 }
Willy Tarreau8f240232019-08-27 16:41:06 +0200233 return 0;
234}
Willy Tarreau67b5a162019-08-11 16:38:56 +0200235
Willy Tarreau8f240232019-08-27 16:41:06 +0200236/* Tries to emit a message indicating the number of dropped events. In case of
237 * success, the amount of drops is reduced by as much. It's supposed to be
238 * called under an exclusive lock on the sink to avoid multiple produces doing
239 * the same. On success, >0 is returned, otherwise <=0 on failure.
240 */
Emeric Brunbd163812020-05-06 14:33:46 +0200241int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid)
Willy Tarreau8f240232019-08-27 16:41:06 +0200242{
243 unsigned int dropped;
244 struct buffer msg;
245 struct ist msgvec[1];
246 char logbuf[64];
Emeric Brunbd163812020-05-06 14:33:46 +0200247 struct ist sd;
248 struct ist tag;
Willy Tarreau8f240232019-08-27 16:41:06 +0200249
250 while (unlikely((dropped = sink->ctx.dropped) > 0)) {
251 chunk_init(&msg, logbuf, sizeof(logbuf));
252 chunk_printf(&msg, "%u event%s dropped", dropped, dropped > 1 ? "s" : "");
253 msgvec[0] = ist2(msg.area, msg.data);
Emeric Brunbd163812020-05-06 14:33:46 +0200254
255 sd.ptr = default_rfc5424_sd_log_format;
256 sd.len = 2;
257 tag.ptr = global.log_tag.area;
258 tag.len = global.log_tag.data;
259 if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, &tag, pid, &sd) <= 0)
Willy Tarreau8f240232019-08-27 16:41:06 +0200260 return 0;
261 /* success! */
262 HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
263 }
264 return 1;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200265}
266
Willy Tarreau9f830d72019-08-26 18:17:04 +0200267/* parse the "show events" command, returns 1 if a message is returned, otherwise zero */
268static int cli_parse_show_events(char **args, char *payload, struct appctx *appctx, void *private)
269{
270 struct sink *sink;
Willy Tarreau1d181e42019-08-30 11:17:01 +0200271 int arg;
Willy Tarreau9f830d72019-08-26 18:17:04 +0200272
273 args++; // make args[1] the 1st arg
274
275 if (!*args[1]) {
276 /* no arg => report the list of supported sink */
Willy Tarreau1d181e42019-08-30 11:17:01 +0200277 chunk_printf(&trash, "Supported events sinks are listed below. Add -w(wait), -n(new). Any key to stop\n");
Willy Tarreau9f830d72019-08-26 18:17:04 +0200278 list_for_each_entry(sink, &sink_list, sink_list) {
279 chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
280 sink->name,
281 sink->type == SINK_TYPE_NEW ? "init" :
282 sink->type == SINK_TYPE_FD ? "fd" :
283 sink->type == SINK_TYPE_BUFFER ? "buffer" : "?",
284 sink->ctx.dropped, sink->desc);
285 }
286
287 trash.area[trash.data] = 0;
288 return cli_msg(appctx, LOG_WARNING, trash.area);
289 }
290
291 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
292 return 1;
293
294 sink = sink_find(args[1]);
295 if (!sink)
296 return cli_err(appctx, "No such event sink");
297
298 if (sink->type != SINK_TYPE_BUFFER)
299 return cli_msg(appctx, LOG_NOTICE, "Nothing to report for this sink");
300
Willy Tarreau1d181e42019-08-30 11:17:01 +0200301 for (arg = 2; *args[arg]; arg++) {
302 if (strcmp(args[arg], "-w") == 0)
303 appctx->ctx.cli.i0 |= 1; // wait mode
304 else if (strcmp(args[arg], "-n") == 0)
305 appctx->ctx.cli.i0 |= 2; // seek to new
306 else if (strcmp(args[arg], "-nw") == 0 || strcmp(args[arg], "-wn") == 0)
307 appctx->ctx.cli.i0 |= 3; // seek to new + wait
308 else
309 return cli_err(appctx, "unknown option");
310 }
Willy Tarreau9f830d72019-08-26 18:17:04 +0200311 return ring_attach_cli(sink->ctx.ring, appctx);
312}
313
Willy Tarreau973e6622019-08-20 11:57:52 +0200314static void sink_init()
315{
316 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
317 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
Willy Tarreauf8340e32019-09-26 08:05:15 +0200318 sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_TIMED, 1048576);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200319}
320
321static void sink_deinit()
322{
323 struct sink *sink, *sb;
324
325 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
326 if (sink->type == SINK_TYPE_BUFFER)
327 ring_free(sink->ctx.ring);
328 LIST_DEL(&sink->sink_list);
329 free(sink);
330 }
Willy Tarreau973e6622019-08-20 11:57:52 +0200331}
332
333INITCALL0(STG_REGISTER, sink_init);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200334REGISTER_POST_DEINIT(sink_deinit);
Willy Tarreau973e6622019-08-20 11:57:52 +0200335
Willy Tarreau9f830d72019-08-26 18:17:04 +0200336static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaufcf94982019-11-15 15:07:21 +0100337 { { "show", "events", NULL }, "show events [<sink>] : show event sink state", cli_parse_show_events, NULL, NULL },
Willy Tarreau9f830d72019-08-26 18:17:04 +0200338 {{},}
339}};
340
341INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
342
Willy Tarreau67b5a162019-08-11 16:38:56 +0200343/*
344 * Local variables:
345 * c-indent-level: 8
346 * c-basic-offset: 8
347 * End:
348 */