[syslog-ng] [PATCH 2/4] value-pairs: Type hinting support

Gergely Nagy algernon at balabit.hu
Fri Feb 15 16:26:03 CET 2013


This implements very basic support for type hinting template strings,
where those hints will be available to value-pairs too, allowing any
user of either LogTemplate or value-pairs to handle different types of
values than plain strings. How they handle it, is up to the callers,
this patch just lays the groundwork that makes it possible to pass
these hints around.

Furthermore, this includes support for the on-error() statement, which
tells drivers how type-casting failures shall be handled: they can
either drop the message, drop the property, or fall back to string;
and can do either of these either verbosely, logging the fact, or
silently.

This fixes #4 and #5.

Signed-off-by: Gergely Nagy <algernon at balabit.hu>
---
 lib/Makefile.am               |    2 +
 lib/cfg-grammar.y             |   86 ++++++++++++++--
 lib/cfg-parser.c              |    4 +
 lib/cfg.c                     |    2 +
 lib/cfg.h                     |    2 +
 lib/driver.c                  |    7 ++
 lib/driver.h                  |    2 +
 lib/scratch-buffers.c         |   48 +++++++++
 lib/scratch-buffers.h         |   17 ++++
 lib/templates.c               |    1 +
 lib/templates.h               |    3 +
 lib/type-hinting.c            |  226 +++++++++++++++++++++++++++++++++++++++++
 lib/type-hinting.h            |   73 +++++++++++++
 lib/value-pairs.c             |  153 +++++++++++++++++++++-------
 lib/value-pairs.h             |   36 ++++---
 modules/afamqp/afamqp.c       |    3 +-
 modules/afmongodb/afmongodb.c |    2 +-
 modules/json/format-json.c    |    3 +-
 tests/unit/Makefile.am        |    1 +
 tests/unit/test_type_hints.c  |  191 ++++++++++++++++++++++++++++++++++
 tests/unit/test_value_pairs.c |    8 +-
 21 files changed, 801 insertions(+), 69 deletions(-)
 create mode 100644 lib/type-hinting.c
 create mode 100644 lib/type-hinting.h
 create mode 100644 tests/unit/test_type_hints.c

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 8780394..d4b50cc 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -83,6 +83,7 @@ pkginclude_HEADERS = 		\
 	tls-support.h		\
 	tlscontext.h  		\
 	tlstransport.h		\
+	type-hinting.h		\
 	utils.h			\
 	uuid.h			\
 	value-pairs.h		\
@@ -161,6 +162,7 @@ libsyslog_ng_la_SOURCES = \
 	tags.c			\
 	templates.c		\
 	timeutils.c		\
+	type-hinting.c		\
 	utils.c			\
 	value-pairs.c		\
 	vptransform.c		\
diff --git a/lib/cfg-grammar.y b/lib/cfg-grammar.y
index f45d486..c48b4cb 100644
--- a/lib/cfg-grammar.y
+++ b/lib/cfg-grammar.y
@@ -32,6 +32,7 @@
 /* YYSTYPE and YYLTYPE is defined by the lexer */
 #include "cfg-lexer.h"
 #include "afinter.h"
+#include "type-hinting.h"
 #include "filter-expr-parser.h"
 #include "parser-expr-parser.h"
 #include "rewrite-expr-parser.h"
@@ -49,6 +50,7 @@ extern struct _FilePermOptions *last_file_perm_options;
 extern struct _MsgFormatOptions *last_msg_format_options;
 extern struct _LogDriver *last_driver;
 extern struct _LogParser *last_parser;
+LogTemplate *last_template;
 
 }
 
@@ -303,6 +305,9 @@ extern struct _LogParser *last_parser;
 %token KW_ADD_PREFIX                  10508
 %token KW_REPLACE                     10509
 
+%token KW_TYPE_CAST                   10510
+%token KW_ON_ERROR                    10511
+
 /* END_DECLS */
 
 %code {
@@ -341,7 +346,6 @@ LogReaderOptions *last_reader_options;
 LogWriterOptions *last_writer_options;
 MsgFormatOptions *last_msg_format_options;
 FilePermOptions *last_file_perm_options;
-LogTemplate *last_template;
 CfgArgs *last_block_args;
 ValuePairs *last_value_pairs;
 ValuePairsTransformSet *last_vp_transset;
@@ -370,6 +374,8 @@ ValuePairsTransformSet *last_vp_transset;
 %type	<ptr> dest_item
 %type   <ptr> dest_plugin
 
+%type   <ptr> template_content
+
 %type   <ptr> filter_content
 
 %type   <ptr> parser_content
@@ -396,6 +402,7 @@ ValuePairsTransformSet *last_vp_transset;
  /* START_DECLS */
 
 %type   <ptr> value_pair_option
+%type   <num> on_error_stmt
 
 %type	<num> yesno
 %type   <num> dnsmode
@@ -709,13 +716,37 @@ template_items
 	|
 	;
 
-template_item
-	: KW_TEMPLATE '(' string ')'		{
-                                                  GError *error = NULL;
+/* START_RULES */
 
-                                                  CHECK_ERROR(log_template_compile(last_template, $3, &error), @3, "Error compiling template (%s)", error->message);
-                                                  free($3);
-                                                }
+template_content
+        : string
+        {
+          GError *error = NULL;
+
+          CHECK_ERROR(log_template_compile(last_template, $1, &error), @1, "Error compiling template (%s)", error->message);
+          free($1);
+        }
+        | LL_IDENTIFIER '(' string ')'
+        {
+          GError *error = NULL;
+          TypeHint type;
+
+          if (!last_template)
+            last_template = log_template_new(configuration, NULL);
+
+          CHECK_ERROR(log_template_compile(last_template, $3, &error), @3, "Error compiling template (%s)", error->message);
+          free($3);
+
+          CHECK_ERROR(type_hint_parse($1, &type, &error), @1, "Error setting the template type-hint (%s)", error->message);
+          last_template->type_hint = type;
+          free($1);
+        }
+        ;
+
+/* END_RULES */
+
+template_item
+	: KW_TEMPLATE '(' template_content ')'
 	| KW_TEMPLATE_ESCAPE '(' yesno ')'	{ log_template_set_escape(last_template, $3); }
 	;
 
@@ -762,6 +793,24 @@ block_arg
           }
         ;
 
+/* START_RULES */
+
+on_error_stmt
+	: KW_ON_ERROR '(' string ')'
+        {
+          GError *error = NULL;
+          gint on_error;
+
+          CHECK_ERROR(type_cast_strictness_parse($3, &on_error, &error),
+                      @3, "Invalid strictness");
+          free($3);
+
+          $$ = on_error;
+        }
+	;
+
+/* END_RULES */
+
 options_items
 	: options_item ';' options_items	{ $$ = $1; }
 	|					{ $$ = NULL; }
@@ -822,6 +871,7 @@ options_item
 	| KW_RECV_TIME_ZONE '(' string ')'      { configuration->recv_time_zone = g_strdup($3); free($3); }
 	| KW_SEND_TIME_ZONE '(' string ')'      { configuration->template_options.time_zone[LTZ_SEND] = g_strdup($3); free($3); }
 	| KW_LOCAL_TIME_ZONE '(' string ')'     { configuration->template_options.time_zone[LTZ_LOCAL] = g_strdup($3); free($3); }
+	| KW_TYPE_CAST '(' on_error_stmt ')'    { configuration->type_cast_strictness = $3; }
 	;
 
 /* START_RULES */
@@ -1065,8 +1115,26 @@ vp_options
 	;
 
 vp_option
