/* * Copyright (C) 2019 Weber Yann * * This file is part of PyFCGI. * * PyFCGI is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * PyFCGI 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PyFCGI. If not, see . */ #ifndef __LOGGER_H___ #define __LOGGER_H___ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include /**@file logger.h * @brief PyFCGI logging facility * @ingroup conf_logger */ /**@defgroup conf_logger Logging configuration * @ingroup conf_internal */ /**@defgroup log_facility Logger custom facilities */ /**@ingroup log_facility */ #define LOG_GLOBAL 0 /**@ingroup log_facility */ #define LOG_ACCESS 1 /**@ingroup log_facility */ #define LOG_INTERN 2 /**@ingroup log_facility */ #define LOG_WORKER 4 /**@ingroup log_facility */ #define LOG_MASTER 8 #define PYFCGI_LOGGER_MAX 64 #define PYFCGI_LOGGER_FMT_MAX PYFCGI_LOGGER_MAX #define PYFCGI_LOGGER_FIELD_MAX 24 #define PYFCGI_LOGGER_FMT_PARSE_ERRSZ 64 /**@brief The size of the @ref struct pyfcgi_logger_format_t.buf allocated to * the message field */ #define PYFCGI_LOGGER_MSG_BUF_SZ 64 #define PYFCGI_LOG_DTM_LEN 25 #define PYFCGI_LOG_LVL_LEN 7 #define PYFCGI_LOG_TYP_LEN 7 #define PYFCGI_LOG_PID_LEN 6 #define PYFCGI_LOG_PID_FMT "%6d" #define SYSLOG_syslog syslog #define SYSLOG_vsyslog vsyslog #define SYSLOG_EMERG LOG_EMERG #define SYSLOG_ALERT LOG_ALERT #define SYSLOG_CRIT LOG_CRIT #define SYSLOG_ERR LOG_ERR #define SYSLOG_WARNING LOG_WARNING #define SYSLOG_NOTICE LOG_NOTICE #define SYSLOG_INFO LOG_INFO #define SYSLOG_DEBUG LOG_DEBUG #undef LOG_EMERG #undef LOG_ALERT #undef LOG_CRIT #undef LOG_ERR #undef LOG_WARNING #undef LOG_NOTICE #undef LOG_INFO #undef LOG_DEBUG /* This way we can mimic syslog and the or'ed level | facility */ //#define LOG_EMERG (1 << 4) #define LOG_EMERG (2 << 4) #define LOG_ALERT (2 << 4) #define LOG_CRIT (3 << 4) #define LOG_ERR (4 << 4) #define LOG_WARNING (5 << 4) #define LOG_NOTICE (6 << 4) #define LOG_INFO (7 << 4) #define LOG_DEBUG (8 << 4) /**@brief Convert a PyFCGI loglevel in a syslog one */ #define PYFCGI_SYSLOG_LVL(lvl) (((lvl & 0xF0) >> 4)-1) /**@brief Macro checking if syslog is activated and log a message * @params int lvl the PyFCGI log level * @param char* fmt the message format * @see _vsyslog syslog */ #define _syslog(lvl, ...) if(conf->flags & PYFCGI_LOG_FSYSLOG) { \ syslog(PYFCGI_SYSLOG_LVL(lvl), __VA_ARGS__);\ } #define _vsyslog(lvl, fmt, ap) if(conf->flags & PYFCGI_LOG_FSYSLOG) { \ vsyslog(PYFCGI_SYSLOG_LVL(lvl), fmt, ap);\ } /**@brief Macro checking if syslog is activated and log a message using PyFCGI * loglevels * @params int lvl the PyFCGI log level * @param char* fmt the message format * @see vsyslog _syslog */ /**@defgroup conf_logger_flags Logger confifurations flags * @ingroup conf_logger */ /**@ingroup cong_logger_flags */ #define PYFCGI_LOG_FSYSLOG 1 /**@brief Indicate if the logger should try to reopen on failure * @warn not implemented * @ingroup cong_logger_flags */ #define PYFCGI_LOG_FRETRY 2 /**@brief Exit if failure * @warn not implemented * @ingroup cong_logger_flags */ #define PYFCGI_LOG_FEXIT_ONFAIL 4 #define PYFCGI_LOGGER_FMT_DEFAULT "{datetime} {ident}[{pid}] {level} {msg}" #define PYFCGI_LOGGER_TIME_FMT_DEFAULT "%F %T%z" /**@brief Log level mask * @ingroup conf_logger * Allow selection of loglevels using a bitwise mask 1 << LEVEL */ typedef unsigned char logmask_t; typedef unsigned char loglvl_t; typedef struct pyfcgi_logger_s pyfcgi_logger_t; typedef struct pyfcgi_logger_format_s pyfcgi_logger_format_t; typedef struct pyfcgi_logger_fmt_field_s pyfcgi_logger_fmt_field_t; typedef union pyfcgi_logger_fmt_field_u pyfcgi_logger_fmt_value_t; typedef enum pyfcgi_logger_field_type pyfcgi_logger_field_type_e; typedef struct strftime_args_s strftime_args_t; typedef struct ident_args_s ident_args_t; typedef union pyfcgi_logger_field_args pyfcgi_logger_field_args_u; static const char* PYFCGI_LOGGER_LVLNAME[] __attribute__((unused)) = { " Emerge", " Alert", " Critic", " Error", "Warning", " Notice", " Info", " Debug"}; static const char* PYFCGI_LOGGER_TYPNAME[] __attribute__((unused)) = {"Global", "Access", "Internal", "Worker", "Master"} ; /**@defgroup conf_logger_format Logline format * @ingroup conf_logger * A small description langage allows to describe wanted logline format. * A format is a string with special markup indicating fields. Fields markup * are surrounded by '{' and '}' chr. This markup can be divided in multiple * subfields separated by ':' chr, but the first field is always the field * name. * * Valid fields are : * - {datetime:SIZE:FMT} defines a format and a constant length for date * string. By default %F %T%z ISO 8601 date format + time + tz * see @ref PYFCGI_LOGGER_TIME_FMT_DEFAULT * - {level} the loglevel (with a constant length of 7) * - {facility} the log facility (constant length od 6) * - {pid:SIZE} PID with a constant length for pid field * - {ident} the defined ident (size is processed when set) * - {msg} the log message (can only appears once) * * @note You can escape '{' and '}' by using '{{' and '}}' * @note There is a maximum of 24 fields by format (see * @note All fields can be abbreviate to one character * @ref PYFCGI_LOGGER_FIELD_MAX ) */ struct strftime_args_s { //char **s; // field.buf_ptr //size_t max; // field.len char *format; //const struct tm *tm; // fecthed each time }; struct ident_args_s { size_t len; }; union pyfcgi_logger_field_args { strftime_args_t datetime; ident_args_t ident; }; /**@brief Logger format field's type * @ingroup conf_logger_format */ enum pyfcgi_logger_field_type { pyfcgi_logger_field_null = 0, pyfcgi_logger_field_const = -1, pyfcgi_logger_field_datetime = 1, pyfcgi_logger_field_level = 2, pyfcgi_logger_field_facility = 3, pyfcgi_logger_field_pid = 4, pyfcgi_logger_field_ppid = 5, pyfcgi_logger_field_ident = 6, pyfcgi_logger_field_msg = 7, }; /**@brief Logger format field data * @ingroup conf_logger_format */ struct pyfcgi_logger_fmt_field_s { pyfcgi_logger_field_type_e type; short known_length; size_t len; /** @brief pointer on value (interpreted given field type) */ void *val; pyfcgi_logger_field_args_u args; /**@brief Points in prefix or sufix buffer */ char *buf_ptr; /**@brief Field offset in msg buffer (prefix or sufix) */ size_t buf_off; /**@brief 1 if in suffix else 0 */ short suff; }; /**@brief Logger format data * @ingroup conf_logger_format */ struct pyfcgi_logger_format_s { /**@brief String copy of the format */ char *fmt; /**@brief Stores data about fields */ pyfcgi_logger_fmt_field_t fields[PYFCGI_LOGGER_FIELD_MAX]; /**@brief field count */ int nfield; /**@brief Preallocated buffer for prefix & suffix */ char *buf; size_t buflen[2]; /**@brief Message prefix */ char *prefix; /**@brief Message suffix */ char *suffix; char *_msg; size_t _msglen; }; /**@brief Informations on a logger * @ingroup conf_logger */ struct pyfcgi_logger_s { char *filename; int fd; logmask_t loglvl; logmask_t logtyp; size_t fmt_id; }; /**@brief Logger configuration * @ingroup conf_logger */ struct pyfcgi_conf_logger_s { /**@brief Or combination of @ref PYFCGI_LOG_SYSLOG or * @ref PYFCGI_LOG_RETRY */ short flags; char *syslog_ident; int syslog_facility; logmask_t syslog_loglvl; logmask_t syslog_logtyp; /**@brief PyFCGI internal ident, prefixes all log messages */ char *ident; logmask_t facility; pyfcgi_logger_t loggers[PYFCGI_LOGGER_MAX]; unsigned char logger_sz; pyfcgi_logger_format_t formats[PYFCGI_LOGGER_MAX]; //char **format; unsigned char format_sz; /**@brief Buffer for message field formating */ char *msg_buf; size_t msg_buf_sz; /**@brief Internal pipe to tee(2) the message on loggers (USELESS)*/ int pipes[2]; }; #include "conf.h" /**@brief Initiliaze logger conf */ int pyfcgi_logger_init(); /**@brief Stop & free the logger * @note also free formats */ int pyfcgi_logger_stop(); /**@brief Enable syslog logging with given ident * @param char* ident if NULL use current ident */ void pyfcgi_logger_enable_syslog(char*); /**@brief Stop & free an individual logger */ void _pyfcgi_logger_free(pyfcgi_logger_t*); /**@brief Add a new logger * @param char* filename * @param logmask_t loglvl a mask indicating wich loglevels should be logged * @param logmask_t typemask a mask indicating wich facility should be logged * @param char* log format (or NULL for default format) */ int pyfcgi_logger_add(const char*, logmask_t, logmask_t, const char*); /**@brief Add a new format * @param char *format * @param size_t* idx if not NULL, will contain the format index * @return 0 if OK */ int pyfcgi_logger_format_add(const char*, size_t*); /**@brief Parse a format string and populate corresponding * @ref struct pyfgci_logger_format_s * @param const char* fmt string * @param pyfcgi_logger_format_t* fmt_data * @return 0 if no errors */ int pyfcgi_logger_parse_format(const char*, pyfcgi_logger_format_t*); /**@brief Initialize a pyfcgi_logger_format_t.buf attribute (to init the * prefix & sufix attributes. * @note have to be called when ident is updated * @param pyfcgi_logger_format_t* fmt * @note called by @ref pyfcgi_logger_add_format but should be called * by @ref pyfcgi_logger_parse_format */ int pyfcgi_logger_format_bufinit(pyfcgi_logger_format_t*); /**@brief Parse a field string and populate corresponding * @ret struct pyfcgi_logger_field_s * @param const char ** ptr on current format pointer * @param char * if not NULL and parse error occurs, this string * will be set * @return 0 if no errors and 1 if parse error */ int pyfcgi_logger_parse_field(const char**, const char *, pyfcgi_logger_fmt_field_t*, char[PYFCGI_LOGGER_FMT_PARSE_ERRSZ]); /**@brief Free allocated memory in given field * @param pyfcgi_logger_fmt_field_t* field to free */ void pyfcgi_logger_field_free(pyfcgi_logger_fmt_field_t*); /**@brief Given a field pointer return the size option incrementing * ptr * @warning do not check if the next character is OK, the caller has to * do it * @param char** pointer on format * @param size_t* size if found will be set to size option. if not found set to * 0 * @return 0 if no error */ int pyfcgi_logger_parse_field_sz(const char**, size_t*); /**@brief Alloc memory by parsing & strdup the strftime format found * in field. If not found use default format * @note set the @ref struct pyfcgi_logger_fmt_field_s.args field * @param const char** ptr * @param char** format * @return */ int pyfcgi_logger_parse_field_dtfmt(const char**, char**); /**@brief Return a str repr of given loglevel * @warning returned pointer must NOT be freed or modified * @return The loglevel name or NULL if error */ const char* pyfcgi_logger_value_level(short); const char* pyfcgi_logger_value_facility(short); /**@brief Open a logger * @param pyfcgi_logger_t * @return 0 if no errors */ int pyfcgi_logger_open(pyfcgi_logger_t*); /**@brief Set programm identity for logger * @note ident is global to a process * @note this function triggers the refresh of the loggers internal * buffers if needed (see @ref pyfcgi_logger_format_bufinit() ) * @param const char* new_ident * @return 0 if no error */ int pyfcgi_logger_set_ident(const char*); /**@brief Format a message using a @ref struct pyfcgi_logger_format_s * @note alloc and write the logline in the attribute _msg * @param pyfcgi_logger_format_t the pyfcgi logger format structure * @param loglvl_t the loglevel (see @ref pyfcgi_log() ) * @param char* message the message sent by user * @param size_t len the message len * @return NULL if error */ char* vpyfcgi_logger_format_message(pyfcgi_logger_format_t *fmt, loglvl_t lvl, const char* message, size_t len); int pyfcgi_log(loglvl_t, const char*, ...); int vpyfcgi_log(loglvl_t, const char*, va_list); #endif