/*
* 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
* @ingroup logging
*/
/**@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"
#define PYFCGI_SYSLOG_IDENT_FMT "pyfcgi(%d)[%s]"
#define PYFCGI_SYSLOG_IDENT_FMT_SHORT "pyfcgi(%d)"
/**@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(const char*);
/**@brief Stop & free an individual logger */
void _pyfcgi_logger_free(pyfcgi_logger_t*);
/**@brief Add a new logger
* @param char* filename or NULL if stderr (fd 2 ) wanted
* @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