-        : KW_PAIR '(' string ':' string ')'      { value_pairs_add_pair(last_value_pairs, configuration, $3, $5); free($3); free($5); }
-        | KW_PAIR '(' string string ')'          { value_pairs_add_pair(last_value_pairs, configuration, $3, $4); free($3); free($4); }
+        : KW_PAIR '(' string ':' template_content ')'
+        {
+          GError *error = NULL;
+
+          CHECK_ERROR(value_pairs_add_pair(last_value_pairs, configuration, $3,
+                                           last_template->type_hint,
+                                           last_template->template, &error),
+                      @5, "Error processing value-pair (%s)", error->message);
+          free($3);
+        }
+        | KW_PAIR '(' string template_content ')'
+        {
+          GError *error = NULL;
+
+          CHECK_ERROR(value_pairs_add_pair(last_value_pairs, configuration, $3,
+                                           last_template->type_hint,
+                                           last_template->template, &error),
+                      @4, "Error processing value-pair (%s)", error->message);
+          free($3);
+        }
         | KW_KEY '(' string KW_REKEY '('
         {
           last_vp_transset = value_pairs_transform_set_new($3);
diff --git a/lib/cfg-parser.c b/lib/cfg-parser.c
index 222a8cf..538dd2d 100644
--- a/lib/cfg-parser.c
+++ b/lib/cfg-parser.c
@@ -67,6 +67,10 @@ static CfgLexerKeyword main_keywords[] = {
   { "add_prefix",         KW_ADD_PREFIX, 0x0303 },
   { "replace",            KW_REPLACE, 0x0303 },
 
+  /* type hints */
+  { "typecast",           KW_TYPE_CAST, 0x0304 },
+  { "on_error",           KW_ON_ERROR, 0x0304 },
+
   /* option items */
   { "flags",              KW_FLAGS },
   { "pad_size",           KW_PAD_SIZE },
diff --git a/lib/cfg.c b/lib/cfg.c
index 610c448..c305c04 100644
--- a/lib/cfg.c
+++ b/lib/cfg.c
@@ -325,6 +325,8 @@ cfg_new(gint version)
   self->recv_time_zone = NULL;
   self->keep_timestamp = TRUE;
 
+  self->type_cast_strictness = TYPE_CAST_DROP_MESSAGE;
+
   cfg_tree_init_instance(&self->tree, self);
   return self;
 }
diff --git a/lib/cfg.h b/lib/cfg.h
index 86b9a9a..db70ff9 100644
--- a/lib/cfg.h
+++ b/lib/cfg.h
@@ -31,6 +31,7 @@
 #include "cfg-parser.h"
 #include "persist-state.h"
 #include "templates.h"
+#include "type-hinting.h"
 
 #include <sys/types.h>
 #include <regex.h>
@@ -84,6 +85,7 @@ struct _GlobalConfig
   gint time_reopen;
   gint time_reap;
   gint suppress;
+  gint type_cast_strictness;
 
   gint log_fifo_size;
   gint log_msg_size;
diff --git a/lib/driver.c b/lib/driver.c
index d77fe57..0e2b4e4 100644
--- a/lib/driver.c
+++ b/lib/driver.c
@@ -282,6 +282,8 @@ log_dest_driver_deinit_method(LogPipe *s)
 void
 log_dest_driver_init_instance(LogDestDriver *self)
 {
+  GlobalConfig *config;
+
   log_driver_init_instance(&self->super);
   self->super.super.init = log_dest_driver_init_method;
   self->super.super.deinit = log_dest_driver_deinit_method;
@@ -290,6 +292,11 @@ log_dest_driver_init_instance(LogDestDriver *self)
   self->release_queue = log_dest_driver_release_queue_method;
   self->log_fifo_size = -1;
   self->throttle = 0;
+
+  config = log_pipe_get_config((LogPipe *)self);
+  if (!config)
+    config = configuration;
+  self->type_cast_strictness = config->type_cast_strictness;
 }
 
 void
diff --git a/lib/driver.h b/lib/driver.h
index 4e0bf47..c0ad669 100644
--- a/lib/driver.h
+++ b/lib/driver.h
@@ -29,6 +29,7 @@
 #include "logpipe.h"
 #include "logqueue.h"
 #include "cfg.h"
+#include "type-hinting.h"
 
 /*
  * Drivers overview
@@ -161,6 +162,7 @@ struct _LogDestDriver
   gint log_fifo_size;
   gint throttle;
   StatsCounterItem *queued_global_messages;
+  gint type_cast_strictness;
 };
 
 /* returns a reference */
diff --git a/lib/scratch-buffers.c b/lib/scratch-buffers.c
index 481ce73..7c21e04 100644
--- a/lib/scratch-buffers.c
+++ b/lib/scratch-buffers.c
@@ -29,6 +29,7 @@
 TLS_BLOCK_START
 {
   GTrashStack *sb_gstrings;
+  GTrashStack *sb_th_gstrings;
   GList *sb_registry;
 }
 TLS_BLOCK_END;
@@ -80,6 +81,52 @@ ScratchBufferStack SBGStringStack = {
   .free_stack = sb_gstring_free_stack
 };
 
+/* Type-hinted GStrings */
+
+#define local_sb_th_gstrings        __tls_deref(sb_th_gstrings)
+
+GTrashStack *
+sb_th_gstring_acquire_buffer (void)
+{
+  SBTHGString *sb;
+
+  sb = g_trash_stack_pop(&local_sb_th_gstrings);
+  if (!sb)
+    {
+      sb = g_new(SBTHGString, 1);
+      g_string_steal(sb_th_gstring_string(sb));
+      sb->type_hint = TYPE_HINT_STRING;
+    }
+  else
+    g_string_set_size(sb_th_gstring_string(sb), 0);
+
+  return (GTrashStack *)sb;
+}
+
+void
+sb_th_gstring_release_buffer(GTrashStack *s)
+{
+  g_trash_stack_push(&local_sb_th_gstrings, s);
+}
+
+void
+sb_th_gstring_free_stack(void)
+{
+  SBGString *sb;
+
+  while ((sb = g_trash_stack_pop(&local_sb_th_gstrings)) != NULL)
+    {
+      g_free(sb_gstring_string(sb)->str);
+      g_free(sb);
+    }
+}
+
+ScratchBufferStack SBTHGStringStack = {
+  .acquire_buffer = sb_th_gstring_acquire_buffer,
+  .release_buffer = sb_th_gstring_release_buffer,
+  .free_stack = sb_th_gstring_free_stack
+};
+
 /* Global API */
 
 #define local_sb_registry  __tls_deref(sb_registry)
@@ -95,6 +142,7 @@ scratch_buffers_init(void)
 {
   local_sb_registry = NULL;
   scratch_buffers_register(&SBGStringStack);
+  scratch_buffers_register(&SBTHGStringStack);
 }
 
 static void
diff --git a/lib/scratch-buffers.h b/lib/scratch-buffers.h
index 72ab40a..66c701f 100644
--- a/lib/scratch-buffers.h
+++ b/lib/scratch-buffers.h
@@ -26,6 +26,7 @@
 #define SCRATCH_BUFFERS_H_INCLUDED 1
 
 #include <glib.h>
+#include "type-hinting.h"
 
 /* Global API */
 
@@ -67,4 +68,20 @@ extern ScratchBufferStack SBGStringStack;
 
 #define sb_gstring_string(buffer) (&buffer->s)
 
+/* Type-hinted GStrings */
+
+typedef struct
+{
+  GTrashStack stackp;
+  GString s;
+  TypeHint type_hint;
+} SBTHGString;
+
+extern ScratchBufferStack SBTHGStringStack;
+
+#define sb_th_gstring_acquire() ((SBTHGString *)scratch_buffer_acquire(&SBTHGStringStack))
+#define sb_th_gstring_release(b) (scratch_buffer_release(&SBTHGStringStack, (GTrashStack *)b))
+
+#define sb_th_gstring_string(buffer) (&buffer->s)
+
 #endif
diff --git a/lib/templates.c b/lib/templates.c
index f62f8b8..fb312e9 100644
--- a/lib/templates.c
+++ b/lib/templates.c
@@ -1273,6 +1273,7 @@ log_template_new(GlobalConfig *cfg, gchar *name)
   LogTemplate *self = g_new0(LogTemplate, 1);
   
   self->name = g_strdup(name);
+  self->type_cast_strictness = TYPE_CAST_DROP_MESSAGE;
   self->ref_cnt = 1;
   self->cfg = cfg;
   g_static_mutex_init(&self->arg_lock);
diff --git a/lib/templates.h b/lib/templates.h
index ed5b688..69f005b 100644
--- a/lib/templates.h
+++ b/lib/templates.h
@@ -27,6 +27,7 @@
 
 #include "syslog-ng.h"
 #include "timeutils.h"
+#include "type-hinting.h"
 
 #define LTZ_LOCAL 0
 #define LTZ_SEND  1
@@ -54,6 +55,8 @@ typedef struct _LogTemplate
   GlobalConfig *cfg;
   GStaticMutex arg_lock;
   GPtrArray *arg_bufs;
+  TypeHint type_hint;
+  gint type_cast_strictness;
 } LogTemplate;
 
 /* template expansion options that can be influenced by the user and
diff --git a/lib/type-hinting.c b/lib/type-hinting.c
new file mode 100644
index 0000000..7be91a1
--- /dev/null
+++ b/lib/type-hinting.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2012-2013 BalaBit IT Ltd, Budapest, Hungary
+ * Copyright (c) 2012-2013 Gergely Nagy <algernon at balabit.hu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As an additional exemption you are allowed to compile & link against the
+ * OpenSSL libraries as published by the OpenSSL project. See the file
+ * COPYING for details.
+ *
+ */
+
+#include "messages.h"
+#include "type-hinting.h"
+#include "templates.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+GQuark
+type_hinting_error_quark()
+{
+  return g_quark_from_static_string("type-hinting-error-quark");
+}
+
+gboolean
+type_hint_parse(const gchar *hint, TypeHint *out_type, GError **error)
+{
+  if (hint == NULL)
+    {
+      *out_type = TYPE_HINT_STRING;
+      return TRUE;
+    }
+
+  if (strcmp(hint, "string") == 0)
+    *out_type = TYPE_HINT_STRING;
+  else if (strcmp(hint, "literal") == 0)
+    *out_type = TYPE_HINT_LITERAL;
+  else if (strcmp(hint, "int32") == 0 || strcmp(hint, "int") == 0)
+    *out_type = TYPE_HINT_INT32;
+  else if (strcmp(hint, "int64") == 0)
+    *out_type = TYPE_HINT_INT64;
+  else if (strcmp(hint, "datetime") == 0)
+    *out_type = TYPE_HINT_DATETIME;
+  else if (strcmp(hint, "boolean") == 0)
+    *out_type = TYPE_HINT_BOOLEAN;
+  else if (strcmp(hint, "default") == 0)
+    *out_type = TYPE_HINT_DEFAULT;
+  else
+    {
+      g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_TYPE,
+                  "%s", hint);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+type_cast_strictness_parse(const gchar *strictness, gint *out, GError **error)
+{
+  const gchar *p = strictness;
+  gboolean silently = FALSE;
+
+  if (!strictness)
+    {
+      *out = TYPE_CAST_DROP_MESSAGE;
+      return TRUE;
+    }
+
+  if (strncmp(strictness, "silently-", strlen("silently-")) == 0)
+    {
+      silently = TRUE;
+      p = strictness + strlen("silently-");
+    }
+
+  if (strcmp(p, "drop-message") == 0)
+    *out = TYPE_CAST_DROP_MESSAGE;
+  else if (strcmp(p, "drop-property") == 0)
+    *out = TYPE_CAST_DROP_PROPERTY;
+  else if (strcmp(p, "fallback-to-string") == 0)
+    *out = TYPE_CAST_FALLBACK_TO_STRING;
+  else
+    {
+      g_set_error(error, TYPE_HINTING_ERROR, TYPE_CAST_INVALID_STRICTNESS,
+                  "%s",strictness);
+      return FALSE;
+    }
+
+  if (silently)
+    *out |= TYPE_CAST_SILENTLY;
+
+  return TRUE;
+}
+
+gboolean
+type_cast_drop_helper(gint drop_flags, const gchar *value,
+                      const gchar *type_hint)
+{
+  if (!(drop_flags & TYPE_CAST_SILENTLY))
+    {
+      msg_error("Casting error",
+                evt_tag_str("value", value),
+                evt_tag_str("type-hint", type_hint),
+                NULL);
+    }
+  return drop_flags & TYPE_CAST_DROP_MESSAGE;
+}
+
+gboolean
+type_cast_to_boolean(const gchar *value, gboolean *out, GError **error)
+{
+  if (value[0] == 'T' || value[0] == 't' || value[0] == '1')
+    *out = TRUE;
+  else if (value[0] == 'F' || value[0] == 'f' || value[0] == '0')
+    *out = FALSE;
+  else
+    {
+      if (error)
+        g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                    "boolean(%s)", value);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+gboolean
+type_cast_to_int32(const gchar *value, gint32 *out, GError **error)
+{
+  gchar *endptr;
+
+  *out = (gint32)strtol(value, &endptr, 10);
+
+  if (endptr[0] != '\0')
+    {
+      if (error)
+        g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                    "int32(%s)", value);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+gboolean
+type_cast_to_int64(const gchar *value, gint64 *out, GError **error)
+{
+  gchar *endptr;
+
+  *out = (gint64)strtoll(value, &endptr, 10);
+
+  if (endptr[0] != '\0')
+    {
+      if (error)
+        g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                    "int64(%s)", value);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+gboolean
+type_cast_to_datetime_int(const gchar *value, guint64 *out, GError **error)
+{
+  gchar *endptr;
+
+  *out = (gint64)strtoll(value, &endptr, 10) * 1000;
+
+  if (endptr[0] == '.')
+    {
+      gsize len = strlen(endptr) - 1, p;
+      gchar *e, tmp[4];
+      glong i;
+
+      if (len > 3)
+        len = 3;
+
+      memcpy(tmp, endptr + 1, len);
+      tmp[len] = '\0';
+
+      i = strtoll(tmp, &e, 10);
+
+      if (e[0] != '\0')
+        {
+          if (error)
+            g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                        "datetime(%s)", value);
+          return FALSE;
+        }
+
+      for (p = 3 - len; p > 0; p--)
+        i *= 10;
+
+      *out += i;
+    }
+  else if (endptr[0] != '\0')
+    {
+      if (error)
+        g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                    "datetime(%s)", value);
+      return FALSE;
+    }
+  return TRUE;
+}
+
+gboolean
+type_cast_to_datetime_str(const gchar *value, const char *format,
+                          gchar **out, GError **error)
+{
+  if (error)
+    g_set_error(error, TYPE_HINTING_ERROR, TYPE_HINTING_INVALID_CAST,
+                "datetime_str is not supported yet");
+  return FALSE;
+}
diff --git a/lib/type-hinting.h b/lib/type-hinting.h
new file mode 100644
index 0000000..94ec757
--- /dev/null
+++ b/lib/type-hinting.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012-2013 BalaBit IT Ltd, Budapest, Hungary
+ * Copyright (c) 2012-2013 Gergely Nagy <algernon at balabit.hu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * As an additional exemption you are allowed to compile & link against the
+ * OpenSSL libraries as published by the OpenSSL project. See the file
+ * COPYING for details.
+ *
+ */
+
+#ifndef TYPE_HINTING_H
+#define TYPE_HINTING_H
+
+#include "syslog-ng.h"
+
+#define TYPE_HINTING_ERROR type_hinting_error_quark()
+
+GQuark type_hinting_error_quark(void);
+
+enum TypeHintingError
+{
+  TYPE_HINTING_INVALID_TYPE,
+  TYPE_HINTING_INVALID_CAST,
+  TYPE_CAST_INVALID_STRICTNESS,
+};
+
+typedef enum
+{
+  TYPE_HINT_STRING,
+  TYPE_HINT_LITERAL,
+  TYPE_HINT_BOOLEAN,
+  TYPE_HINT_INT32,
+  TYPE_HINT_INT64,
+  TYPE_HINT_DATETIME,
+  TYPE_HINT_DEFAULT,
+} TypeHint;
+
+typedef enum
+{
+  TYPE_CAST_DROP_MESSAGE        = 0x01,
+  TYPE_CAST_DROP_PROPERTY       = 0x02,
+  TYPE_CAST_FALLBACK_TO_STRING  = 0x04,
+  TYPE_CAST_SILENTLY            = 0x08
+} TypeCastStrictness;
+
+gboolean type_cast_strictness_parse(const gchar *strictness, gint *out, GError **error);
+gboolean type_hint_parse(const gchar *hint, TypeHint *out_hint, GError **error);
+
+gboolean type_cast_drop_helper(gint drop_flags, const gchar *value,
+                               const gchar *type_hint);
+
+gboolean type_cast_to_boolean(const gchar *value, gboolean *out, GError **error);
+gboolean type_cast_to_int32(const gchar *value, gint32 *out, GError **error);
+gboolean type_cast_to_int64(const gchar *value, gint64 *out, GError **error);
+gboolean type_cast_to_datetime_int(const gchar *value, guint64 *out, GError **error);
+gboolean type_cast_to_datetime_str(const gchar *value, const char *format,
+                                   gchar **out, GError **error);
+
+#endif
diff --git a/lib/value-pairs.c b/lib/value-pairs.c
index e78fc58..dafd301 100644
--- a/lib/value-pairs.c
+++ b/lib/value-pairs.c
@@ -26,6 +26,7 @@
 #include "vptransform.h"
 #include "logmsg.h"
 #include "templates.h"
+#include "type-hinting.h"
 #include "cfg-parser.h"
 #include "misc.h"
 #include "scratch-buffers.h"
@@ -151,16 +152,24 @@ value_pairs_add_glob_pattern(ValuePairs *vp, const gchar *pattern,
   vp->patterns[i] = p;
 }
 
-void
-value_pairs_add_pair(ValuePairs *vp, GlobalConfig *cfg, const gchar *key, const gchar *value)
+gboolean
+value_pairs_add_pair(ValuePairs *vp, GlobalConfig *cfg,
+                     const gchar *key, TypeHint type,
+                     const gchar *value, GError **error)
 {
   VPPairConf *p = g_new(VPPairConf, 1);
 
   p->name = g_strdup(key);
   p->template = log_template_new(cfg, NULL);
-  log_template_compile(p->template, value, NULL);
+  log_template_compile(p->template, value, error);
+  if (error && *error)
+    return FALSE;
+  p->template->type_hint = type;
+  if (error && *error)
+    return FALSE;
 
   g_ptr_array_add(vp->vpairs, p);
+  return TRUE;
 }
 
 static gchar *
@@ -193,22 +202,20 @@ vp_pairs_foreach(gpointer data, gpointer user_data)
   LogMessage *msg = ((gpointer *)user_data)[2];
   gint32 seq_num = GPOINTER_TO_INT (((gpointer *)user_data)[3]);
   GTree *scope_set = ((gpointer *)user_data)[5];
-  SBGString *sb = sb_gstring_acquire();
+  SBTHGString *sb = sb_th_gstring_acquire();
   VPPairConf *vpc = (VPPairConf *)data;
 
-  log_template_format((LogTemplate *)vpc->template, msg, NULL, LTZ_LOCAL,
-                      seq_num, NULL, sb_gstring_string(sb));
+  sb->type_hint = vpc->template->type_hint;
+  log_template_append_format((LogTemplate *)vpc->template, msg, NULL, LTZ_LOCAL,
+                             seq_num, NULL, sb_th_gstring_string(sb));
 
-  if (!sb_gstring_string(sb)->str[0])
+  if (sb_th_gstring_string(sb)->len == 0)
     {
-      sb_gstring_release(sb);
+      sb_th_gstring_release(sb);
       return;
     }
 
-  g_tree_insert(scope_set, vp_transform_apply(vp, vpc->name),
-                sb_gstring_string(sb)->str);
-  g_string_steal(sb_gstring_string(sb));
-  sb_gstring_release(sb);
+  g_tree_insert(scope_set, vp_transform_apply(vp, vpc->name), sb);
 }
 
 /* runs over the LogMessage nv-pairs, and inserts them unless excluded */
@@ -234,8 +241,11 @@ vp_msg_nvpairs_foreach(NVHandle handle, gchar *name,
        (log_msg_is_handle_sdata(handle) && (vp->scopes & VPS_SDATA))) ||
       inc)
     {
-      /* NOTE: the key is a borrowed reference in the hash, and value is freed */
-      g_tree_insert(scope_set, vp_transform_apply(vp, name), g_strndup(value, value_len));
+      SBTHGString *sb = sb_th_gstring_acquire();
+
+      g_string_append_len(sb_th_gstring_string(sb), value, value_len);
+      sb->type_hint = TYPE_HINT_STRING;
+      g_tree_insert(scope_set, vp_transform_apply(vp, name), sb);
     }
 
   return FALSE;
