From dba096ac81f68ee1a7c49bb6232285dc0211bf10 Mon Sep 17 00:00:00 2001 From: Dmitri Pal Date: Fri, 7 Aug 2009 15:50:34 -0400 Subject: ELAPI Next round of functionality - logging part of the interface a) Added the main logging interface which allows creating dispatcher and logging messages or events. Can't actully log anything yet since the sinks are stubbed out. b) Made default template be a part of the default dispatcher. c) Updated UNIT test. d) Some of the calls are stubbed out but they are there to indicate where next round of work will be. --- elapi/elapi_log.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 631 insertions(+) create mode 100644 elapi/elapi_log.c (limited to 'elapi/elapi_log.c') diff --git a/elapi/elapi_log.c b/elapi/elapi_log.c new file mode 100644 index 0000000..7c0d72a --- /dev/null +++ b/elapi/elapi_log.c @@ -0,0 +1,631 @@ +/* + ELAPI + + Implementation of the ELAPI logging interface. + + Copyright (C) Dmitri Pal 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + This program 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 General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#define _GNU_SOURCE +#include /* for stat() */ +#include /* for stat() */ +#include /* for stat() */ +#include /* for errors */ +#include /* for memset() and other */ +#include /* for va_arg() */ +#include /* for free() */ + + +#include "elapi_priv.h" +#include "elapi_event.h" +#include "elapi_log.h" +#include "ini_config.h" +#include "trace.h" +#include "config.h" + + +/* Pointer to default global dispatcher */ +struct elapi_dispatcher *global_dispatcher = NULL; + +/* Deafult sink names */ +char remote_sink[] = "remote"; +char altremote_sink[] = "altremote"; +char syslog_sink[] = "syslog"; +char db_sink[] = "db"; +char file_sink[] = "file"; +char failover_sink[] = "failover"; +char stderr_sink[] = "stderr"; + +/* Deafult sink list */ +char *default_sinks[] = { remote_sink, + altremote_sink, + syslog_sink, + db_sink, + file_sink, + failover_sink, + stderr_sink, + NULL }; + +/* Per review I was told to hard cord this name. So be it... */ +#define ELAPI_CONFIG_FILE_NAME "elapi.conf" + +/* Default config file */ +static char default_config_file[] = ELAPI_DEFAULT_CONFIG_DIR "/" ELAPI_CONFIG_FILE_NAME; +/* Default config dir */ +static char default_config_dir[] = ELAPI_DEFAULT_CONFIG_APP_DIR; + + +/* Was a cleanup callback registered ? */ +static int elapi_close_registered = 0; + + +/* Internal function to log message using args */ +static int elapi_dsp_msg_with_vargs(struct elapi_dispatcher *dispatcher, + struct collection_item *template, + va_list args) +{ + int error = EOK; + struct collection_item *event; + + TRACE_FLOW_STRING("elapi_dsp_msg_with_vargs", "Entry"); + + if (!dispatcher) { + TRACE_ERROR_NUMBER("Invalid argument", EINVAL); + return EINVAL; + } + + /* Create event */ + error = elapi_create_event_with_vargs(&event, + template, + NULL, + 0, + args); + + if (error) { + TRACE_ERROR_NUMBER("Failed to create event", error); + return error; + } + + /* Now log event */ + error = elapi_dsp_log(dispatcher, event); + + /* Destroy event */ + elapi_destroy_event(event); + + if (error) { + TRACE_ERROR_NUMBER("Failed to log event", error); + return error; + } + + TRACE_FLOW_STRING("elapi_dsp_msg_with_vargs", "Exit"); + return error; +} + + +/********** Main functions of the interface **********/ + +/* Function to create a dispatcher */ +int elapi_create_dispatcher_adv(struct elapi_dispatcher **dispatcher, + const char *appname, + const char *config_path, + elapi_add_fd add_fd_add_fn, + elapi_rem_fd add_fd_rem_fn, + elapi_add_timer add_timer_fn, + void *callers_data) +{ + struct elapi_dispatcher *handle = NULL; + struct collection_item *error_set = NULL; + int error = EOK; + struct collection_item *item = NULL; + const char *config_file = NULL; + const char *config_dir = NULL; + struct stat stat_data; + int prm_cnt = 0; + + TRACE_FLOW_STRING("elapi_create_dispatcher_adv", "Entry point"); + + /* Make sure the memory for handle is passed in */ + if (dispatcher == NULL) { + TRACE_ERROR_STRING("elapi_create_dispatcher_adv", "Invalid parameter."); + return EINVAL; + } + + /* Make sure we got the right constant */ + TRACE_INFO_NUMBER("ELAPI_DEFAULT_APP_NAME_SIZE = ", ELAPI_DEFAULT_APP_NAME_SIZE); + + if ((appname != NULL) && (strlen(appname) > ELAPI_DEFAULT_APP_NAME_SIZE)) { + TRACE_ERROR_STRING("elapi_create_dispatcher", "Application name is too long."); + return EINVAL; + } + + /* Check that all the async data is present */ + if (!add_fd_add_fn) prm_cnt++; + if (!add_fd_rem_fn) prm_cnt++; + if (!add_timer_fn) prm_cnt++; + if (!callers_data) prm_cnt++; + + if ((prm_cnt > 0) && (prm_cnt < 4)) { + /* We got a mixture of NULLs and not NULLs. + * This is bad since all should be either provided + * or all should be NULL. + */ + TRACE_ERROR_STRING("Invalid sync parameters.", "At least one is NULL while others are not."); + return EINVAL; + } + + /* Check what is passed in the config_path */ + if (config_path) { + /* What is it ? */ + if(stat(config_path, &stat_data)) { + error = errno; + TRACE_ERROR_NUMBER("Invalid path assume defaults. Error", error); + config_file = default_config_file; + config_dir = default_config_dir; + } + else { + if (S_ISREG(stat_data.st_mode)) { + config_file = config_path; + config_dir = NULL; + TRACE_INFO_STRING("Will use config file", config_file); + } + else if (S_ISDIR(stat_data.st_mode)) { + config_file = NULL; + config_dir = config_path; + TRACE_INFO_STRING("Will use directory", config_dir); + } + else { + config_file = default_config_file; + config_dir = default_config_dir; + } + } + } + else { + config_file = default_config_file; + config_dir = default_config_dir; + } + + TRACE_INFO_STRING("FILE:", config_file); + TRACE_INFO_STRING("DIR:", config_dir); + + /* Allocate memory */ + handle = (struct elapi_dispatcher *) malloc(sizeof(struct elapi_dispatcher)); + if (handle == NULL) { + error = errno; + TRACE_ERROR_NUMBER("Memory allocation failed. Error", error); + return error; + } + + /* Clean memory - we need it to be able to destroy the dispatcher at any moment */ + /* FIXME - eventually remove the memset from here when the structure finalizes */ + /* Valgrind requires explicit initialization of the structure member, otherwise + * it complains about jump or move based on the uninitialized variable. + */ + memset(handle, 0, sizeof(struct elapi_dispatcher *)); + handle->ini_config = NULL; + handle->sink_list = NULL; + handle->sinks = NULL; + handle->default_template = NULL; + handle->need_to_free = 0; + + /* Save application name in the handle */ + if (appname != NULL) handle->appname = strdup(appname); + else handle->appname = strdup(ELAPI_DEFAULT_APP_NAME); + + TRACE_FLOW_STRING("Application name:", handle->appname); + + /* Check error */ + if (handle->appname == NULL) { + error = errno; + TRACE_ERROR_NUMBER("Memory allocation failed. Error", error); + elapi_destroy_dispatcher(handle); + return error; + } + + /* Read the ELAPI configuration and store it in the dispatcher handle */ + error = config_for_app(handle->appname, + config_file, + config_dir, + &(handle->ini_config), + INI_STOP_ON_ANY, + &error_set); + if (error) { + TRACE_ERROR_NUMBER("Attempt to read configuration returned error", error); + elapi_destroy_dispatcher(handle); + if (error_set) { + elapi_internal_dump_errors_to_file(error_set); + free_ini_config_errors(error_set); + } + return error; + } + /* Have to clean error set anyways */ + free_ini_config_errors(error_set); + + /* Get sink list from configuration */ + error = get_config_item(ELAPI_DISPATCHER, + ELAPI_SINKS, + handle->ini_config, + &item); + if (error) { + TRACE_ERROR_NUMBER("Attempt to read configuration returned error", error); + elapi_destroy_dispatcher(handle); + return error; + } + + if (!item) { + /* There is no list of sinks - use default list */ + handle->sinks = default_sinks; + } + else { + /* Get one from config but make sure we free it later */ + handle->sinks = get_string_config_array(item, NULL, NULL, NULL); + handle->need_to_free = 1; + } + + /* Populate async processing data if any */ + if (prm_cnt) { + TRACE_INFO_STRING("Async data is present", ""); + handle->add_fd_add_fn = add_fd_add_fn; + handle->add_fd_rem_fn = add_fd_rem_fn; + handle->add_timer_fn = add_timer_fn; + handle->callers_data = callers_data; + handle->async_mode = 1; + } + + /* Create the list of sinks */ + error = elapi_internal_construct_sink_list(handle); + if (error != EOK) { + TRACE_ERROR_NUMBER("Failed to create sink list. Error", error); + elapi_destroy_dispatcher(handle); + return error; + } + + *dispatcher = handle; + + TRACE_FLOW_STRING("elapi_create_dispatcher_adv", "Returning Success."); + return EOK; + +} + +/* Simple dispatcher */ +int elapi_create_dispatcher(struct elapi_dispatcher **dispatcher, + const char *appname, + const char *config_path) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_create_dispatcher", "Entry."); + + /* Will have more parmeters in future */ + error = elapi_create_dispatcher_adv(dispatcher, + appname, + config_path, + NULL, + NULL, + NULL, + NULL); + + TRACE_FLOW_STRING("elapi_create_dispatcher", "Exit."); + return error; + +} + +/* Function to clean memory associated with the dispatcher */ +void elapi_destroy_dispatcher(struct elapi_dispatcher *dispatcher) +{ + TRACE_FLOW_STRING("elapi_destroy_dispatcher", "Entry."); + + if (dispatcher) { + TRACE_INFO_STRING("Deleting template if any...", ""); + col_destroy_collection(dispatcher->default_template); + TRACE_INFO_STRING("Closing sink handler.", ""); + (void)col_traverse_collection(dispatcher->sink_list, + COL_TRAVERSE_ONELEVEL, + elapi_internal_sink_cleanup_handler, + NULL); + TRACE_INFO_STRING("Deleting sink list.", ""); + col_destroy_collection(dispatcher->sink_list); + TRACE_INFO_STRING("Freeing application name.", ""); + free(dispatcher->appname); + TRACE_INFO_STRING("Freeing config.", ""); + free_ini_config(dispatcher->ini_config); + TRACE_INFO_STRING("Deleting sink name array.", ""); + if (dispatcher->need_to_free) free_string_config_array(dispatcher->sinks); + TRACE_INFO_STRING("Freeing dispatcher.", ""); + free(dispatcher); + } + + TRACE_FLOW_STRING("elapi_destroy_dispatcher", "Exit."); +} + +/* Function to log an event */ +int elapi_dsp_log(struct elapi_dispatcher *dispatcher, struct collection_item *event) +{ + int error = EOK; + struct elapi_sink_context sink_env; + + TRACE_FLOW_STRING("elapi_dsp_log", "Entry"); + + if ((dispatcher == NULL) || + (event == NULL)) { + TRACE_ERROR_STRING("elapi_dsp_log", "ERROR Invalid argument"); + return EINVAL; + } + + sink_env.handle = dispatcher; + sink_env.event = event; + + /* Logging an event is just iterating through the sinks and calling the sink_handler */ + error = col_traverse_collection(dispatcher->sink_list, + COL_TRAVERSE_ONELEVEL, + elapi_internal_sink_handler, + (void *)(&sink_env)); + + TRACE_FLOW_NUMBER("elapi_dsp_log Exit. Returning", error); + return error; +} + +/* Initializes default internal template */ +int elapi_set_default_template(unsigned base, ...) +{ + int error = EOK; + struct collection_item *tpl = NULL; + va_list args; + + TRACE_FLOW_STRING("elapi_set_default_template", "Entry"); + + if (global_dispatcher == NULL) elapi_init(NULL, NULL); + + /* Clean previous instance of the default template */ + elapi_destroy_event_template(global_dispatcher->default_template); + global_dispatcher->default_template = NULL; + + /* Process varible arguments */ + va_start(args, base); + + /* Create template out of base and args */ + error = elapi_create_event_template_with_vargs(&tpl, + base, + args); + va_end(args); + + if (error) { + TRACE_ERROR_NUMBER("Failed to create template. Error", error); + return error; + } + + global_dispatcher->default_template = tpl; + + TRACE_FLOW_STRING("elapi_set_default_template", "Exit"); + return error; +} + +/* There is one default template associated with the dispatcher */ +int elapi_get_default_template(struct collection_item **template) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_get_default_template", "Entry"); + + if ((global_dispatcher == NULL) || + (global_dispatcher->default_template == NULL)) { + TRACE_INFO_STRING("Default template does not exit", ""); + + error = elapi_set_default_template(E_BASE_DEFV1); + if (error) { + TRACE_ERROR_NUMBER("Set default template returned error", error); + return error; + } + } + + *template = global_dispatcher->default_template; + TRACE_FLOW_NUMBER("elapi_get_default_template. Exit returning", error); + return error; +} + + + +/* Function to log raw key value pairs without creating an event */ +int elapi_dsp_msg(struct elapi_dispatcher *dispatcher, + struct collection_item *template, + ...) +{ + int error = EOK; + va_list args; + + TRACE_FLOW_STRING("elapi_dsp_msg", "Entry"); + + va_start(args, template); + + error = elapi_dsp_msg_with_vargs(dispatcher, template, args); + + va_end(args); + + TRACE_FLOW_STRING("elapi_dsp_msg.", "Exit"); + return error; +} + +/********** Advanced dispatcher managment functions **********/ + +/* Managing the sink collection */ +int elapi_alter_dispatcher(struct elapi_dispatcher *dispatcher, + const char *sink, + int action) +{ + + /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */ + return EOK; +} + +/* Get sink list */ +char **elapi_get_sink_list(struct elapi_dispatcher *dispatcher) +{ + + /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */ + return NULL; +} + +/* Free sink list */ +void elapi_free_sink_list(char **sink_list) +{ + + /* FIXME: FUNCTION IS NOT IMPLEMENTED YET */ + +} + + +/******************** High level interface ************************************/ +/* This interface is not thread safe but hides the dispatcher. */ + +/* This function will use internal default template */ +int elapi_create_simple_event(struct collection_item **event, ...) +{ + int error = EOK; + struct collection_item *evt = NULL; + va_list args; + struct collection_item *template = NULL; + + TRACE_FLOW_STRING("elapi_create_simple_event", "Entry"); + + /* Check storage */ + if (event == NULL ) { + TRACE_ERROR_STRING("Event storage must be provided", ""); + return EINVAL; + } + + *event = NULL; + + /* Get default template */ + error = elapi_get_default_template(&template); + if (error) { + TRACE_ERROR_NUMBER("Failed to get default template. Error", error); + return error; + } + + va_start(args, event); + + /* Create event */ + error = elapi_create_event_with_vargs(&evt, + template, + NULL, + 0, + args); + + va_end(args); + + if (error) { + TRACE_ERROR_NUMBER("Failed to create event using arg list. Error", error); + col_destroy_collection(evt); + return error; + } + + *event = evt; + + TRACE_FLOW_STRING("elapi_create_simple_event", "Exit"); + return error; +} + +/* Log key value pairs */ +int elapi_msg(struct collection_item *template, ...) +{ + int error = EOK; + va_list args; + struct collection_item *use_template; + + TRACE_FLOW_STRING("elapi_msg", "Entry"); + + if (!template) { + /* Get default template */ + error = elapi_get_default_template(&use_template); + if (error) { + TRACE_ERROR_NUMBER("Failed to get default template. Error", error); + return error; + } + } + else use_template = template; + + va_start(args, template); + + error = elapi_dsp_msg_with_vargs(global_dispatcher, use_template, args); + + va_end(args); + + TRACE_FLOW_NUMBER("elapi_msg Exit:", error); + return error; +} + +/* Log event */ +int elapi_log(struct collection_item *event) +{ + int error; + + TRACE_FLOW_STRING("elapi_log", "Entry"); + + /* If dispatcher was not initialized do it automatically */ + if (global_dispatcher == NULL) elapi_init(NULL, NULL); + error = elapi_dsp_log(global_dispatcher, event); + + TRACE_FLOW_NUMBER("elapi_log Exit:", error); + return error; +} + +/* Get dispatcher if you want to add sink to a default dispatcher or do some advanced operations */ +struct elapi_dispatcher *elapi_get_dispatcher(void) +{ + TRACE_FLOW_STRING("elapi_get_dispatcher was called.", "Returning default dispatcher."); + return global_dispatcher; + +} + +/* Close ELAPI */ +void elapi_close(void) +{ + TRACE_FLOW_STRING("elapi_close","Entry"); + + /* Destroy global dispatcher */ + elapi_destroy_dispatcher(global_dispatcher); + global_dispatcher = NULL; + + TRACE_FLOW_STRING("elapi_close","Exit"); +} + +/* Function to initialize ELAPI library in the single threaded applications */ +int elapi_init(const char *appname, const char *config_path) +{ + int error = EOK; + + TRACE_FLOW_STRING("elapi_init","Entry"); + + /* Clean the dispatcher if needed */ + elapi_close(); + + /* Create global dispatcher */ + error = elapi_create_dispatcher(&global_dispatcher, appname, config_path); + if (error) { + TRACE_ERROR_NUMBER("Failed to create default dispatcher. Error", error); + return error; + } + + /* Install a cleanup callback */ + if (!elapi_close_registered) { + if (atexit(elapi_close)) { + TRACE_ERROR_NUMBER("Failed to install cleanup callback. Error", ENOSYS); + /* NOTE: Could not find a better error for this case */ + return ENOSYS; + } + elapi_close_registered = 1; + } + + TRACE_FLOW_NUMBER("elapi_init Exit:",error); + return error; +} -- cgit