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 13KB

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