@@ -246,7 +256,7 @@ static void
 vp_merge_set(ValuePairs *vp, LogMessage *msg, gint32 seq_num, ValuePairSpec *set, GTree *dest)
 {
   gint i;
-  SBGString *sb = sb_gstring_acquire();
+  SBTHGString *sb;
 
   for (i = 0; set[i].name; i++)
     {
@@ -262,10 +272,12 @@ vp_merge_set(ValuePairs *vp, LogMessage *msg, gint32 seq_num, ValuePairSpec *set
       if (exclude)
 	continue;
 
+      sb = sb_th_gstring_acquire();
+
       switch (set[i].type)
         {
         case VPT_MACRO:
-          log_macro_expand(sb_gstring_string(sb), set[i].id, FALSE,
+          log_macro_expand(sb_th_gstring_string(sb), set[i].id, FALSE,
                            NULL, LTZ_LOCAL, seq_num, NULL, msg);
           break;
         case VPT_NVPAIR:
@@ -274,34 +286,55 @@ vp_merge_set(ValuePairs *vp, LogMessage *msg, gint32 seq_num, ValuePairSpec *set
             gssize len;
 
             nv = log_msg_get_value(msg, (NVHandle) set[i].id, &len);
-            g_string_append_len(sb_gstring_string(sb), nv, len);
+            g_string_append_len(sb_th_gstring_string(sb), nv, len);
             break;
           }
         default:
           g_assert_not_reached();
         }
 
-      if (!sb_gstring_string(sb)->str[0])
-	continue;
+      if (sb_th_gstring_string(sb)->len == 0)
+        {
+          sb_th_gstring_release(sb);
+          continue;
+        }
 
-      g_tree_insert(dest, vp_transform_apply(vp, set[i].name),
-                    sb_gstring_string(sb)->str);
-      g_string_steal(sb_gstring_string(sb));
+      g_tree_insert(dest, vp_transform_apply(vp, set[i].name), sb);
     }
-  sb_gstring_release(sb);
 }
 
-void
+static gboolean
+vp_foreach_helper (const gchar *name, const SBTHGString *hinted_value,
+                   gpointer data)
+{
+  VPForeachFunc func = ((gpointer *)data)[0];
+  gpointer user_data = ((gpointer *)data)[1];
+  gboolean *r = ((gpointer *)data)[2];
+
+  *r &= !func(name, hinted_value->type_hint,
+              sb_th_gstring_string(hinted_value)->str, user_data);
+  return !*r;
+}
+
+static void
+vp_data_free (SBTHGString *s)
+{
+  sb_th_gstring_release (s);
+}
+
+gboolean
 value_pairs_foreach_sorted (ValuePairs *vp, VPForeachFunc func,
                             GCompareDataFunc compare_func,
                             LogMessage *msg, gint32 seq_num, gpointer user_data)
 {
   gpointer args[] = { vp, func, msg, GINT_TO_POINTER (seq_num), user_data, NULL };
+  gboolean result = TRUE;
+  gpointer helper_args[] = { func, user_data, &result };
   GTree *scope_set;
 
   scope_set = g_tree_new_full((GCompareDataFunc)compare_func, NULL,
                               (GDestroyNotify)g_free,
-                              (GDestroyNotify)g_free);
+                              (GDestroyNotify)vp_data_free);
   args[5] = scope_set;
 
   /*
@@ -328,18 +361,20 @@ value_pairs_foreach_sorted (ValuePairs *vp, VPForeachFunc func,
   g_ptr_array_foreach(vp->vpairs, (GFunc)vp_pairs_foreach, args);
 
   /* Aaand we run it through the callback! */
-  g_tree_foreach(scope_set, (GTraverseFunc)func, user_data);
+  g_tree_foreach(scope_set, (GTraverseFunc)vp_foreach_helper, helper_args);
 
   g_tree_destroy(scope_set);
+
+  return result;
 }
 
-void
+gboolean
 value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
                     LogMessage *msg, gint32 seq_num,
                     gpointer user_data)
 {
-  value_pairs_foreach_sorted(vp, func, (GCompareDataFunc) strcmp,
-                             msg, seq_num, user_data);
+  return value_pairs_foreach_sorted(vp, func, (GCompareDataFunc) strcmp,
+                                    msg, seq_num, user_data);
 }
 
 typedef struct
