2
* Copyright (c) 2002-2013 BalaBit IT Ltd, Budapest, Hungary
3
* Copyright (c) 1998-2013 Balázs Scheidler
5
* This library is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU Lesser General Public
7
* License as published by the Free Software Foundation; either
8
* version 2.1 of the License, or (at your option) any later version.
10
* This library is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* Lesser General Public License for more details.
15
* You should have received a copy of the GNU Lesser General Public
16
* License along with this library; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
* As an additional exemption you are allowed to compile & link against the
20
* OpenSSL libraries as published by the OpenSSL project. See the file
21
* COPYING for details.
25
#include "template/templates.h"
28
#include "syslog-names.h"
31
#include "filter/filter-expr.h"
34
#include "plugin-types.h"
35
#include "str-format.h"
66
/* only touch this section if you want to add three macros, one w/o
67
* prefix, and a R_ and S_ prefixed macro that relates one of the
68
* timestamps of the log message. */
95
M_TIME_FIRST = M_DATE,
96
M_TIME_LAST = M_UNIXTIME,
97
M_TIME_MACROS_MAX = M_UNIXTIME - M_DATE + 1,
99
M_RECVD_OFS = M_TIME_MACROS_MAX,
100
M_STAMP_OFS = 2 * M_TIME_MACROS_MAX,
101
M_CSTAMP_OFS = 3 * M_TIME_MACROS_MAX,
104
#define M_TIME_MACROS 15
106
LogMacroDef macros[] =
108
{ "FACILITY", M_FACILITY },
109
{ "FACILITY_NUM", M_FACILITY_NUM },
110
{ "PRIORITY", M_LEVEL },
111
{ "LEVEL", M_LEVEL },
112
{ "LEVEL_NUM", M_LEVEL_NUM },
115
{ "BSDTAG", M_BSDTAG },
119
{ "FULLDATE", M_FULLDATE },
120
{ "ISODATE", M_ISODATE },
121
{ "STAMP", M_STAMP },
123
{ "YEAR_DAY", M_YEAR_DAY },
124
{ "MONTH", M_MONTH },
125
{ "MONTH_WEEK", M_MONTH_WEEK },
126
{ "MONTH_ABBREV", M_MONTH_ABBREV },
127
{ "MONTH_NAME", M_MONTH_NAME },
130
{ "HOUR12", M_HOUR12 },
136
{ "WEEKDAY", M_WEEK_DAY_ABBREV }, /* deprecated */
137
{ "WEEK_DAY", M_WEEK_DAY },
138
{ "WEEK_DAY_ABBREV",M_WEEK_DAY_ABBREV },
139
{ "WEEK_DAY_NAME", M_WEEK_DAY_NAME },
141
{ "TZOFFSET", M_TZOFFSET },
143
{ "SYSUPTIME", M_SYSUPTIME },
144
{ "UNIXTIME", M_UNIXTIME },
146
{ "R_DATE", M_RECVD_OFS + M_DATE },
147
{ "R_FULLDATE", M_RECVD_OFS + M_FULLDATE },
148
{ "R_ISODATE", M_RECVD_OFS + M_ISODATE },
149
{ "R_STAMP", M_RECVD_OFS + M_STAMP },
150
{ "R_YEAR", M_RECVD_OFS + M_YEAR },
151
{ "R_YEAR_DAY", M_RECVD_OFS + M_YEAR_DAY },
152
{ "R_MONTH", M_RECVD_OFS + M_MONTH },
153
{ "R_MONTH_WEEK", M_RECVD_OFS + M_MONTH_WEEK },
154
{ "R_MONTH_ABBREV", M_RECVD_OFS + M_MONTH_ABBREV },
155
{ "R_MONTH_NAME", M_RECVD_OFS + M_MONTH_NAME },
156
{ "R_DAY", M_RECVD_OFS + M_DAY },
157
{ "R_HOUR", M_RECVD_OFS + M_HOUR },
158
{ "R_HOUR12", M_RECVD_OFS + M_HOUR12 },
159
{ "R_MIN", M_RECVD_OFS + M_MIN },
160
{ "R_SEC", M_RECVD_OFS + M_SEC },
161
{ "R_MSEC", M_RECVD_OFS + M_MSEC },
162
{ "R_USEC", M_RECVD_OFS + M_USEC },
163
{ "R_AMPM", M_RECVD_OFS + M_AMPM },
164
{ "R_WEEKDAY", M_RECVD_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
165
{ "R_WEEK_DAY", M_RECVD_OFS + M_WEEK_DAY },
166
{ "R_WEEK_DAY_ABBREV",M_RECVD_OFS + M_WEEK_DAY_ABBREV },
167
{ "R_WEEK_DAY_NAME", M_RECVD_OFS + M_WEEK_DAY_NAME },
168
{ "R_WEEK", M_RECVD_OFS + M_WEEK },
169
{ "R_TZOFFSET", M_RECVD_OFS + M_TZOFFSET },
170
{ "R_TZ", M_RECVD_OFS + M_TZ },
171
{ "R_UNIXTIME", M_RECVD_OFS + M_UNIXTIME },
173
{ "S_DATE", M_STAMP_OFS + M_DATE },
174
{ "S_FULLDATE", M_STAMP_OFS + M_FULLDATE },
175
{ "S_ISODATE", M_STAMP_OFS + M_ISODATE },
176
{ "S_STAMP", M_STAMP_OFS + M_STAMP },
177
{ "S_YEAR", M_STAMP_OFS + M_YEAR },
178
{ "S_YEAR_DAY", M_STAMP_OFS + M_YEAR_DAY },
179
{ "S_MONTH", M_STAMP_OFS + M_MONTH },
180
{ "S_MONTH_WEEK", M_STAMP_OFS + M_MONTH_WEEK },
181
{ "S_MONTH_ABBREV", M_STAMP_OFS + M_MONTH_ABBREV },
182
{ "S_MONTH_NAME", M_STAMP_OFS + M_MONTH_NAME },
183
{ "S_DAY", M_STAMP_OFS + M_DAY },
184
{ "S_HOUR", M_STAMP_OFS + M_HOUR },
185
{ "S_HOUR12", M_STAMP_OFS + M_HOUR12 },
186
{ "S_MIN", M_STAMP_OFS + M_MIN },
187
{ "S_SEC", M_STAMP_OFS + M_SEC },
188
{ "S_MSEC", M_STAMP_OFS + M_MSEC },
189
{ "S_USEC", M_STAMP_OFS + M_USEC },
190
{ "S_AMPM", M_STAMP_OFS + M_AMPM },
191
{ "S_WEEKDAY", M_STAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
192
{ "S_WEEK_DAY", M_STAMP_OFS + M_WEEK_DAY },
193
{ "S_WEEK_DAY_ABBREV",M_STAMP_OFS + M_WEEK_DAY_ABBREV },
194
{ "S_WEEK_DAY_NAME", M_STAMP_OFS + M_WEEK_DAY_NAME },
195
{ "S_WEEK", M_STAMP_OFS + M_WEEK },
196
{ "S_TZOFFSET", M_STAMP_OFS + M_TZOFFSET },
197
{ "S_TZ", M_STAMP_OFS + M_TZ },
198
{ "S_UNIXTIME", M_STAMP_OFS + M_UNIXTIME },
200
{ "C_DATE", M_CSTAMP_OFS + M_DATE },
201
{ "C_FULLDATE", M_CSTAMP_OFS + M_FULLDATE },
202
{ "C_ISODATE", M_CSTAMP_OFS + M_ISODATE },
203
{ "C_STAMP", M_CSTAMP_OFS + M_STAMP },
204
{ "C_YEAR", M_CSTAMP_OFS + M_YEAR },
205
{ "C_YEAR_DAY", M_CSTAMP_OFS + M_YEAR_DAY },
206
{ "C_MONTH", M_CSTAMP_OFS + M_MONTH },
207
{ "C_MONTH_WEEK", M_CSTAMP_OFS + M_MONTH_WEEK },
208
{ "C_MONTH_ABBREV", M_CSTAMP_OFS + M_MONTH_ABBREV },
209
{ "C_MONTH_NAME", M_CSTAMP_OFS + M_MONTH_NAME },
210
{ "C_DAY", M_CSTAMP_OFS + M_DAY },
211
{ "C_HOUR", M_CSTAMP_OFS + M_HOUR },
212
{ "C_MIN", M_CSTAMP_OFS + M_MIN },
213
{ "C_SEC", M_CSTAMP_OFS + M_SEC },
214
{ "C_WEEKDAY", M_CSTAMP_OFS + M_WEEK_DAY_ABBREV }, /* deprecated */
215
{ "C_WEEK_DAY", M_CSTAMP_OFS + M_WEEK_DAY },
216
{ "C_WEEK_DAY_ABBREV",M_CSTAMP_OFS + M_WEEK_DAY_ABBREV },
217
{ "C_WEEK_DAY_NAME", M_CSTAMP_OFS + M_WEEK_DAY_NAME },
218
{ "C_WEEK", M_CSTAMP_OFS + M_WEEK },
219
{ "C_TZOFFSET", M_CSTAMP_OFS + M_TZOFFSET },
220
{ "C_TZ", M_CSTAMP_OFS + M_TZ },
221
{ "C_UNIXTIME", M_CSTAMP_OFS + M_UNIXTIME },
223
{ "SDATA", M_SDATA },
224
{ "MSGHDR", M_MSGHDR },
225
{ "SOURCEIP", M_SOURCE_IP },
226
{ "SEQNUM", M_SEQNUM },
227
{ "CONTEXT_ID", M_CONTEXT_ID },
229
/* values that have specific behaviour with older syslog-ng config versions */
230
{ "MSG", M_MESSAGE },
231
{ "MESSAGE", M_MESSAGE },
234
/* message independent macros */
235
{ "LOGHOST", M_LOGHOST },
239
GHashTable *macro_hash;
243
result_append(GString *result, const gchar *sstr, gssize len, gboolean escape)
246
const guchar *ustr = (const guchar *) sstr;
253
for (i = 0; i < len; i++)
255
if (ustr[i] == '\'' || ustr[i] == '"' || ustr[i] == '\\')
257
g_string_append_c(result, '\\');
258
g_string_append_c(result, ustr[i]);
260
else if (ustr[i] < ' ')
262
format_uint32_padded(result, 3, '0', 8, ustr[i]);
265
g_string_append_c(result, ustr[i]);
269
g_string_append_len(result, sstr, len);
273
result_append_value(GString *result, LogMessage *lm, NVHandle handle, gboolean escape)
278
str = log_msg_get_value(lm, handle, &len);
279
result_append(result, str, len, escape);
283
log_macro_expand(GString *result, gint id, gboolean escape, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, LogMessage *msg)
292
n = syslog_name_lookup_name_by_value(msg->pri & LOG_FACMASK, sl_facilities);
295
g_string_append(result, n);
299
format_uint32_padded(result, 0, 0, 16, (msg->pri & LOG_FACMASK) >> 3);
305
format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_FACMASK) >> 3);
313
n = syslog_name_lookup_name_by_value(msg->pri & LOG_PRIMASK, sl_levels);
316
g_string_append(result, n);
320
format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
327
format_uint32_padded(result, 0, 0, 10, msg->pri & LOG_PRIMASK);
332
format_uint32_padded(result, 2, '0', 16, msg->pri);
337
log_msg_print_tags(msg, result);
342
format_uint32_padded(result, 0, 0, 10, (msg->pri & LOG_PRIMASK));
343
g_string_append_c(result, (((msg->pri & LOG_FACMASK) >> 3) + 'A'));
348
format_uint32_padded(result, 0, 0, 10, msg->pri);
353
if (msg->flags & LF_CHAINED_HOSTNAME)
356
const gchar *p1, *p2;
357
int remaining, length;
359
const gchar *host = log_msg_get_value(msg, LM_V_HOST, &host_len);
361
p1 = memchr(host, '@', host_len);
367
remaining = host_len - (p1 - host);
368
p2 = memchr(p1, '/', remaining);
369
length = p2 ? p2 - p1
370
: host_len - (p1 - host);
372
result_append(result, p1, length, escape);
376
result_append_value(result, msg, LM_V_HOST, escape);
383
GString *sdstr = g_string_sized_new(0);
385
log_msg_append_format_sdata(msg, sdstr, seq_num);
386
result_append(result, sdstr->str, sdstr->len, TRUE);
387
g_string_free(sdstr, TRUE);
391
log_msg_append_format_sdata(msg, result, seq_num);
395
if ((msg->flags & LF_LEGACY_MSGHDR))
397
/* fast path for now, as most messages come from legacy devices */
399
result_append_value(result, msg, LM_V_LEGACY_MSGHDR, escape);
403
/* message, complete with program name and pid */
407
result_append_value(result, msg, LM_V_PROGRAM, escape);
408
if (len != result->len)
410
const gchar *pid = log_msg_get_value(msg, LM_V_PID, &len);
413
result_append(result, "[", 1, FALSE);
414
result_append(result, pid, len, escape);
415
result_append(result, "]", 1, FALSE);
417
result_append(result, ": ", 2, FALSE);
422
if (cfg_is_config_version_older(configuration, 0x0300))
423
log_macro_expand(result, M_MSGHDR, escape, opts, tz, seq_num, context_id, msg);
424
result_append_value(result, msg, LM_V_MESSAGE, escape);
430
if (msg->saddr && (g_sockaddr_inet_check(msg->saddr) ||
432
g_sockaddr_inet6_check(msg->saddr))
438
gchar buf[MAX_SOCKADDR_STRING];
440
g_sockaddr_format(msg->saddr, buf, sizeof(buf), GSA_ADDRESS_ONLY);
447
result_append(result, ip, strlen(ip), escape);
454
format_uint32_padded(result, 0, 0, 10, seq_num);
462
result_append(result, context_id, strlen(context_id), escape);
469
const gchar *hname = get_local_hostname(&hname_len);
471
result_append(result, hname, hname_len, escape);
478
g_get_current_time(&ct);
479
format_uint64_padded(result, 0, 0, 10, g_time_val_diff(&ct, &app_uptime) / 1000 / 10);
485
/* year, month, day */
486
struct tm *tm, tm_storage;
490
LogStamp *stamp, sstamp;
494
if (id >= M_TIME_FIRST && id <= M_TIME_LAST)
496
stamp = &msg->timestamps[LM_TS_STAMP];
498
else if (id >= M_TIME_FIRST + M_RECVD_OFS && id <= M_TIME_LAST + M_RECVD_OFS)
501
stamp = &msg->timestamps[LM_TS_RECVD];
503
else if (id >= M_TIME_FIRST + M_STAMP_OFS && id <= M_TIME_LAST + M_STAMP_OFS)
506
stamp = &msg->timestamps[LM_TS_STAMP];
508
else if (id >= M_TIME_FIRST + M_CSTAMP_OFS && id <= M_TIME_LAST + M_CSTAMP_OFS)
513
cached_g_current_time(&tv);
514
sstamp.tv_sec = tv.tv_sec;
515
sstamp.tv_usec = tv.tv_usec;
516
sstamp.zone_offset = -1;
521
g_assert_not_reached();
525
/* try to use the following zone values in order:
526
* destination specific timezone, if one is specified
527
* message specific timezone, if one is specified
530
zone_ofs = (opts->time_zone_info[tz] != NULL ? time_zone_info_get_offset(opts->time_zone_info[tz], stamp->tv_sec) : stamp->zone_offset);
532
zone_ofs = stamp->zone_offset;
534
t = stamp->tv_sec + zone_ofs;
536
cached_gmtime(&t, &tm_storage);
541
case M_WEEK_DAY_ABBREV:
542
g_string_append_len(result, weekday_names_abbrev[tm->tm_wday], 3);
544
case M_WEEK_DAY_NAME:
545
g_string_append(result, weekday_names[tm->tm_wday]);
548
format_uint32_padded(result, 0, 0, 10, tm->tm_wday + 1);
551
format_uint32_padded(result, 2, '0', 10, (tm->tm_yday - (tm->tm_wday - 1 + 7) % 7 + 7) / 7);
554
format_uint32_padded(result, 4, '0', 10, tm->tm_year + 1900);
557
format_uint32_padded(result, 3, '0', 10, tm->tm_yday + 1);
560
format_uint32_padded(result, 2, '0', 10, tm->tm_mon + 1);
563
format_uint32_padded(result, 0, 0, 10, ((tm->tm_mday / 7) + ((tm->tm_wday > 0) && ((tm->tm_mday % 7) >= tm->tm_wday))));
566
g_string_append_len(result, month_names_abbrev[tm->tm_mon], 3);
569
g_string_append(result, month_names[tm->tm_mon]);
572
format_uint32_padded(result, 2, '0', 10, tm->tm_mday);
575
format_uint32_padded(result, 2, '0', 10, tm->tm_hour);
578
if (tm->tm_hour < 12)
579
tmp_hour = tm->tm_hour;
581
tmp_hour = tm->tm_hour - 12;
585
format_uint32_padded(result, 2, '0', 10, tmp_hour);
588
format_uint32_padded(result, 2, '0', 10, tm->tm_min);
591
format_uint32_padded(result, 2, '0', 10, tm->tm_sec);
594
format_uint32_padded(result, 3, '0', 10, stamp->tv_usec/1000);
597
format_uint32_padded(result, 6, '0', 10, stamp->tv_usec);
600
g_string_append(result, tm->tm_hour < 12 ? "AM" : "PM");
608
gint format = id == M_DATE ? TS_FMT_BSD :
609
id == M_ISODATE ? TS_FMT_ISO :
610
id == M_FULLDATE ? TS_FMT_FULL :
611
id == M_UNIXTIME ? TS_FMT_UNIX :
614
log_stamp_append_format(stamp, result, format, zone_ofs, opts->frac_digits);
619
length = format_zone_info(buf, sizeof(buf), zone_ofs);
620
g_string_append_len(result, buf, length);
630
log_macro_lookup(gchar *macro, gint len)
635
g_assert(macro_hash);
636
g_strlcpy(buf, macro, MIN(sizeof(buf), len+1));
637
macro_id = GPOINTER_TO_INT(g_hash_table_lookup(macro_hash, buf));
639
if (cfg_is_config_version_older(configuration, 0x0300) && (macro_id == M_MESSAGE))
641
static gboolean msg_macro_warning = FALSE;
643
if (!msg_macro_warning)
645
msg_warning("WARNING: template: the meaning of the $MSG/$MESSAGE macros has changed from " VERSION_3_0 ", please prepend a $MSGHDR when upgrading to " VERSION_3_0 " config format", NULL);
646
msg_macro_warning = TRUE;
654
LogTemplate *template;
659
} LogTemplateCompiler;
668
typedef struct _LogTemplateElem
672
gchar *default_value;
678
NVHandle value_handle;
681
LogTemplateFunction *ops;
688
/* simple template functions which take templates as arguments */
691
tf_simple_func_prepare(LogTemplateFunction *self, gpointer s, LogTemplate *parent, gint argc, gchar *argv[], GError **error)
693
TFSimpleFuncState *state = (TFSimpleFuncState *) s;
696
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
697
state->argv = g_malloc(sizeof(LogTemplate *) * (argc - 1));
699
/* NOTE: the argv argument con tains the function name as argv[0],
700
* but the LogTemplate array doesn't. Thus the index is shifted by
702
for (i = 0; i < argc - 1; i++)
704
state->argv[i] = log_template_new(parent->cfg, NULL);
705
log_template_set_escape(state->argv[i], parent->escape);
706
if (!log_template_compile(state->argv[i], argv[i + 1], error))
709
state->argc = argc - 1;
716
tf_simple_func_eval(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args)
718
TFSimpleFuncState *state = (TFSimpleFuncState *) s;
721
for (i = 0; i < state->argc; i++)
725
if (args->bufs->len <= i)
726
g_ptr_array_add(args->bufs, g_string_sized_new(256));
728
arg = (GString **) &g_ptr_array_index(args->bufs, i);
729
g_string_truncate(*arg, 0);
731
log_template_append_format_recursive(state->argv[i], args, *arg);
736
tf_simple_func_call(LogTemplateFunction *self, gpointer s, const LogTemplateInvokeArgs *args, GString *result)
738
TFSimpleFunc simple_func = (TFSimpleFunc) self->arg;
739
TFSimpleFuncState *state = (TFSimpleFuncState *) s;
741
simple_func(args->messages[args->num_messages-1], state->argc, (GString **) args->bufs->pdata, result);
745
tf_simple_func_free_state(gpointer s)
747
TFSimpleFuncState *state = (TFSimpleFuncState *) s;
750
for (i = 0; i < state->argc; i++)
753
log_template_unref(state->argv[i]);
759
log_template_add_macro_elem(LogTemplateCompiler *self, guint macro, gchar *default_value)
763
e = g_new0(LogTemplateElem, 1);
765
e->text_len = self->text ? self->text->len : 0;
766
e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
768
e->default_value = default_value;
769
e->msg_ref = self->msg_ref;
770
self->result = g_list_prepend(self->result, e);
774
log_template_add_value_elem(LogTemplateCompiler *self, gchar *value_name, gsize value_name_len, gchar *default_value)
779
e = g_new0(LogTemplateElem, 1);
781
e->text_len = self->text ? self->text->len : 0;
782
e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
783
/* value_name is not NUL terminated */
784
dup = g_strndup(value_name, value_name_len);
785
e->value_handle = log_msg_get_value_handle(dup);
787
e->default_value = default_value;
788
e->msg_ref = self->msg_ref;
789
self->result = g_list_prepend(self->result, e);
793
/* NOTE: this steals argv if successful */
795
log_template_add_func_elem(LogTemplateCompiler *self, gint argc, gchar *argv[], GError **error)
799
gchar *argv_copy[argc + 1];
801
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
806
e = g_malloc0(sizeof(LogTemplateElem) + (argc - 1) * sizeof(LogTemplate *));
808
e->text_len = self->text ? self->text->len : 0;
809
e->text = self->text ? g_strndup(self->text->str, self->text->len) : NULL;
810
e->msg_ref = self->msg_ref;
812
p = plugin_find(self->template->cfg, LL_CONTEXT_TEMPLATE_FUNC, argv[0]);
815
g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE, "Unknown template function %s", argv[0]);
820
e->func.ops = plugin_construct(p, self->template->cfg, LL_CONTEXT_TEMPLATE_FUNC, argv[0]);
823
e->func.state = g_malloc0(e->func.ops->size_of_state);
825
/* prepare may modify the argv array: remove and rearrange elements */
826
memcpy(argv_copy, argv, (argc + 1) * sizeof(argv[0]));
827
if (!e->func.ops->prepare(e->func.ops, e->func.state, self->template, argc, argv_copy, error))
829
e->func.ops->free_state(e->func.state);
830
g_free(e->func.state);
834
self->result = g_list_prepend(self->result, e);
845
log_template_elem_free(LogTemplateElem *e)
852
e->func.ops->free_state(e->func.state);
853
g_free(e->func.state);
857
if (e->default_value)
858
g_free(e->default_value);
865
log_template_elem_free_list(GList *el)
867
for (; el; el = el->next)
869
log_template_elem_free((LogTemplateElem *) el->data);
875
parse_msg_ref(LogTemplateCompiler *self)
878
if ((*self->cursor) == '@')
881
if ((*self->cursor) >= '0' && (*self->cursor) <= '9')
883
/* syntax: ${name}@1 to denote the log message index in the correllation state */
884
while ((*self->cursor) >= '0' && (*self->cursor) <= '9')
886
self->msg_ref += self->msg_ref * 10 + ((*self->cursor) - '0');
893
if ((*self->cursor) != '@')
895
msg_warning("Non-numeric correlation state ID found, assuming a literal '@' character. To avoid confusion when using a literal '@' after a macro or template function, write '@@' in the template.",
896
evt_tag_str("Template", self->template->template),
906
log_template_compiler_fill_compile_error(GError **error, const gchar *error_info, gint error_pos)
908
g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE, "%s, error_pos='%d'", error_info, error_pos);
912
log_template_compiler_append_and_increment(LogTemplateCompiler *self, GString *text)
914
g_string_append_c(text, *self->cursor);
919
log_template_compiler_get_macro_length(gchar *start, gchar *end, gchar **token)
922
gchar *colon = memchr(start, ':', end - start - 1);
925
result = colon - start;
926
*token = colon < end ? colon + 1 : NULL;
930
result = end - start - 1;
938
log_template_compiler_get_default_value(LogTemplateCompiler *self, gchar *token)
945
return g_strndup(token + 1, self->cursor - token - 2);
948
static inline gboolean
949
is_macro_name(gchar c)
951
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '_') || (c >= '0' && c <= '9');
954
#define STEP_BY_TRUE(p, x) while(x) p++;
957
log_template_compiler_add_elem(LogTemplateCompiler *self, gchar *start, gint macro_len, gchar *default_value)
959
gint macro = log_macro_lookup(start, macro_len);
962
log_template_add_value_elem(self, start, macro_len, default_value);
966
log_template_add_macro_elem(self, macro, default_value);
971
log_template_compiler_process_braced_template(LogTemplateCompiler *self, GError **error)
974
gchar *default_value = NULL;
980
start = self->cursor;
981
end = strchr(self->cursor, '}');
985
log_template_compiler_fill_compile_error(error, "Invalid macro, '}' is missing", strlen(self->template->template));
989
self->cursor = end + 1;
990
macro_len = log_template_compiler_get_macro_length(start, self->cursor, &token);
993
default_value = log_template_compiler_get_default_value(self, token);
996
log_template_compiler_fill_compile_error(error, "Unknown substitution function", token - self->template->template);
1000
parse_msg_ref(self);
1001
log_template_compiler_add_elem(self, start, macro_len, default_value);
1006
log_template_compiler_add_quoted_string(LogTemplateCompiler *self, gboolean is_top_level, GString *result)
1008
gchar *quote = self->cursor;
1009
gchar *end_of_quote = strchr(quote + 1, *quote);
1014
self->cursor = end_of_quote + 1;
1017
/* skip the quote in top-level and don't skip in expressions enclosed in parens */
1024
g_string_append_len(result, quote, end_of_quote - quote);
1029
log_template_compiler_process_arg_list(LogTemplateCompiler *self, GPtrArray *result)
1031
GString *arg_buf = g_string_sized_new(32);
1035
while (*self->cursor && *self->cursor == ' ')
1038
while(*self->cursor)
1040
if (*self->cursor == '\\')
1044
else if (*self->cursor == '(')
1048
else if (*self->cursor == ')')
1056
else if (*self->cursor == '"' || *self->cursor == '\'')
1058
if (!log_template_compiler_add_quoted_string(self, parens == 1, arg_buf))
1060
g_ptr_array_add(result, NULL);
1061
g_string_free(arg_buf, TRUE);
1066
else if (parens == 1 && (*self->cursor == ' ' || *self->cursor == '\t'))
1068
g_ptr_array_add(result, g_strndup(arg_buf->str, arg_buf->len));
1069
g_string_truncate(arg_buf, 0);
1070
while (*self->cursor && (*self->cursor == ' ' || *self->cursor == '\t'))
1074
log_template_compiler_append_and_increment(self, arg_buf);
1076
if (arg_buf->len > 0)
1078
g_ptr_array_add(result, g_strndup(arg_buf->str, arg_buf->len));
1080
g_ptr_array_add(result, NULL);
1081
g_string_free(arg_buf, TRUE);
1082
return *self->cursor == ')';
1086
log_template_compiler_process_template_function(LogTemplateCompiler *self, GError **error)
1088
GPtrArray *strv = g_ptr_array_new();
1090
if (!log_template_compiler_process_arg_list(self, strv))
1092
log_template_compiler_fill_compile_error(error, "Invalid template function reference, missing function name or inbalanced '('", self->cursor - self->template->template);
1097
parse_msg_ref(self);
1098
if (!log_template_add_func_elem(self, strv->len - 1, (gchar **) strv->pdata, error))
1102
g_ptr_array_free(strv, FALSE);
1105
g_strfreev((gchar **)strv->pdata);
1106
g_ptr_array_free(strv, FALSE);
1111
log_template_compiler_process_unbraced_template(LogTemplateCompiler *self)
1113
gchar *start = self->cursor;
1119
while (is_macro_name(*self->cursor));
1120
macro_len = self->cursor - start;
1121
log_template_compiler_add_elem(self, start, macro_len, NULL);
1125
log_template_compiler_process_value(LogTemplateCompiler *self, GError **error)
1127
gboolean finished = FALSE;
1131
/* macro reference */
1134
if (!log_template_compiler_process_braced_template(self, error))
1140
/* template function */
1143
if (!log_template_compiler_process_template_function(self, error))
1149
/* unbraced macro */
1150
else if (is_macro_name(p))
1152
log_template_compiler_process_unbraced_template(self);
1155
/* escaped value with dollar */
1160
g_string_append_c(self->text, '$');
1164
log_template_compiler_append_and_increment(self, self->text);
1169
g_string_truncate(self->text, 0);
1175
log_template_compiler_process_token(LogTemplateCompiler *self, GError **error)
1178
if (*self->cursor == '$')
1180
return log_template_compiler_process_value(self, error);
1182
if (*self->cursor == '\\')
1184
if (cfg_is_config_version_older(self->template->cfg, 0x305))
1186
msg_warning("Template escaping changed in version 3.5. Use '$$' to specify a literal dollar sign instead of '\\$' and remove the escaping of the backslash character when you upgrade your configuration",
1187
evt_tag_str("Template", self->template->template),
1195
log_template_compiler_append_and_increment(self, self->text);
1201
log_template_compiler_free_result(LogTemplateCompiler *self)
1203
log_template_elem_free_list(self->result);
1204
self->result = NULL;
1208
log_template_compiler_compile(LogTemplateCompiler *self, GList **compiled_template, GError **error)
1210
gboolean result = FALSE;
1212
while (*self->cursor)
1214
if (!log_template_compiler_process_token(self, error))
1216
log_template_compiler_free_result(self);
1217
g_string_sprintf(self->text, "error in template: %s", self->template->template);
1218
log_template_add_macro_elem(self, M_NONE, NULL);
1222
if (self->text->len)
1224
log_template_add_macro_elem(self, M_NONE, NULL);
1228
*compiled_template = g_list_reverse(self->result);
1229
self->result = NULL;
1234
log_template_compiler_init(LogTemplateCompiler *self, LogTemplate *template)
1236
memset(self, 0, sizeof(*self));
1238
self->template = log_template_ref(template);
1239
self->cursor = self->template->template;
1240
self->text = g_string_sized_new(32);
1244
log_template_compiler_clear(LogTemplateCompiler *self)
1246
log_template_unref(self->template);
1247
g_string_free(self->text, TRUE);
1251
log_template_reset_compiled(LogTemplate *self)
1253
log_template_elem_free_list(self->compiled_template);
1254
self->compiled_template = NULL;
1258
log_template_compile(LogTemplate *self, const gchar *template, GError **error)
1260
LogTemplateCompiler compiler;
1263
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1265
log_template_reset_compiled(self);
1267
g_free(self->template);
1268
self->template = g_strdup(template);
1270
log_template_compiler_init(&compiler, self);
1271
result = log_template_compiler_compile(&compiler, &self->compiled_template, error);
1272
log_template_compiler_clear(&compiler);
1277
log_template_set_escape(LogTemplate *self, gboolean enable)
1279
self->escape = enable;
1283
log_template_set_type_hint(LogTemplate *self, const gchar *type_hint, GError **error)
1285
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1287
return type_hint_parse(type_hint, &self->type_hint, error);
1292
log_template_append_format_with_context(LogTemplate *self, LogMessage **messages, gint num_messages, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
1298
opts = &self->cfg->template_options;
1300
for (p = self->compiled_template; p; p = g_list_next(p))
1304
e = (LogTemplateElem *) p->data;
1307
g_string_append_len(result, e->text, e->text_len);
1310
/* NOTE: msg_ref is 1 larger than the index specified by the user in
1311
* order to make it distinguishable from the zero value. Therefore
1312
* the '>' instead of '>='
1314
* msg_ref == 0 means that the user didn't specify msg_ref
1315
* msg_ref >= 1 means that the user supplied the given msg_ref, 1 is equal to @0 */
1316
if (e->msg_ref > num_messages)
1318
msg_ndx = num_messages - e->msg_ref;
1320
/* value and macro can't understand a context, assume that no msg_ref means @0 */
1321
if (e->msg_ref == 0)
1328
const gchar *value = NULL;
1329
gssize value_len = -1;
1331
value = log_msg_get_value(messages[msg_ndx], e->value_handle, &value_len);
1332
if (value && value[0])
1333
result_append(result, value, value_len, self->escape);
1334
else if (e->default_value)
1335
result_append(result, e->default_value, -1, self->escape);
1340
gint len = result->len;
1344
log_macro_expand(result, e->macro, self->escape, opts ? opts : &self->cfg->template_options, tz, seq_num, context_id, messages[msg_ndx]);
1345
if (len == result->len && e->default_value)
1346
g_string_append(result, e->default_value);
1352
g_static_mutex_lock(&self->arg_lock);
1353
if (!self->arg_bufs)
1354
self->arg_bufs = g_ptr_array_sized_new(0);
1358
LogTemplateInvokeArgs args =
1361
e->msg_ref ? &messages[msg_ndx] : messages,
1362
e->msg_ref ? 1 : num_messages,
1370
/* if a function call is called with an msg_ref, we only
1371
* pass that given logmsg to argument resolution, otherwise
1372
* we pass the whole set so the arguments can individually
1373
* specify which message they want to resolve from
1375
if (e->func.ops->eval)
1376
e->func.ops->eval(e->func.ops, e->func.state, &args);
1377
e->func.ops->call(e->func.ops, e->func.state, &args, result);
1379
g_static_mutex_unlock(&self->arg_lock);
1387
log_template_append_format_recursive(LogTemplate *self, const LogTemplateInvokeArgs *args, GString *result)
1389
log_template_append_format_with_context(self,
1390
args->messages, args->num_messages,
1391
args->opts, args->tz, args->seq_num, args->context_id, result);
1395
log_template_format_with_context(LogTemplate *self, LogMessage **messages, gint num_messages, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
1397
g_string_truncate(result, 0);
1398
log_template_append_format_with_context(self, messages, num_messages, opts, tz, seq_num, context_id, result);
1402
log_template_append_format(LogTemplate *self, LogMessage *lm, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
1404
log_template_append_format_with_context(self, &lm, 1, opts, tz, seq_num, context_id, result);
1408
log_template_format(LogTemplate *self, LogMessage *lm, const LogTemplateOptions *opts, gint tz, gint32 seq_num, const gchar *context_id, GString *result)
1410
g_string_truncate(result, 0);
1411
log_template_append_format(self, lm, opts, tz, seq_num, context_id, result);
1415
log_template_new(GlobalConfig *cfg, gchar *name)
1417
LogTemplate *self = g_new0(LogTemplate, 1);
1419
self->name = g_strdup(name);
1422
g_static_mutex_init(&self->arg_lock);
1423
if (cfg_is_config_version_older(cfg, 0x0300))
1425
static gboolean warn_written = FALSE;
1429
msg_warning("WARNING: template: the default value for template-escape has changed to 'no' from " VERSION_3_0 ", please update your configuration file accordingly",
1431
warn_written = TRUE;
1433
self->escape = TRUE;
1439
log_template_free(LogTemplate *self)
1445
for (i = 0; i < self->arg_bufs->len; i++)
1446
g_string_free(g_ptr_array_index(self->arg_bufs, i), TRUE);
1447
g_ptr_array_free(self->arg_bufs, TRUE);
1449
log_template_reset_compiled(self);
1451
g_free(self->template);
1452
g_static_mutex_free(&self->arg_lock);
1457
log_template_ref(LogTemplate *s)
1461
g_assert(s->ref_cnt > 0);
1468
log_template_unref(LogTemplate *s)
1472
g_assert(s->ref_cnt > 0);
1473
if (--s->ref_cnt == 0)
1474
log_template_free(s);
1478
/* NOTE: _init needs to be idempotent when called multiple times w/o invoking _destroy */
1480
log_template_options_init(LogTemplateOptions *options, GlobalConfig *cfg)
1484
if (options->initialized)
1486
if (options->ts_format == -1)
1487
options->ts_format = cfg->template_options.ts_format;
1488
for (i = 0; i < LTZ_MAX; i++)
1490
if (options->time_zone[i] == NULL)
1491
options->time_zone[i] = g_strdup(cfg->template_options.time_zone[i]);
1492
if (options->time_zone_info[i] == NULL)
1493
options->time_zone_info[i] = time_zone_info_new(options->time_zone[i]);
1496
if (options->frac_digits == -1)
1497
options->frac_digits = cfg->template_options.frac_digits;
1498
if (options->on_error == -1)
1499
options->on_error = cfg->template_options.on_error;
1500
options->initialized = TRUE;
1504
log_template_options_destroy(LogTemplateOptions *options)
1508
for (i = 0; i < LTZ_MAX; i++)
1510
if (options->time_zone[i])
1511
g_free(options->time_zone[i]);
1512
if (options->time_zone_info[i])
1513
time_zone_info_free(options->time_zone_info[i]);
1515
options->initialized = FALSE;
1519
log_template_options_defaults(LogTemplateOptions *options)
1521
memset(options, 0, sizeof(LogTemplateOptions));
1522
options->frac_digits = -1;
1523
options->ts_format = -1;
1524
options->on_error = -1;
1528
log_template_error_quark()
1530
return g_quark_from_static_string("log-template-error-quark");
1534
log_template_global_init(void)
1538
/* init the uptime (SYSUPTIME macro) */
1539
g_get_current_time(&app_uptime);
1541
macro_hash = g_hash_table_new(g_str_hash, g_str_equal);
1542
for (i = 0; macros[i].name; i++)
1544
g_hash_table_insert(macro_hash, macros[i].name,
1545
GINT_TO_POINTER(macros[i].id));
1551
log_template_global_deinit(void)
1553
g_hash_table_destroy(macro_hash);
1558
log_template_on_error_parse(const gchar *strictness, gint *out)
1560
const gchar *p = strictness;
1561
gboolean silently = FALSE;
1565
*out = ON_ERROR_DROP_MESSAGE;
1569
if (strncmp(strictness, "silently-", strlen("silently-")) == 0)
1572
p = strictness + strlen("silently-");
1575
if (strcmp(p, "drop-message") == 0)
1576
*out = ON_ERROR_DROP_MESSAGE;
1577
else if (strcmp(p, "drop-property") == 0)
1578
*out = ON_ERROR_DROP_PROPERTY;
1579
else if (strcmp(p, "fallback-to-string") == 0)
1580
*out = ON_ERROR_FALLBACK_TO_STRING;
1585
*out |= ON_ERROR_SILENT;
1591
log_template_options_set_on_error(LogTemplateOptions *options, gint on_error)
1593
options->on_error = on_error;