Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

logger.h 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*
  2. * Copyright (C) 2019 Weber Yann
  3. *
  4. * This file is part of PyFCGI.
  5. *
  6. * PyFCGI is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * any later version.
  10. *
  11. * PyFCGI is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PyFCGI. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef __LOGGER_H___
  20. #define __LOGGER_H___
  21. #include "config.h"
  22. #include <syslog.h>
  23. #include <stdlib.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include <stdarg.h>
  27. #include <unistd.h>
  28. #include <fcntl.h>
  29. #include <stdio.h>
  30. #include <time.h>
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. /**@file logger.h
  34. * @brief PyFCGI logging facility
  35. * @ingroup conf_logger
  36. */
  37. /**@defgroup conf_logger Logging configuration
  38. * @ingroup conf_internal
  39. */
  40. /**@defgroup log_facility Logger custom facilities */
  41. /**@ingroup log_facility */
  42. #define LOG_GLOBAL 0
  43. /**@ingroup log_facility */
  44. #define LOG_ACCESS 1
  45. /**@ingroup log_facility */
  46. #define LOG_INTERN 2
  47. /**@ingroup log_facility */
  48. #define LOG_WORKER 4
  49. /**@ingroup log_facility */
  50. #define LOG_MASTER 8
  51. #define PYFCGI_LOGGER_MAX 64
  52. #define PYFCGI_LOGGER_FMT_MAX PYFCGI_LOGGER_MAX
  53. #define PYFCGI_LOGGER_FIELD_MAX 24
  54. #define PYFCGI_LOGGER_FMT_PARSE_ERRSZ 64
  55. /**@brief The size of the @ref struct pyfcgi_logger_format_t.buf allocated to
  56. * the message field */
  57. #define PYFCGI_LOGGER_MSG_BUF_SZ 64
  58. #define PYFCGI_LOG_DTM_LEN 25
  59. #define PYFCGI_LOG_LVL_LEN 7
  60. #define PYFCGI_LOG_TYP_LEN 7
  61. #define PYFCGI_LOG_PID_LEN 6
  62. #define PYFCGI_LOG_PID_FMT "%6d"
  63. #define SYSLOG_syslog syslog
  64. #define SYSLOG_vsyslog vsyslog
  65. #define SYSLOG_EMERG LOG_EMERG
  66. #define SYSLOG_ALERT LOG_ALERT
  67. #define SYSLOG_CRIT LOG_CRIT
  68. #define SYSLOG_ERR LOG_ERR
  69. #define SYSLOG_WARNING LOG_WARNING
  70. #define SYSLOG_NOTICE LOG_NOTICE
  71. #define SYSLOG_INFO LOG_INFO
  72. #define SYSLOG_DEBUG LOG_DEBUG
  73. #undef LOG_EMERG
  74. #undef LOG_ALERT
  75. #undef LOG_CRIT
  76. #undef LOG_ERR
  77. #undef LOG_WARNING
  78. #undef LOG_NOTICE
  79. #undef LOG_INFO
  80. #undef LOG_DEBUG
  81. /* This way we can mimic syslog and the or'ed level | facility */
  82. //#define LOG_EMERG (1 << 4)
  83. #define LOG_EMERG (2 << 4)
  84. #define LOG_ALERT (2 << 4)
  85. #define LOG_CRIT (3 << 4)
  86. #define LOG_ERR (4 << 4)
  87. #define LOG_WARNING (5 << 4)
  88. #define LOG_NOTICE (6 << 4)
  89. #define LOG_INFO (7 << 4)
  90. #define LOG_DEBUG (8 << 4)
  91. /**@brief Convert a PyFCGI loglevel in a syslog one */
  92. #define PYFCGI_SYSLOG_LVL(lvl) (((lvl & 0xF0) >> 4)-1)
  93. /**@brief Macro checking if syslog is activated and log a message
  94. * @params int lvl the PyFCGI log level
  95. * @param char* fmt the message format
  96. * @see _vsyslog syslog
  97. */
  98. #define _syslog(lvl, ...) if(conf->flags & PYFCGI_LOG_FSYSLOG) { \
  99. syslog(PYFCGI_SYSLOG_LVL(lvl), __VA_ARGS__);\
  100. }
  101. #define _vsyslog(lvl, fmt, ap) if(conf->flags & PYFCGI_LOG_FSYSLOG) { \
  102. vsyslog(PYFCGI_SYSLOG_LVL(lvl), fmt, ap);\
  103. }
  104. /**@brief Macro checking if syslog is activated and log a message using PyFCGI
  105. * loglevels
  106. * @params int lvl the PyFCGI log level
  107. * @param char* fmt the message format
  108. * @see vsyslog _syslog
  109. */
  110. /**@defgroup conf_logger_flags Logger confifurations flags
  111. * @ingroup conf_logger */
  112. /**@ingroup cong_logger_flags */
  113. #define PYFCGI_LOG_FSYSLOG 1
  114. /**@brief Indicate if the logger should try to reopen on failure
  115. * @warn not implemented
  116. * @ingroup cong_logger_flags */
  117. #define PYFCGI_LOG_FRETRY 2
  118. /**@brief Exit if failure
  119. * @warn not implemented
  120. * @ingroup cong_logger_flags */
  121. #define PYFCGI_LOG_FEXIT_ONFAIL 4
  122. #define PYFCGI_LOGGER_FMT_DEFAULT "{datetime} {ident}[{pid}] {level} {msg}"
  123. #define PYFCGI_LOGGER_TIME_FMT_DEFAULT "%F %T%z"
  124. /**@brief Log level mask
  125. * @ingroup conf_logger
  126. * Allow selection of loglevels using a bitwise mask 1 << LEVEL
  127. */
  128. typedef unsigned char logmask_t;
  129. typedef unsigned char loglvl_t;
  130. typedef struct pyfcgi_logger_s pyfcgi_logger_t;
  131. typedef struct pyfcgi_logger_format_s pyfcgi_logger_format_t;
  132. typedef struct pyfcgi_logger_fmt_field_s pyfcgi_logger_fmt_field_t;
  133. typedef union pyfcgi_logger_fmt_field_u pyfcgi_logger_fmt_value_t;
  134. typedef enum pyfcgi_logger_field_type pyfcgi_logger_field_type_e;
  135. typedef struct strftime_args_s strftime_args_t;
  136. typedef struct ident_args_s ident_args_t;
  137. typedef union pyfcgi_logger_field_args pyfcgi_logger_field_args_u;
  138. static const char* PYFCGI_LOGGER_LVLNAME[] __attribute__((unused)) = {
  139. " Emerge",
  140. " Alert",
  141. " Critic",
  142. " Error",
  143. "Warning",
  144. " Notice",
  145. " Info",
  146. " Debug"};
  147. static const char* PYFCGI_LOGGER_TYPNAME[] __attribute__((unused)) =
  148. {"Global", "Access", "Internal", "Worker", "Master"} ;
  149. /**@defgroup conf_logger_format Logline format
  150. * @ingroup conf_logger
  151. * A small description langage allows to describe wanted logline format.
  152. * A format is a string with special markup indicating fields. Fields markup
  153. * are surrounded by '{' and '}' chr. This markup can be divided in multiple
  154. * subfields separated by ':' chr, but the first field is always the field
  155. * name.
  156. *
  157. * Valid fields are :
  158. * - {datetime:SIZE:FMT} defines a format and a constant length for date
  159. * string. By default %F %T%z ISO 8601 date format + time + tz
  160. * see @ref PYFCGI_LOGGER_TIME_FMT_DEFAULT
  161. * - {level} the loglevel (with a constant length of 7)
  162. * - {facility} the log facility (constant length od 6)
  163. * - {pid:SIZE} PID with a constant length for pid field
  164. * - {ident} the defined ident (size is processed when set)
  165. * - {msg} the log message (can only appears once)
  166. *
  167. * @note You can escape '{' and '}' by using '{{' and '}}'
  168. * @note There is a maximum of 24 fields by format (see
  169. * @note All fields can be abbreviate to one character
  170. * @ref PYFCGI_LOGGER_FIELD_MAX )
  171. */
  172. struct strftime_args_s
  173. {
  174. //char **s; // field.buf_ptr
  175. //size_t max; // field.len
  176. char *format;
  177. //const struct tm *tm; // fecthed each time
  178. };
  179. struct ident_args_s
  180. {
  181. size_t len;
  182. };
  183. union pyfcgi_logger_field_args
  184. {
  185. strftime_args_t datetime;
  186. ident_args_t ident;
  187. };
  188. /**@brief Logger format field's type
  189. * @ingroup conf_logger_format */
  190. enum pyfcgi_logger_field_type
  191. {
  192. pyfcgi_logger_field_null = 0,
  193. pyfcgi_logger_field_const = -1,
  194. pyfcgi_logger_field_datetime = 1,
  195. pyfcgi_logger_field_level = 2,
  196. pyfcgi_logger_field_facility = 3,
  197. pyfcgi_logger_field_pid = 4,
  198. pyfcgi_logger_field_ppid = 5,
  199. pyfcgi_logger_field_ident = 6,
  200. pyfcgi_logger_field_msg = 7,
  201. };
  202. /**@brief Logger format field data
  203. * @ingroup conf_logger_format */
  204. struct pyfcgi_logger_fmt_field_s
  205. {
  206. pyfcgi_logger_field_type_e type;
  207. short known_length;
  208. size_t len;
  209. /** @brief pointer on value (interpreted given field type) */
  210. void *val;
  211. pyfcgi_logger_field_args_u args;
  212. /**@brief Points in prefix or sufix buffer */
  213. char *buf_ptr;
  214. /**@brief Field offset in msg buffer (prefix or sufix) */
  215. size_t buf_off;
  216. /**@brief 1 if in suffix else 0 */
  217. short suff;
  218. };
  219. /**@brief Logger format data
  220. * @ingroup conf_logger_format */
  221. struct pyfcgi_logger_format_s
  222. {
  223. /**@brief String copy of the format */
  224. char *fmt;
  225. /**@brief Stores data about fields */
  226. pyfcgi_logger_fmt_field_t fields[PYFCGI_LOGGER_FIELD_MAX];
  227. /**@brief field count */
  228. int nfield;
  229. /**@brief Preallocated buffer for prefix & suffix */
  230. char *buf;
  231. size_t buflen[2];
  232. /**@brief Message prefix */
  233. char *prefix;
  234. /**@brief Message suffix */
  235. char *suffix;
  236. char *_msg;
  237. size_t _msglen;
  238. };
  239. /**@brief Informations on a logger
  240. * @ingroup conf_logger
  241. */
  242. struct pyfcgi_logger_s
  243. {
  244. char *filename;
  245. int fd;
  246. logmask_t loglvl;
  247. logmask_t logtyp;
  248. size_t fmt_id;
  249. };
  250. /**@brief Logger configuration
  251. * @ingroup conf_logger
  252. */
  253. struct pyfcgi_conf_logger_s
  254. {
  255. /**@brief Or combination of @ref PYFCGI_LOG_SYSLOG or
  256. * @ref PYFCGI_LOG_RETRY */
  257. short flags;
  258. char *syslog_ident;
  259. int syslog_facility;
  260. logmask_t syslog_loglvl;
  261. logmask_t syslog_logtyp;
  262. /**@brief PyFCGI internal ident, prefixes all log messages */
  263. char *ident;
  264. logmask_t facility;
  265. pyfcgi_logger_t loggers[PYFCGI_LOGGER_MAX];
  266. unsigned char logger_sz;
  267. pyfcgi_logger_format_t formats[PYFCGI_LOGGER_MAX];
  268. //char **format;
  269. unsigned char format_sz;
  270. /**@brief Buffer for message field formating */
  271. char *msg_buf;
  272. size_t msg_buf_sz;
  273. /**@brief Internal pipe to tee(2) the message on loggers (USELESS)*/
  274. int pipes[2];
  275. };
  276. #include "conf.h"
  277. /**@brief Initiliaze logger conf */
  278. int pyfcgi_logger_init();
  279. /**@brief Stop & free the logger
  280. * @note also free formats
  281. */
  282. int pyfcgi_logger_stop();
  283. /**@brief Enable syslog logging with given ident
  284. * @param char* ident if NULL use current ident
  285. */
  286. void pyfcgi_logger_enable_syslog(const char*);
  287. /**@brief Stop & free an individual logger */
  288. void _pyfcgi_logger_free(pyfcgi_logger_t*);
  289. /**@brief Add a new logger
  290. * @param char* filename
  291. * @param logmask_t loglvl a mask indicating wich loglevels should be logged
  292. * @param logmask_t typemask a mask indicating wich facility should be logged
  293. * @param char* log format (or NULL for default format)
  294. */
  295. int pyfcgi_logger_add(const char*, logmask_t, logmask_t, const char*);
  296. /**@brief Add a new format
  297. * @param char *format
  298. * @param size_t* idx if not NULL, will contain the format index
  299. * @return 0 if OK
  300. */
  301. int pyfcgi_logger_format_add(const char*, size_t*);
  302. /**@brief Parse a format string and populate corresponding
  303. * @ref struct pyfgci_logger_format_s
  304. * @param const char* fmt string
  305. * @param pyfcgi_logger_format_t* fmt_data
  306. * @return 0 if no errors
  307. */
  308. int pyfcgi_logger_parse_format(const char*, pyfcgi_logger_format_t*);
  309. /**@brief Initialize a pyfcgi_logger_format_t.buf attribute (to init the
  310. * prefix & sufix attributes.
  311. * @note have to be called when ident is updated
  312. * @param pyfcgi_logger_format_t* fmt
  313. * @note called by @ref pyfcgi_logger_add_format but should be called
  314. * by @ref pyfcgi_logger_parse_format
  315. */
  316. int pyfcgi_logger_format_bufinit(pyfcgi_logger_format_t*);
  317. /**@brief Parse a field string and populate corresponding
  318. * @ret struct pyfcgi_logger_field_s
  319. * @param const char ** ptr on current format pointer
  320. * @param char * if not NULL and parse error occurs, this string
  321. * will be set
  322. * @return 0 if no errors and 1 if parse error
  323. */
  324. int pyfcgi_logger_parse_field(const char**, const char *,
  325. pyfcgi_logger_fmt_field_t*,
  326. char[PYFCGI_LOGGER_FMT_PARSE_ERRSZ]);
  327. /**@brief Free allocated memory in given field
  328. * @param pyfcgi_logger_fmt_field_t* field to free
  329. */
  330. void pyfcgi_logger_field_free(pyfcgi_logger_fmt_field_t*);
  331. /**@brief Given a field pointer return the size option incrementing
  332. * ptr
  333. * @warning do not check if the next character is OK, the caller has to
  334. * do it
  335. * @param char** pointer on format
  336. * @param size_t* size if found will be set to size option. if not found set to
  337. * 0
  338. * @return 0 if no error
  339. */
  340. int pyfcgi_logger_parse_field_sz(const char**, size_t*);
  341. /**@brief Alloc memory by parsing & strdup the strftime format found
  342. * in field. If not found use default format
  343. * @note set the @ref struct pyfcgi_logger_fmt_field_s.args field
  344. * @param const char** ptr
  345. * @param char** format
  346. * @return
  347. */
  348. int pyfcgi_logger_parse_field_dtfmt(const char**, char**);
  349. /**@brief Return a str repr of given loglevel
  350. * @warning returned pointer must NOT be freed or modified
  351. * @return The loglevel name or NULL if error
  352. */
  353. const char* pyfcgi_logger_value_level(short);
  354. const char* pyfcgi_logger_value_facility(short);
  355. /**@brief Open a logger
  356. * @param pyfcgi_logger_t
  357. * @return 0 if no errors
  358. */
  359. int pyfcgi_logger_open(pyfcgi_logger_t*);
  360. /**@brief Set programm identity for logger
  361. * @note ident is global to a process
  362. * @note this function triggers the refresh of the loggers internal
  363. * buffers if needed (see @ref pyfcgi_logger_format_bufinit() )
  364. * @param const char* new_ident
  365. * @return 0 if no error
  366. */
  367. int pyfcgi_logger_set_ident(const char*);
  368. /**@brief Format a message using a @ref struct pyfcgi_logger_format_s
  369. * @note alloc and write the logline in the attribute _msg
  370. * @param pyfcgi_logger_format_t the pyfcgi logger format structure
  371. * @param loglvl_t the loglevel (see @ref pyfcgi_log() )
  372. * @param char* message the message sent by user
  373. * @param size_t len the message len
  374. * @return NULL if error
  375. */
  376. char* vpyfcgi_logger_format_message(pyfcgi_logger_format_t *fmt,
  377. loglvl_t lvl, const char* message, size_t len);
  378. int pyfcgi_log(loglvl_t, const char*, ...);
  379. int vpyfcgi_log(loglvl_t, const char*, va_list);
  380. #endif