summaryrefslogtreecommitdiffstats
path: root/src/lib/lib-event.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib/lib-event.h432
1 files changed, 432 insertions, 0 deletions
diff --git a/src/lib/lib-event.h b/src/lib/lib-event.h
new file mode 100644
index 0000000..74aafd7
--- /dev/null
+++ b/src/lib/lib-event.h
@@ -0,0 +1,432 @@
+#ifndef LIB_EVENT_H
+#define LIB_EVENT_H
+/* event.h name is probably a bit too generic, so lets avoid using it. */
+
+#include <sys/time.h>
+
+/* Field name for the reason_code string list. */
+#define EVENT_REASON_CODE "reason_code"
+
+struct event;
+struct event_log_params;
+
+/* Hierarchical category of events. Each event can belong to multiple
+ categories. For example [ lib-storage/maildir, syscall/io ]. The categories
+ are expected to live as long as they're used in events. */
+struct event_category {
+ struct event_category *parent;
+ const char *name;
+
+ /* non-NULL if this category has been registered
+
+ Do NOT dereference outside of event code in src/lib.
+
+ At any point in time it is safe to (1) check the pointer for
+ NULL/non-NULL to determine if this particular category instance
+ has been registered, and (2) compare two categories' internal
+ pointers to determine if they represent the same category. */
+ void *internal;
+};
+
+enum event_field_value_type {
+ EVENT_FIELD_VALUE_TYPE_STR,
+ EVENT_FIELD_VALUE_TYPE_INTMAX,
+ EVENT_FIELD_VALUE_TYPE_TIMEVAL,
+ EVENT_FIELD_VALUE_TYPE_STRLIST,
+};
+
+struct event_field {
+ const char *key;
+ enum event_field_value_type value_type;
+ struct {
+ const char *str;
+ intmax_t intmax;
+ struct timeval timeval;
+ ARRAY_TYPE(const_string) strlist;
+ } value;
+};
+
+struct event_add_field {
+ const char *key;
+ /* The first non-0/NULL value is used. */
+ const char *value;
+ intmax_t value_intmax;
+ struct timeval value_timeval;
+};
+
+struct event_passthrough {
+ /* wrappers to event_set_*() and event_add_*() for passthrough events,
+ so these can be chained like:
+ event_create_passthrough(parent)->name("name")->...->event() */
+ struct event_passthrough *
+ (*append_log_prefix)(const char *prefix);
+ struct event_passthrough *
+ (*replace_log_prefix)(const char *prefix);
+ struct event_passthrough *
+ (*set_name)(const char *name);
+ struct event_passthrough *
+ (*set_source)(const char *filename,
+ unsigned int linenum, bool literal_fname);
+ struct event_passthrough *
+ (*set_always_log_source)(void);
+
+ struct event_passthrough *
+ (*add_categories)(struct event_category *const *categories);
+ struct event_passthrough *
+ (*add_category)(struct event_category *category);
+ struct event_passthrough *
+ (*add_fields)(const struct event_add_field *fields);
+
+ struct event_passthrough *
+ (*add_str)(const char *key, const char *value);
+ struct event_passthrough *
+ (*add_int)(const char *key, intmax_t num);
+ struct event_passthrough *
+ (*add_int_nonzero)(const char *key, intmax_t num);
+ struct event_passthrough *
+ (*add_timeval)(const char *key, const struct timeval *tv);
+
+ struct event_passthrough *
+ (*inc_int)(const char *key, intmax_t num);
+
+ struct event_passthrough *
+ (*strlist_append)(const char *key, const char *value);
+ struct event_passthrough *
+ (*strlist_replace)(const char *key, const char *const *value,
+ unsigned int count);
+
+ struct event_passthrough *
+ (*clear_field)(const char *key);
+
+ struct event *(*event)(void);
+};
+
+typedef const char *
+event_log_prefix_callback_t(void *context);
+typedef const char *
+event_log_message_callback_t(void *context, enum log_type log_type,
+ const char *message);
+
+/* Returns TRUE if the event has all the categories that the "other" event has
+ (and maybe more). */
+bool event_has_all_categories(struct event *event, const struct event *other);
+/* Returns TRUE if the event has all the fields that the "other" event has
+ (and maybe more). Only the fields in the events themselves are checked.
+ Parent events' fields are not checked. */
+bool event_has_all_fields(struct event *event, const struct event *other);
+
+/* Returns the source event duplicated into a new event. Event pointers are
+ dropped. */
+struct event *event_dup(const struct event *source);
+/* Returns a flattened version of the source event.
+ Both categories and fields will be flattened.
+ A new reference to the source event is returned if no flattening was
+ needed. Event pointers are dropped if a new event was created. */
+struct event *event_flatten(struct event *src);
+/* Returns a minimized version of the source event.
+ Remove parents with no fields or categories, attempt to flatten fields
+ and categories to avoid sending one-off parent events. (There is a more
+ detailed description in a comment above the function implementation.)
+ A new reference to the source event is returned if no simplification
+ occured. Event pointers are dropped if a new event was created. */
+struct event *event_minimize(struct event *src);
+/* Copy all categories from source to dest.
+ Only the categories in source event itself are copied.
+ Parent events' categories aren't copied. */
+void event_copy_categories(struct event *to, struct event *from);
+/* Copy all fields from source to dest.
+ Only the fields in source event itself are copied.
+ Parent events' fields aren't copied. */
+void event_copy_fields(struct event *to, struct event *from);
+
+/* Create a new empty event under the parent event, or NULL for root event. */
+struct event *event_create(struct event *parent, const char *source_filename,
+ unsigned int source_linenum);
+#define event_create(parent) \
+ event_create((parent), __FILE__, __LINE__)
+/* This is a temporary "passthrough" event. Its main purpose is to make it
+ easier to create temporary events as part of the event parameter in
+ e_error(), e_warning(), e_info() or e_debug(). These passthrough events are
+ automatically freed when the e_*() call is finished. Because this makes the
+ freeing less obvious, it should be avoided outside e_*()'s event parameter.
+
+ The passthrough events also change the API to be more convenient towards
+ being used in a parameter. Instead of having to use e.g.
+ event_add_str(event_set_name(event_create(parent), "name"), "key", "value")
+ the event_passthrough API can be a bit more readable as:
+ event_create_passthrough(parent)->set_name("name")->
+ add_str("key", "value")->event(). The passthrough event is converted to
+ a normal event at the end with the event() call. Note that this API works
+ by modifying the last created passthrough event, so it's not possible to
+ have multiple passthrough events created in parallel. */
+struct event_passthrough *
+event_create_passthrough(struct event *parent, const char *source_filename,
+ unsigned int source_linenum);
+#define event_create_passthrough(parent) \
+ event_create_passthrough((parent), __FILE__, __LINE__)
+
+/* Reference the event. Returns the event parameter. */
+struct event *event_ref(struct event *event);
+/* Unreference the event. If the reference count drops to 0, the event is
+ freed. The current global event's refcount must not drop to 0. */
+void event_unref(struct event **event);
+
+/* Set the event to be the global event and push it at the top of the global
+ event stack. Returns the event parameter. The event must be explicitly
+ popped before it's freed.
+
+ The global event acts as the root event for all the events while they are
+ being emitted. The global events don't permanently affect the event
+ hierarchy. The global events are typically used to add extra fields to all
+ emitted events while some specific work is running.
+
+ For example the global event can be "IMAP command SELECT", which can be used
+ for filtering events that happen while the SELECT command is being executed.
+ However, for the created struct mailbox the parent event should be the
+ mail_user, not the SELECT command. (If the mailbox used SELECT command as
+ the parent event, then any future event emitted via the mailbox event would
+ show SELECT command as the parent, even after SELECT had already finished.)
+
+ The global event works the same as if all the events' roots were instead
+ pointing to the global event. Global events don't affect log prefixes.
+
+ If ioloop contexts are used, the global events will automatically follow the
+ contexts. Any global events pushed while running in a context are popped
+ out when the context is deactivated, and pushed back when context is
+ activated again.
+
+ The created global events should use event_get_global() as their parent
+ event. Only the last pushed global event is used. */
+struct event *event_push_global(struct event *event);
+/* Pop the current global event and set the global event to the next one at
+ the top of the stack. Assert-crash if the current global event isn't the
+ given event parameter. Returns the next (now activated) global event in the
+ stack, or NULL if the stack is now empty. */
+struct event *event_pop_global(struct event *event);
+/* Returns the current global event. */
+struct event *event_get_global(void);
+
+/* Shortcut to create and push a global event and set its reason_code field. */
+struct event_reason *
+event_reason_begin(const char *reason_code, const char *source_filename,
+ unsigned int source_linenum);
+#define event_reason_begin(reason_code) \
+ event_reason_begin(reason_code, __FILE__, __LINE__)
+/* Finish the reason event. It pops the global event, which means it must be
+ at the top of the stack. */
+void event_reason_end(struct event_reason **reason);
+/* Generate a reason code as <module>:<name>. This function does some
+ sanity checks and conversions to make sure the reason codes are reasonable:
+
+ - Assert-crash if module has space, '-', ':' or uppercase characters.
+ - Assert-crash if module is empty
+ - Convert name to lowercase.
+ - Replace all space and '-' in name with '_'.
+ - Assert-crash if name has ':'
+ - assert-crash if name is empty
+*/
+const char *event_reason_code(const char *module, const char *name);
+/* Same as event_reason_code(), but concatenate name_prefix and name.
+ The name_prefix must not contain spaces, '-', ':' or uppercase characters. */
+const char *event_reason_code_prefix(const char *module,
+ const char *name_prefix, const char *name);
+
+/* Set the appended log prefix string for this event. All the parent events'
+ log prefixes will be concatenated together when logging. The log type
+ text (e.g. "Info: ") will be inserted before appended log prefixes (but
+ after replaced log prefix).
+
+ Clears log_prefix callback.
+ */
+struct event *
+event_set_append_log_prefix(struct event *event, const char *prefix);
+/* Replace the full log prefix string for this event. The parent events' log
+ prefixes won't be used. Also, any parent event's message amendment callback
+ is not used.
+
+ Clears log_prefix callback.
+*/
+struct event *event_replace_log_prefix(struct event *event, const char *prefix);
+
+/* Drop count prefixes from parents when this event is used for logging. This
+ does not affect the parent events. This only counts actual prefixes and not
+ parents. If the count is higher than the actual number of prefixes added by
+ parents, all will be dropped. */
+struct event *
+event_drop_parent_log_prefixes(struct event *event, unsigned int count);
+
+/* Sets event prefix callback, sets log_prefix empty */
+struct event *
+event_set_log_prefix_callback(struct event *event, bool replace,
+ event_log_prefix_callback_t *callback,
+ void *context);
+#define event_set_log_prefix_callback(event, replace, callback, context) \
+ event_set_log_prefix_callback(event, replace, \
+ (event_log_prefix_callback_t*)callback, TRUE ? context : \
+ CALLBACK_TYPECHECK(callback, const char *(*)(typeof(context))))
+
+/* Sets event message amendment callback */
+struct event *
+event_set_log_message_callback(struct event *event,
+ event_log_message_callback_t *callback,
+ void *context);
+#define event_set_log_message_callback(event, callback, context) \
+ event_set_log_message_callback(event, \
+ (event_log_message_callback_t*)callback, TRUE ? context : \
+ CALLBACK_TYPECHECK(callback, \
+ const char *(*)(typeof(context), enum log_type, \
+ const char *)))
+
+/* Set the event's name. The name is specific to a single sending of an event,
+ and it'll be automatically cleared once the event is sent. This should
+ typically be used only in a parameter to e_debug(), etc. */
+struct event *
+event_set_name(struct event *event, const char *name);
+/* Set the source filename:linenum to the event. If literal_fname==TRUE,
+ it's assumed that __FILE__ has been used and the pointer is stored directly,
+ otherwise the filename is strdup()ed. */
+struct event *
+event_set_source(struct event *event, const char *filename,
+ unsigned int linenum, bool literal_fname);
+/* Always include the source path:line in the log replies. This is
+ especially useful when logging about unexpected syscall failures, because
+ it allow quickly finding which of the otherwise identical syscalls in the
+ code generated the error. */
+struct event *event_set_always_log_source(struct event *event);
+/* Set minimum normal log level for the event. By default events with INFO
+ level and higher are logged. This can be used to easily hide even the INFO
+ log lines unless some verbose-setting is enabled.
+
+ Note that this functionality is mostly independent of debug logging.
+ Don't use this to enable debug log - use event_set_forced_debug() instead. */
+struct event *event_set_min_log_level(struct event *event, enum log_type level);
+enum log_type event_get_min_log_level(const struct event *event);
+
+/* Add an internal pointer to an event. It can be looked up only with
+ event_get_ptr(). The keys are in their own namespace and won't conflict
+ with event fields. The pointers are specific to this specific event only -
+ they will be dropped from any duplicated/flattened/minimized events. */
+struct event *event_set_ptr(struct event *event, const char *key, void *value);
+/* Return a pointer set with event_set_ptr(), or NULL if it doesn't exist.
+ The pointer is looked up only from the event itself, not its parents. */
+void *event_get_ptr(const struct event *event, const char *key);
+
+/* Add NULL-terminated list of categories to the event. The categories pointer
+ doesn't need to stay valid afterwards, but the event_category structs
+ themselves must be. Returns the event parameter. */
+struct event *
+event_add_categories(struct event *event,
+ struct event_category *const *categories);
+/* Add a single category to the event. */
+struct event *
+event_add_category(struct event *event, struct event_category *category);
+
+/* Add key=value field to the event. If a key already exists, it's replaced.
+ Child events automatically inherit key=values from their parents at the
+ time the event is sent. So changing a key in parent will change the values
+ in the child events as well, unless the key has been overwritten in the
+ child event. Setting the value to "" is the same as event_field_clear().
+ Returns the event parameter. */
+struct event *
+event_add_str(struct event *event, const char *key, const char *value);
+struct event *
+event_add_int(struct event *event, const char *key, intmax_t num);
+/* Adds int value to event if it is non-zero */
+struct event *
+event_add_int_nonzero(struct event *event, const char *key, intmax_t num);
+/* Increase the key's value. If it's not set or isn't an integer type,
+ initialize the value to num. */
+struct event *
+event_inc_int(struct event *event, const char *key, intmax_t num);
+struct event *
+event_add_timeval(struct event *event, const char *key,
+ const struct timeval *tv);
+/* Append new value to list. If the key is not a list, it will
+ be cleared first. NULL values are ignored. Duplicate values are ignored. */
+struct event *
+event_strlist_append(struct event *event, const char *key, const char *value);
+/* Replace value with this strlist. */
+struct event *
+event_strlist_replace(struct event *event, const char *key,
+ const char *const *value, unsigned int count);
+/* Copy the string list from src and its parents to dest. This can be especially
+ useful to copy the current global events' reason_codes to a more permanent
+ (e.g. async) event that can exist after the global events are popped out. */
+struct event *
+event_strlist_copy_recursive(struct event *dest, const struct event *src,
+ const char *key);
+/* Same as event_add_str/int(), but do it via event_field struct. The fields
+ terminates with key=NULL. Returns the event parameter. */
+struct event *
+event_add_fields(struct event *event, const struct event_add_field *fields);
+/* Mark a field as nonexistent. If a parent event has the field set, this
+ allows removing it from the child event. Using an event filter with e.g.
+ "key=*" won't match this field anymore, although it's still visible in
+ event_find_field*() and event_get_fields(). This is the same as using
+ event_add_str() with value="". */
+void event_field_clear(struct event *event, const char *key);
+
+/* Returns the parent event, or NULL if it doesn't exist. */
+struct event *event_get_parent(const struct event *event);
+/* Get the event's creation time. */
+void event_get_create_time(const struct event *event, struct timeval *tv_r);
+/* Get the time when the event was last sent. Returns TRUE if time was
+ returned, FALSE if event has never been sent. */
+bool event_get_last_send_time(const struct event *event, struct timeval *tv_r);
+/* Get the event duration field in microseconds. This is calculated from
+ the event's last sent time. */
+void event_get_last_duration(const struct event *event,
+ uintmax_t *duration_usecs_r);
+/* Returns field for a given key, or NULL if it doesn't exist. */
+struct event_field *
+event_find_field_nonrecursive(const struct event *event, const char *key);
+/* Returns field for a given key, or NULL if it doesn't exist. If the key
+ isn't found from the event itself, find it from parent events, including
+ from the global event. */
+const struct event_field *
+event_find_field_recursive(const struct event *event, const char *key);
+/* Same as event_find_field(), but return the value converted to a string.
+ If the field isn't stored as a string, the result is allocated from
+ data stack. */
+const char *
+event_find_field_recursive_str(const struct event *event, const char *key);
+/* Returns all key=value fields that the event has.
+ Parent events' fields aren't returned. */
+const struct event_field *
+event_get_fields(const struct event *event, unsigned int *count_r);
+/* Return all categories that the event has.
+ Parent events' categories aren't returned. */
+struct event_category *const *
+event_get_categories(const struct event *event, unsigned int *count_r);
+
+/* Export the event into a tabescaped string, so its fields are separated
+ with TABs and there are no NUL, CR or LF characters. */
+void event_export(const struct event *event, string_t *dest);
+/* Import event. The string is expected to be generated by event_export().
+ All the used categories must already be registered.
+ Returns TRUE on success, FALSE on invalid string. */
+bool event_import(struct event *event, const char *str, const char **error_r);
+/* Same as event_import(), but string is already split into an array
+ of strings via *_strsplit_tabescaped(). */
+bool event_import_unescaped(struct event *event, const char *const *args,
+ const char **error_r);
+
+/* The event wasn't sent after all - free everything related to it.
+ Most importantly this frees any passthrough events. Typically this shouldn't
+ need to be called. */
+void event_send_abort(struct event *event);
+
+/* Enable "user_cpu_usecs" event field to event by getting current resource
+ usage which will be used in consequent event_send() to calculate
+ cpu time. This function can be called multiple times to update the current
+ resource usage.
+
+ The "user_cpu_usecs" field is automatically inherited by passthrough events,
+ but not full events.
+*/
+void event_enable_user_cpu_usecs(struct event *event);
+
+void lib_event_init(void);
+void lib_event_deinit(void);
+
+#endif