440 lines
12 KiB
C
440 lines
12 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef __LOGGER_H___
|
|
#define __LOGGER_H___
|
|
|
|
|
|
#include "config.h"
|
|
#include <syslog.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
/**@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 7
|
|
#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
|