@@ -483,7 +518,7 @@ vp_walker_name_split(vp_walk_stack_t **stack, vp_walk_state_t *state,
 }
 
 static gboolean
-value_pairs_walker(const gchar *name, const gchar *value,
+value_pairs_walker(const gchar *name, TypeHint type, const gchar *value,
                    gpointer user_data)
 {
   vp_walk_state_t *state = (vp_walk_state_t *)user_data;
@@ -496,10 +531,14 @@ value_pairs_walker(const gchar *name, const gchar *value,
   key = vp_walker_name_split (&st, state, name);
 
   if (st)
-    result = state->process_value(key, st->prefix, value, &st->data,
+    result = state->process_value(key, st->prefix,
+                                  type, value,
+                                  &st->data,
                                   state->user_data);
   else
-    result = state->process_value(key, NULL, value, NULL,
+    result = state->process_value(key, NULL,
+                                  type, value,
+                                  NULL,
                                   state->user_data);
 
   g_free(key);
@@ -516,7 +555,7 @@ vp_walk_cmp(const gchar *s1, const gchar *s2)
   return strcmp(s2, s1);
 }
 
-void
+gboolean
 value_pairs_walk(ValuePairs *vp,
                  VPWalkCallbackFunc obj_start_func,
                  VPWalkValueCallbackFunc process_value_func,
@@ -525,6 +564,7 @@ value_pairs_walk(ValuePairs *vp,
                  gpointer user_data)
 {
   vp_walk_state_t state;
+  gboolean result;
 
   state.user_data = user_data;
   state.obj_start = obj_start_func;
@@ -533,10 +573,12 @@ value_pairs_walk(ValuePairs *vp,
   state.stack = NULL;
 
   state.obj_start(NULL, NULL, NULL, NULL, NULL, user_data);
-  value_pairs_foreach_sorted(vp, value_pairs_walker,
-                             (GCompareDataFunc)vp_walk_cmp, msg, seq_num, &state);
+  result = value_pairs_foreach_sorted(vp, value_pairs_walker,
+                                      (GCompareDataFunc)vp_walk_cmp, msg, seq_num, &state);
   vp_walker_stack_unwind_all(&state.stack, &state);
   state.obj_end(NULL, NULL, NULL, NULL, NULL, user_data);
+
+  return result;
 }
 
 static void
@@ -722,6 +764,32 @@ vp_cmdline_parse_rekey(const gchar *option_name, const gchar *value,
   return TRUE;
 }
 
+static void
+value_pairs_parse_type(gchar *spec, gchar **value, gchar **type)
+{
+  char *sp, *ep;
+
+  *type = NULL;
+
+  sp = strchr(spec, '(');
+  if (sp == NULL)
+    {
+      *value = spec;
+      return;
+    }
+  ep = strchr(sp, ')');
+  if (ep == NULL || ep[1] != '\0')
+    {
+      *value = spec;
+      return;
+    }
+
+  *value = sp + 1;
+  *type = spec;
+  sp[0] = '\0';
+  ep[0] = '\0';
+}
+
 static gboolean
 vp_cmdline_parse_pair (const gchar *option_name, const gchar *value,
 		       gpointer data, GError **error)
@@ -729,7 +797,8 @@ vp_cmdline_parse_pair (const gchar *option_name, const gchar *value,
   gpointer *args = (gpointer *) data;
   ValuePairs *vp = (ValuePairs *) args[1];
   GlobalConfig *cfg = (GlobalConfig *) args[0];
-  gchar **kv;
+  gchar **kv, *v, *t;
+  TypeHint type;
 
   vp_cmdline_parse_rekey_finish (data);
   if (!g_strstr_len (value, strlen (value), "="))
@@ -740,7 +809,17 @@ vp_cmdline_parse_pair (const gchar *option_name, const gchar *value,
     }
 
   kv = g_strsplit(value, "=", 2);
-  value_pairs_add_pair (vp, cfg, kv[0], kv[1]);
+  value_pairs_parse_type(kv[1], &v, &t);
+
+  if (type_hint_parse(t, &type, error) != TRUE)
+    {
+      g_free(kv[0]);
+      g_free(kv[1]);
+      g_free(kv);
+      return FALSE;
+    }
+
+  value_pairs_add_pair(vp, cfg, kv[0], type, v, NULL);
 
   g_free (kv[0]);
   g_free (kv[1]);
diff --git a/lib/value-pairs.h b/lib/value-pairs.h
index a6c9f34..75888c9 100644
--- a/lib/value-pairs.h
+++ b/lib/value-pairs.h
@@ -27,12 +27,14 @@
 
 #include "syslog-ng.h"
 #include "nvtable.h"
+#include "type-hinting.h"
 
 typedef struct _ValuePairs ValuePairs;
-typedef gboolean (*VPForeachFunc)(const gchar *name, const gchar *value, gpointer user_data);
+typedef gboolean (*VPForeachFunc)(const gchar *name, TypeHint type,
+                                  const gchar *value, gpointer user_data);
 
 typedef gboolean (*VPWalkValueCallbackFunc)(const gchar *name, const gchar *prefix,
-                                            const gchar *value,
+                                            TypeHint type, const gchar *value,
                                             gpointer *prefix_data, gpointer user_data);
 typedef gboolean (*VPWalkCallbackFunc)(const gchar *name,
                                        const gchar *prefix, gpointer *prefix_data,
@@ -41,24 +43,26 @@ typedef gboolean (*VPWalkCallbackFunc)(const gchar *name,
 
 gboolean value_pairs_add_scope(ValuePairs *vp, const gchar *scope);
 void value_pairs_add_glob_pattern(ValuePairs *vp, const gchar *pattern, gboolean include);
-void value_pairs_add_pair(ValuePairs *vp, GlobalConfig *cfg, const gchar *key, const gchar *value);
+gboolean value_pairs_add_pair(ValuePairs *vp, GlobalConfig *cfg,
+                              const gchar *key, TypeHint type,
+                              const gchar *value, GError **error);
 
 void value_pairs_add_transforms(ValuePairs *vp, gpointer vpts);
 
-void value_pairs_foreach_sorted(ValuePairs *vp, VPForeachFunc func,
-                                GCompareDataFunc compare_func,
-                                LogMessage *msg, gint32 seq_num,
-                                gpointer user_data);
-void value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
-                         LogMessage *msg, gint32 seq_num,
-                         gpointer user_data);
+gboolean value_pairs_foreach_sorted(ValuePairs *vp, VPForeachFunc func,
+                                    GCompareDataFunc compare_func,
+                                    LogMessage *msg, gint32 seq_num,
+                                    gpointer user_data);
+gboolean value_pairs_foreach(ValuePairs *vp, VPForeachFunc func,
+                             LogMessage *msg, gint32 seq_num,
+                             gpointer user_data);
 
-void value_pairs_walk(ValuePairs *vp,
-                      VPWalkCallbackFunc obj_start_func,
-                      VPWalkValueCallbackFunc process_value_func,
-                      VPWalkCallbackFunc obj_end_func,
-                      LogMessage *msg, gint32 seq_num,
-                      gpointer user_data);
+gboolean value_pairs_walk(ValuePairs *vp,
+                          VPWalkCallbackFunc obj_start_func,
+                          VPWalkValueCallbackFunc process_value_func,
+                          VPWalkCallbackFunc obj_end_func,
+                          LogMessage *msg, gint32 seq_num,
+                          gpointer user_data);
 
 ValuePairs *value_pairs_new(void);
 void value_pairs_free(ValuePairs *vp);
diff --git a/modules/afamqp/afamqp.c b/modules/afamqp/afamqp.c
index 923f90b..894f81d 100644
--- a/modules/afamqp/afamqp.c
+++ b/modules/afamqp/afamqp.c
@@ -374,7 +374,8 @@ afamqp_dd_connect(AMQPDestDriver *self, gboolean reconnect)
  */
 
 static gboolean
-afamqp_vp_foreach(const gchar *name, const gchar *value,
+afamqp_vp_foreach(const gchar *name,
+                  TypeHint type, const gchar *value,
                   gpointer user_data)
 {
   amqp_table_entry_t **entries = (amqp_table_entry_t **) ((gpointer *)user_data)[0];
diff --git a/modules/afmongodb/afmongodb.c b/modules/afmongodb/afmongodb.c
index 9779097..5fac90a 100644
--- a/modules/afmongodb/afmongodb.c
+++ b/modules/afmongodb/afmongodb.c
@@ -359,7 +359,7 @@ afmongodb_vp_obj_end(const gchar *name,
 
 static gboolean
 afmongodb_vp_process_value(const gchar *name, const gchar *prefix,
-                           const gchar *value,
+                           TypeHint type, const gchar *value,
                            gpointer *prefix_data, gpointer user_data)
 {
   bson *o;
diff --git a/modules/json/format-json.c b/modules/json/format-json.c
index d654c2e..edb1608 100644
--- a/modules/json/format-json.c
+++ b/modules/json/format-json.c
@@ -163,7 +163,8 @@ tf_json_obj_end(const gchar *name,
 }
 
 static gboolean
-tf_json_value(const gchar *name, const gchar *prefix, const gchar *value,
+tf_json_value(const gchar *name, const gchar *prefix,
+              TypeHint type, const gchar *value,
               gpointer *prefix_data, gpointer user_data)
 {
   json_state_t *state = (json_state_t *)user_data;
diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am
index ccd5e29..cf59ee1 100644
--- a/tests/unit/Makefile.am
+++ b/tests/unit/Makefile.am
@@ -26,6 +26,7 @@ check_PROGRAMS =			\
 	test_serialize			\
 	test_zone			\
 	test_persist_state		\
+	test_type_hints			\
 	test_value_pairs
 
 test_msgparse_SOURCES = test_msgparse.c
diff --git a/tests/unit/test_type_hints.c b/tests/unit/test_type_hints.c
new file mode 100644
index 0000000..d453792
--- /dev/null
+++ b/tests/unit/test_type_hints.c
@@ -0,0 +1,191 @@
+#include "testutils.h"
+#include "type-hinting.h"
+#include "apphook.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define assert_type_hint(hint,expected)                                 \
+  do                                                                    \
+    {                                                                   \
+      TypeHint t;                                                       \
+      GError *e = NULL;                                                 \
+                                                                        \
+      assert_true(type_hint_parse(hint, &t, &e),                        \
+                  "Parsing '%s' as type hint", hint);                   \
+                                                                        \
+      assert_gint(t, expected,                                          \
+                  "Parsing '%s' as type hint results in correct type", hint); \
+    } while(0)                                                          \
+
+static void
+assert_error(GError *error, gint code, const gchar *expected_message)
+{
+  assert_not_null(error, "GError expected to be non-NULL");
+
+  assert_gint(error->code, code, "GError error code is as expected");
+  if (expected_message)
+    assert_string(error->message, expected_message, "GError error message is as expected");
+}
+
+static void
+test_type_hint_parse(void)
+{
+  TypeHint t;
+  GError *e = NULL;
+
+  testcase_begin("Testing type hint parsing");
+
+  assert_type_hint(NULL, TYPE_HINT_STRING);
+  assert_type_hint("string", TYPE_HINT_STRING);
+  assert_type_hint("literal", TYPE_HINT_LITERAL);
+  assert_type_hint("boolean", TYPE_HINT_BOOLEAN);
+  assert_type_hint("int", TYPE_HINT_INT32);
+  assert_type_hint("int32", TYPE_HINT_INT32);
+  assert_type_hint("int64", TYPE_HINT_INT64);
+  assert_type_hint("datetime", TYPE_HINT_DATETIME);
+  assert_type_hint("default", TYPE_HINT_DEFAULT);
+
+  assert_false(type_hint_parse("invalid-hint", &t, &e),
+               "Parsing an invalid hint results in an error.");
+
+  assert_error(e, TYPE_HINTING_INVALID_TYPE, "invalid-hint");
+
+  testcase_end();
+}
+
+#define assert_type_cast(target,value,out)                              \
+  do                                                                    \
+    {                                                                   \
+      assert_true(type_cast_to_##target(value, out, &error),            \
+                  "Casting '%s' to %s works", value, #target);          \
+      assert_no_error(error, "Successful casting returns no error");    \
+    } while(0)
+
+#define assert_type_cast_fail(target,value,out)                 \
+  do                                                            \
+    {                                                           \
+      assert_false(type_cast_to_##target(value, out, &error),   \
+                   "Casting '%s' to %s fails", value, #target); \
+      assert_error(error, TYPE_HINTING_INVALID_CAST, NULL);     \
+      error = NULL;                                             \
+    } while(0)
+
+#define assert_bool_cast(value,expected)                                \
+  do                                                                    \
+    {                                                                   \
+      gboolean ob;                                                      \
+      assert_type_cast(boolean, value, &ob);                            \
+      assert_gboolean(ob, expected, "'%s' casted to boolean is %s",     \
+                      value, #expected);                                \
+    } while(0)
+
+#define assert_int_cast(value,width,expected)                           \
+  do                                                                    \
+    {                                                                   \
+      gint##width i;                                                    \
+      assert_type_cast(int##width, value, &i);                          \
+      assert_gint##width(i, expected, "'%s' casted to int%s is %u",     \
+                         value, expected);                              \
+    } while(0) \
+
+static void
+test_type_cast(void)
+{
+  GError *error = NULL;
+  gboolean ob;
+  gint32 i32;
+  gint64 i64;
+  guint64 dt;
+
+  testcase_begin("Testing type casting");
+
+  /* Boolean */
+
+  assert_bool_cast("True", TRUE);
+  assert_bool_cast("true", TRUE);
+  assert_bool_cast("1", TRUE);
+  assert_bool_cast("totally true", TRUE);
+  assert_bool_cast("False", FALSE);
+  assert_bool_cast("false", FALSE);
+  assert_bool_cast("0", FALSE);
+  assert_bool_cast("fatally false", FALSE);
+
+  assert_type_cast_fail(boolean, "booyah", &ob);
+
+  /* int32 */
+  assert_int_cast("12345", 32, 12345);
+  assert_type_cast_fail(int32, "12345a", &i32);
+
+  /* int64 */
+  assert_int_cast("12345", 64, 12345);
+  assert_type_cast_fail(int64, "12345a", &i64);
+
+  /* datetime */
+  assert_type_cast(datetime_int, "12345", &dt);
+  assert_guint64(dt, 12345000, "Casting '12345' to datetime works");
+  assert_type_cast(datetime_int, "12345.5", &dt);
+  assert_guint64(dt, 12345500, "Casting '12345.5' to datetime works");
+  assert_type_cast(datetime_int, "12345.54", &dt);
+  assert_guint64(dt, 12345540, "Casting '12345.54' to datetime works");
+  assert_type_cast(datetime_int, "12345.543", &dt);
+  assert_guint64(dt, 12345543, "Casting '12345.543' to datetime works");
+  assert_type_cast(datetime_int, "12345.54321", &dt);
+  assert_guint64(dt, 12345543, "Casting '12345.54321' to datetime works");
+
+  assert_type_cast_fail(datetime_int, "invalid", &dt);
+
+  testcase_end();
+}
+
+#define assert_type_cast_parse_strictness(strictness,expected)          \
+  do                                                                    \
+    {                                                                   \
+      gint r;                                                           \
+                                                                        \
+      assert_true(type_cast_strictness_parse(strictness, &r, &error),   \
+                  "Parsing '%s' works", strictness);                    \
+      assert_no_error(error, "Successful strictness parsing returns no error"); \
+      assert_gint32(r, expected, "'%s' parses down to '%s'", strictness, #expected); \
+    } while(0)
+
+static void
+test_type_cast_strictness(void)
+{
+  GError *error = NULL;
+  gint r;
+
+  testcase_begin("Testing type cast strictness");
+
+  assert_type_cast_parse_strictness("drop-message", TYPE_CAST_DROP_MESSAGE);
+  assert_type_cast_parse_strictness("silently-drop-message",
+                                    TYPE_CAST_DROP_MESSAGE | TYPE_CAST_SILENTLY);
+
+  assert_type_cast_parse_strictness("drop-property", TYPE_CAST_DROP_PROPERTY);
+  assert_type_cast_parse_strictness("silently-drop-property",
+                                    TYPE_CAST_DROP_PROPERTY | TYPE_CAST_SILENTLY);
+
+  assert_type_cast_parse_strictness("fallback-to-string", TYPE_CAST_FALLBACK_TO_STRING);
+  assert_type_cast_parse_strictness("silently-fallback-to-string",
+                                    TYPE_CAST_FALLBACK_TO_STRING | TYPE_CAST_SILENTLY);
+
+  assert_false(type_cast_strictness_parse("do-what-i-mean", &r, &error),
+               "The 'do-what-i-mean' strictness is not recognised");
+  assert_error(error, TYPE_CAST_INVALID_STRICTNESS, NULL);
+
+  testcase_end();
+}
+
+int
+main (void)
+{
+  app_startup();
+
+  test_type_hint_parse();
+  test_type_cast();
+  test_type_cast_strictness();
+
+  app_shutdown();
+
+  return 0;
+}
diff --git a/tests/unit/test_value_pairs.c b/tests/unit/test_value_pairs.c
index ca4ba32..f857ca8 100644
--- a/tests/unit/test_value_pairs.c
+++ b/tests/unit/test_value_pairs.c
@@ -10,7 +10,7 @@
 gboolean success = TRUE;
 
 gboolean
-vp_keys_foreach(const gchar  *name, const gchar *value, gpointer user_data)
+vp_keys_foreach(const gchar  *name, TypeHint type, const gchar *value, gpointer user_data)
 {
   gpointer *args = (gpointer *) user_data;
   GList **keys = (GList **) args[0];
@@ -62,7 +62,7 @@ testcase(const gchar *scope, const gchar *exclude, const gchar *expected, GPtrAr
   value_pairs_add_scope(vp, scope);
   if (exclude)
     value_pairs_add_glob_pattern(vp, exclude, FALSE);
-  value_pairs_add_pair(vp, configuration, "test.key", "$MESSAGE");
+  value_pairs_add_pair(vp, configuration, "test.key", TYPE_HINT_STRING, "$MESSAGE", NULL);
 
   if (transformers)
     {
@@ -123,7 +123,7 @@ main(int argc, char *argv[])
 
   testcase("all-nv-pairs", NULL, ".SDATA.EventData at 18372.4.Data,.SDATA.Keywords at 18372.4.Keyword,.SDATA.meta.sequenceId,.SDATA.meta.sysUpTime,.SDATA.origin.ip,HOST,MESSAGE,MSGID,PID,PROGRAM", NULL);
 
-  testcase("everything", NULL, ".SDATA.EventData at 18372.4.Data,.SDATA.Keywords at 18372.4.Keyword,.SDATA.meta.sequenceId,.SDATA.meta.sysUpTime,.SDATA.origin.ip,AMPM,BSDTAG,C_DATE,C_DAY,C_FULLDATE,C_HOUR,C_ISODATE,C_MIN,C_MONTH,C_MONTH_ABBREV,C_MONTH_NAME,C_MONTH_WEEK,C_SEC,C_STAMP,C_TZ,C_TZOFFSET,C_UNIXTIME,C_WEEK,C_WEEKDAY,C_WEEK_DAY,C_WEEK_DAY_ABBREV,C_WEEK_DAY_NAME,C_YEAR,C_YEAR_DAY,DATE,DAY,FACILITY,FACILITY_NUM,FULLDATE,HOST,HOUR,HOUR12,ISODATE,LEVEL,LEVEL_NUM,LOGHOST,MESSAGE,MIN,MONTH,MONTH_ABBREV,MONTH_NAME,MONTH_WEEK,MSEC,MSG,MSGHDR,MSGID,PID,PRI,PRIORITY,PROGRAM,R_AMPM,R_DATE,R_DAY,R_FULLDATE,R_HOUR,R_HOUR12,R_ISODATE,R_MIN,R_MONTH,R_MONTH_ABBREV,R_MONTH_NAME,R_MONTH_WEEK,R_MSEC,R_SEC,R_STAMP,R_TZ,R_TZOFFSET,R_UNIXTIME,R_USEC,R_WEEK,R_WEEKDAY,R_WEEK_DAY,R_WEEK_DAY_ABBREV,R_WEEK_DAY_NAME,R_YEAR,R_YEAR_DAY,SDATA,SEC,SEQNUM,SOURCEIP,STAMP,SYSUPTIME,S_AMPM,S_DATE,S_DAY,S_FULLDATE,S_HOUR,S_HOUR12,S_ISODATE,S_MIN,S_MONTH,S_MONTH_ABBREV,S_MONTH_NAME,S_MONTH_WEEK,S_MSEC,S_SEC,S
 _STAMP,S
 _TZ,S_TZOFFSET,S_UNIXTIME,S_USEC,S_WEEK,S_WEEKDAY,S_WEEK_DAY,S_WEEK_DAY_ABBREV,S_WEEK_DAY_NAME,S_YEAR,S_YEAR_DAY,TAG,TAGS,TZ,TZOFFSET,UNIXTIME,USEC,WEEK,WEEKDAY,WEEK_DAY,WEEK_DAY_ABBREV,WEEK_DAY_NAME,YEAR,YEAR_DAY", NULL);
+  testcase("everything", NULL, ".SDATA.EventData at 18372.4.Data,.SDATA.Keywords at 18372.4.Keyword,.SDATA.meta.sequenceId,.SDATA.meta.sysUpTime,.SDATA.origin.ip,AMPM,BSDTAG,CONTEXT_ID,C_DATE,C_DAY,C_FULLDATE,C_HOUR,C_ISODATE,C_MIN,C_MONTH,C_MONTH_ABBREV,C_MONTH_NAME,C_MONTH_WEEK,C_SEC,C_STAMP,C_TZ,C_TZOFFSET,C_UNIXTIME,C_WEEK,C_WEEKDAY,C_WEEK_DAY,C_WEEK_DAY_ABBREV,C_WEEK_DAY_NAME,C_YEAR,C_YEAR_DAY,DATE,DAY,FACILITY,FACILITY_NUM,FULLDATE,HOST,HOUR,HOUR12,ISODATE,LEVEL,LEVEL_NUM,LOGHOST,MESSAGE,MIN,MONTH,MONTH_ABBREV,MONTH_NAME,MONTH_WEEK,MSEC,MSG,MSGHDR,MSGID,PID,PRI,PRIORITY,PROGRAM,R_AMPM,R_DATE,R_DAY,R_FULLDATE,R_HOUR,R_HOUR12,R_ISODATE,R_MIN,R_MONTH,R_MONTH_ABBREV,R_MONTH_NAME,R_MONTH_WEEK,R_MSEC,R_SEC,R_STAMP,R_TZ,R_TZOFFSET,R_UNIXTIME,R_USEC,R_WEEK,R_WEEKDAY,R_WEEK_DAY,R_WEEK_DAY_ABBREV,R_WEEK_DAY_NAME,R_YEAR,R_YEAR_DAY,SDATA,SEC,SEQNUM,SOURCEIP,STAMP,SYSUPTIME,S_AMPM,S_DATE,S_DAY,S_FULLDATE,S_HOUR,S_HOUR12,S_ISODATE,S_MIN,S_MONTH,S_MONTH_ABBREV,S_MONTH_NAME,S_MONTH_WEEK,S_M
 SEC,S_SE
 C,S_STAMP,S_TZ,S_TZOFFSET,S_UNIXTIME,S_USEC,S_WEEK,S_WEEKDAY,S_WEEK_DAY,S_WEEK_DAY_ABBREV,S_WEEK_DAY_NAME,S_YEAR,S_YEAR_DAY,TAG,TAGS,TZ,TZOFFSET,UNIXTIME,USEC,WEEK,WEEKDAY,WEEK_DAY,WEEK_DAY_ABBREV,WEEK_DAY_NAME,YEAR,YEAR_DAY", NULL);
 
   testcase("nv-pairs", ".SDATA.*", "HOST,MESSAGE,MSGID,PID,PROGRAM", NULL);
 
@@ -139,7 +139,7 @@ main(int argc, char *argv[])
   g_ptr_array_add(transformers, value_pairs_new_transform_shift(2));
   g_ptr_array_add(transformers, value_pairs_new_transform_replace("C_", "CC_"));
 
-  testcase("everything", NULL, ".SDATA.EventData at 18372.4.Data,.SDATA.Keywords at 18372.4.Keyword,.SDATA.meta.sequenceId,.SDATA.meta.sysUpTime,.SDATA.origin.ip,AMPM,BSDTAG,CC_DATE,CC_DAY,CC_FULLDATE,CC_HOUR,CC_ISODATE,CC_MIN,CC_MONTH,CC_MONTH_ABBREV,CC_MONTH_NAME,CC_MONTH_WEEK,CC_SEC,CC_STAMP,CC_TZ,CC_TZOFFSET,CC_UNIXTIME,CC_WEEK,CC_WEEKDAY,CC_WEEK_DAY,CC_WEEK_DAY_ABBREV,CC_WEEK_DAY_NAME,CC_YEAR,CC_YEAR_DAY,DATE,DAY,FACILITY,FACILITY_NUM,FULLDATE,HOST,HOUR,HOUR12,ISODATE,LEVEL,LEVEL_NUM,LOGHOST,MESSAGE,MIN,MONTH,MONTH_ABBREV,MONTH_NAME,MONTH_WEEK,MSEC,MSG,MSGHDR,MSGID,PID,PRI,PRIORITY,PROGRAM,R_AMPM,R_DATE,R_DAY,R_FULLDATE,R_HOUR,R_HOUR12,R_ISODATE,R_MIN,R_MONTH,R_MONTH_ABBREV,R_MONTH_NAME,R_MONTH_WEEK,R_MSEC,R_SEC,R_STAMP,R_TZ,R_TZOFFSET,R_UNIXTIME,R_USEC,R_WEEK,R_WEEKDAY,R_WEEK_DAY,R_WEEK_DAY_ABBREV,R_WEEK_DAY_NAME,R_YEAR,R_YEAR_DAY,SDATA,SEC,SEQNUM,SOURCEIP,STAMP,SYSUPTIME,S_AMPM,S_DATE,S_DAY,S_FULLDATE,S_HOUR,S_HOUR12,S_ISODATE,S_MIN,S_MONTH,S_MONTH_ABBREV,S_MONTH_NAME,S_MON
 TH_WEEK,
 S_MSEC,S_SEC,S_STAMP,S_TZ,S_TZOFFSET,S_UNIXTIME,S_USEC,S_WEEK,S_WEEKDAY,S_WEEK_DAY,S_WEEK_DAY_ABBREV,S_WEEK_DAY_NAME,S_YEAR,S_YEAR_DAY,TAG,TAGS,TZ,TZOFFSET,UNIXTIME,USEC,WEEK,WEEKDAY,WEEK_DAY,WEEK_DAY_ABBREV,WEEK_DAY_NAME,YEAR,YEAR_DAY", transformers);
+  testcase("everything", NULL, ".SDATA.EventData at 18372.4.Data,.SDATA.Keywords at 18372.4.Keyword,.SDATA.meta.sequenceId,.SDATA.meta.sysUpTime,.SDATA.origin.ip,AMPM,BSDTAG,CC_DATE,CC_DAY,CC_FULLDATE,CC_HOUR,CC_ISODATE,CC_MIN,CC_MONTH,CC_MONTH_ABBREV,CC_MONTH_NAME,CC_MONTH_WEEK,CC_SEC,CC_STAMP,CC_TZ,CC_TZOFFSET,CC_UNIXTIME,CC_WEEK,CC_WEEKDAY,CC_WEEK_DAY,CC_WEEK_DAY_ABBREV,CC_WEEK_DAY_NAME,CC_YEAR,CC_YEAR_DAY,CONTEXT_ID,DATE,DAY,FACILITY,FACILITY_NUM,FULLDATE,HOST,HOUR,HOUR12,ISODATE,LEVEL,LEVEL_NUM,LOGHOST,MESSAGE,MIN,MONTH,MONTH_ABBREV,MONTH_NAME,MONTH_WEEK,MSEC,MSG,MSGHDR,MSGID,PID,PRI,PRIORITY,PROGRAM,R_AMPM,R_DATE,R_DAY,R_FULLDATE,R_HOUR,R_HOUR12,R_ISODATE,R_MIN,R_MONTH,R_MONTH_ABBREV,R_MONTH_NAME,R_MONTH_WEEK,R_MSEC,R_SEC,R_STAMP,R_TZ,R_TZOFFSET,R_UNIXTIME,R_USEC,R_WEEK,R_WEEKDAY,R_WEEK_DAY,R_WEEK_DAY_ABBREV,R_WEEK_DAY_NAME,R_YEAR,R_YEAR_DAY,SDATA,SEC,SEQNUM,SOURCEIP,STAMP,SYSUPTIME,S_AMPM,S_DATE,S_DAY,S_FULLDATE,S_HOUR,S_HOUR12,S_ISODATE,S_MIN,S_MONTH,S_MONTH_ABBREV,S_MONTH
 _NAME,S_
 MONTH_WEEK,S_MSEC,S_SEC,S_STAMP,S_TZ,S_TZOFFSET,S_UNIXTIME,S_USEC,S_WEEK,S_WEEKDAY,S_WEEK_DAY,S_WEEK_DAY_ABBREV,S_WEEK_DAY_NAME,S_YEAR,S_YEAR_DAY,TAG,TAGS,TZ,TZOFFSET,UNIXTIME,USEC,WEEK,WEEKDAY,WEEK_DAY,WEEK_DAY_ABBREV,WEEK_DAY_NAME,YEAR,YEAR_DAY", transformers);
   g_ptr_array_free(transformers, TRUE);
 
   app_shutdown();
-- 
1.7.10.4




More information about the syslog-ng mailing list