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

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