diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib/lib-event.h | 432 |
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 |