Browse Source

Starts implementation of logger format parsing

Yann Weber 4 years ago
parent
commit
8cba8d40ba
4 changed files with 386 additions and 8 deletions
  1. 5
    1
      include/conf.h
  2. 142
    5
      include/logger.h
  3. 238
    2
      src/logger.c
  4. 1
    0
      src/pyworker.c

+ 5
- 1
include/conf.h View File

@@ -26,6 +26,8 @@
26 26
  * @see struct_pyfcgi_conf_s
27 27
  * @ingroup conf_internal */
28 28
 
29
+#include "logger.h"
30
+
29 31
 /**@defgroup ret_status Function & process return status
30 32
  */
31 33
 #define PYFCGI_ERR 16
@@ -43,7 +45,8 @@ typedef struct pyfcgi_conf_logger_s pyfcgi_conf_logger_t;
43 45
 typedef struct pyfcgi_context_s pyfcgi_context_t;
44 46
 
45 47
 struct pyfcgi_context_s {
46
-	int foo;
48
+	pid_t pid;
49
+	pid_t ppid;
47 50
 };
48 51
 
49 52
 /**@brief Structure containing configuration
@@ -73,6 +76,7 @@ struct pyfcgi_conf_s
73 76
 	 * @ingroupe conf_glob */
74 77
 	pyfcgi_conf_logger_t logs;
75 78
 
79
+	/**@brief Context informations */
76 80
 	pyfcgi_context_t context;
77 81
 };
78 82
 

+ 142
- 5
include/logger.h View File

@@ -28,6 +28,7 @@
28 28
 #include <unistd.h>
29 29
 #include <fcntl.h>
30 30
 #include <stdio.h>
31
+#include <time.h>
31 32
 #include <sys/types.h>
32 33
 #include <sys/stat.h>
33 34
 
@@ -51,6 +52,9 @@
51 52
 /**@ingroup log_facility */
52 53
 #define LOG_MASTER 8
53 54
 
55
+#define PYFCGI_LOGGER_FIELD_MAX 24
56
+#define PYFCGI_LOGGER_FMT_PARSE_ERRSZ 64
57
+
54 58
 #define SYSLOG_syslog syslog
55 59
 #define SYSLOG_vsyslog vsyslog
56 60
 #define SYSLOG_EMERG   LOG_EMERG
@@ -61,8 +65,8 @@
61 65
 #define SYSLOG_NOTICE  LOG_NOTICE
62 66
 #define SYSLOG_INFO    LOG_INFO
63 67
 #define SYSLOG_DEBUG   LOG_DEBUG
64
-const short SYSLOG_LVLS[8] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
65
-                              LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG};
68
+static const short SYSLOG_LVLS[8] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
69
+                                     LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG};
66 70
 
67 71
 #undef LOG_EMERG
68 72
 #undef LOG_ALERT
@@ -116,7 +120,10 @@ const short SYSLOG_LVLS[8] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
116 120
 #define PYFCGI_LOG_FRETRY 2
117 121
 /**@brief Exit if failure
118 122
  * @ingroup cong_logger_flags */
119
-#define PYFCG_LOG_FEXIT_ONFAIL 4
123
+#define PYFCGI_LOG_FEXIT_ONFAIL 4
124
+
125
+#define PYFCGI_LOGGER_TIME_FMT_DEFAULT "%F %T%z"
126
+
120 127
 
121 128
 /**@brief Log level mask
122 129
  * @ingroup conf_logger
@@ -125,6 +132,12 @@ const short SYSLOG_LVLS[8] = {LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR,
125 132
 typedef unsigned char logmask_t;
126 133
 typedef unsigned char loglvl_t;
127 134
 typedef struct pyfcgi_logger_s pyfcgi_logger_t;
135
+typedef struct pyfcgi_logger_format_s pyfcgi_logger_format_t;
136
+typedef struct pyfcgi_logger_fmt_field_s pyfcgi_logger_fmt_field_t;
137
+typedef union pyfcgi_logger_fmt_field_u pyfcgi_logger_fmt_value_t;
138
+typedef enum pyfcgi_logger_field_type pyfcgi_logger_field_type_e;
139
+typedef struct strftime_args_s strftime_args_t;
140
+typedef union pyfcgi_logger_field_args pyfcgi_logger_field_args_u;
128 141
 
129 142
 /**@brief Logger configuration
130 143
  * @ingroup conf_logger
@@ -166,8 +179,92 @@ struct pyfcgi_logger_s
166 179
 	size_t fmt_id;
167 180
 };
168 181
 
182
+/**@defgroup conf_logger_format Logline format
183
+ * @ingroup conf_logger
184
+ * A small description langage allows to describe wanted logline format.
185
+ * A format is a string with special markup indicating fields. Fields markup
186
+ * are surrounded by '{' and '}' chr. This markup can be divided in multiple
187
+ * subfields separated by ':' chr, but the first field is always the field
188
+ * name.
189
+ *
190
+ * Valid fields are :
191
+ * - {datetime:SIZE:FMT} defines a format and a constant length for date
192
+ * string. By default %F %T%z ISO 8601 date format  + time + tz
193
+ * see @ref PYFCGI_LOGGER_TIME_FMT_DEFAULT
194
+ * - {level} the loglevel (with a constant length of 7)
195
+ * - {facility} the log facility (constant length od 6)
196
+ * - {pid:SIZE} PID with a constant length for pid field
197
+ * - {ident} the defined ident (size is processed when set)
198
+ * - {msg} the log message (can only appears once)
199
+ *
200
+ * @note You can escape '{' and '}' by using '{{' and '}}'
201
+ * @note There is a maximum of 24 fields by format (see 
202
+ * @note All fields can be abbreviate to one character
203
+ * @ref PYFCGI_LOGGER_FIELD_MAX )
204
+ */
205
+
206
+struct strftime_args_s
207
+{
208
+	//char **s; // field.buf_ptr
209
+	//size_t max; // field.len
210
+	char *format;
211
+	//const struct tm *tm; // fecthed each time
212
+};
213
+
214
+union pyfcgi_logger_field_args
215
+{
216
+	strftime_args_t datetime;
217
+};
218
+
219
+/**@brief Logger format field's type
220
+ * @ingroup conf_logger_format */
221
+enum pyfcgi_logger_field_type
222
+{
223
+	pyfcgi_logger_field_null = 0,
224
+	pyfcgi_logger_field_const = -1,
225
+	pyfcgi_logger_field_datetime = 1,
226
+	pyfcgi_logger_field_level = 2,
227
+	pyfcgi_logger_field_facility = 3,
228
+	pyfcgi_logger_field_pid = 4,
229
+	pyfcgi_logger_field_ppid = 5,
230
+	pyfcgi_logger_field_ident = 6,
231
+	pyfcgi_logger_field_msg = 7,
232
+};
233
+
234
+/**@brief Logger format field data
235
+ * @ingroup conf_logger_format */
236
+struct pyfcgi_logger_fmt_field_s
237
+{
238
+	pyfcgi_logger_field_type_e type;
239
+
240
+	short known_length;
241
+	size_t len;
242
+
243
+	/** @brief pointer on value (interpreted given field type) */
244
+	void *val;
245
+	pyfcgi_logger_field_args_u args;
246
+	/**@brief Points in prefix or sufix buffer */
247
+	char *buf_ptr;
248
+};
249
+
250
+/**@brief Logger format data
251
+ * @ingroup conf_logger_format */
252
+struct pyfcgi_logger_format_s
253
+{
254
+	/**@brief Stores data about fields */
255
+	pyfcgi_logger_fmt_field_t fields[PYFCGI_LOGGER_FIELD_MAX];
256
+	/**@brief Preallocated buffer for prefix & suffix */
257
+	char *buf;
258
+	/**@brief Message prefix */
259
+	char *prefix;
260
+	/**@brief Message suffix */
261
+	char *suffix;
262
+};
263
+
169 264
 #include "conf.h"
170 265
 
266
+int pyfcgi_logger_init();
267
+
171 268
 /**@brief Add a new logger
172 269
  * @param char* filename
173 270
  * @param logmask_t loglvl a mask indicating wich loglevels should be logged
@@ -181,10 +278,50 @@ int pyfcgi_logger_add(const char*, logmask_t, logmask_t, const char*);
181 278
  * @param size_t* idx if not NULL, will contain the format index
182 279
  * @return 0 if OK
183 280
  */
184
-int pyfcgi_logger_add_format(const char*, size_t*);
281
+int pyfcgi_logger_format_add(const char*, size_t*);
282
+
283
+/**@brief Parse a format string and populate corresponding 
284
+ * @ref struct pyfgci_logger_format_s
285
+ * @param const char* fmt string
286
+ * @param pyfcgi_logger_format_t* fmt_data
287
+ * @return 0 if no errors
288
+ */
289
+int pyfcgi_logger_parse_format(const char*, pyfcgi_logger_format_t*);
290
+
291
+/**@brief Parse a field string and populate corresponding
292
+ * @ret struct pyfcgi_logger_field_s
293
+ * @param const char ** ptr on current format pointer
294
+ * @param char * if not NULL and parse error occurs, this string
295
+ * will be set
296
+ * @return 0 if no errors and 1 if parse error
297
+ */
298
+int pyfcgi_logger_parse_field(const char**, const char *,
299
+                              pyfcgi_logger_fmt_field_t*,
300
+                              char[PYFCGI_LOGGER_FMT_PARSE_ERRSZ]);
301
+
302
+/**@brief Given a field pointer return the size option incrementing
303
+ * ptr
304
+ * @warning do not check if the next character is OK, the caller has to
305
+ * do it
306
+ * @param char** pointer on format
307
+ * @param size_t* size if found will be set to size option. if not found set to
308
+ * 0
309
+ * @return 0 if no error
310
+ */
311
+int pyfcgi_logger_parse_field_sz(const char**, size_t*);
312
+
313
+/**@brief Alloc memory by parsing & strdup the strftime format found
314
+ * in field. If not found use default format
315
+ * @note set the @ref struct pyfcgi_logger_fmt_field_s.args field
316
+ * @param const char** ptr
317
+ * @param char** format
318
+ * @return 
319
+ */
320
+int pyfcgi_logger_parse_field_dtfmt(const char**, char**);
321
+
185 322
 
186 323
 /**@brief Open a logger
187
- * @param pyfcgi_logger_t*
324
+ * @param pyfcgi_logger_t
188 325
  * @return 0 if no errors
189 326
  */
190 327
 int pyfcgi_logger_open(pyfcgi_logger_t*);

+ 238
- 2
src/logger.c View File

@@ -1,5 +1,13 @@
1 1
 #include "logger.h"
2 2
 
3
+int pyfcgi_logger_init()
4
+{
5
+	pyfcgi_conf_logger_t *conf;
6
+	conf = &PyFCGI_conf.logs;
7
+	memset(conf, 0, sizeof(pyfcgi_conf_logger_t));
8
+	return 0;
9
+}
10
+
3 11
 int pyfcgi_logger_add(const char *filename, logmask_t loglvl, logmask_t logtyp,
4 12
                       const char *format)
5 13
 {
@@ -18,12 +26,13 @@ int pyfcgi_logger_add(const char *filename, logmask_t loglvl, logmask_t logtyp,
18 26
 		return PYFCGI_ERR;
19 27
 	}
20 28
 
21
-	if(pyfcgi_logger_add_format(format, &(logger.fmt_id)))
29
+	if(pyfcgi_logger_format_add(format, &(logger.fmt_id)))
22 30
 	{
23 31
 		return PYFCGI_FATAL;
24 32
 	}
25 33
 	if(!logger.filename)
26 34
 	{
35
+		err = errno;
27 36
 		err_fmt = "Unable to duplicate logger filename : %s";
28 37
 		goto err;
29 38
 	}
@@ -53,7 +62,7 @@ err:
53 62
 	return PYFCGI_FATAL;	
54 63
 }
55 64
 
56
-int pyfcgi_logger_add_format(const char* format, size_t* idx)
65
+int pyfcgi_logger_format_add(const char* format, size_t* idx)
57 66
 {
58 67
 	char *fmt;
59 68
 	size_t i;
@@ -90,6 +99,233 @@ int pyfcgi_logger_add_format(const char* format, size_t* idx)
90 99
 	return 0;
91 100
 }
92 101
 
102
+int pyfcgi_logger_parse_format(const char* fmt,
103
+                               pyfcgi_logger_format_t* fmt_data)
104
+{
105
+	const char *ptr, *str_start;
106
+	int err;
107
+	char reason_err[PYFCGI_LOGGER_FMT_PARSE_ERRSZ];
108
+	unsigned char nfield;
109
+	pyfcgi_logger_fmt_field_t *cur_field;
110
+
111
+	memset(fmt_data, 0, sizeof(pyfcgi_logger_format_t));
112
+	ptr = fmt;
113
+	nfield = 0;
114
+	cur_field = fmt_data->fields;
115
+
116
+	while(*ptr)
117
+	{
118
+		if(nfield >= PYFCGI_LOGGER_FIELD_MAX)
119
+		{
120
+			//TODO error truncate !
121
+			break;
122
+		}
123
+		str_start = ptr;
124
+		while(*ptr && *ptr != '{' && *(ptr+1) != '{')
125
+		{
126
+			if(!cur_field->type)
127
+			{
128
+				cur_field->type = pyfcgi_logger_field_const;
129
+				cur_field->known_length = 1;
130
+				cur_field->len = 0;
131
+			}
132
+			if(*ptr == '{' && *(ptr+1) == '{')
133
+			{
134
+				cur_field->len++;
135
+				ptr++;
136
+			}
137
+			cur_field->len++;
138
+			ptr++;
139
+		}
140
+		if(cur_field->len)
141
+		{
142
+			cur_field->val = strndup(str_start, cur_field->len);
143
+			if(!cur_field->val)
144
+			{
145
+				err = errno;
146
+				pyfcgi_log(LOG_ALERT,
147
+				           "Unable to strdup part of format : %s",
148
+					   strerror(err));
149
+				goto exit_err;
150
+			}
151
+			nfield++;
152
+			cur_field = &(fmt_data->fields[nfield]); // next_field
153
+			continue; // to check nfield
154
+		}
155
+		if(*ptr == '{') //should be this or '\0'...
156
+		{
157
+			ptr++;
158
+			if( (1 == pyfcgi_logger_parse_field(&ptr, fmt, 
159
+			                                    cur_field, 
160
+			                                    reason_err)) )
161
+			{
162
+				goto parse_err;
163
+			}
164
+		}
165
+		ptr++;
166
+	};
167
+	
168
+
169
+	return 0;
170
+
171
+parse_err:
172
+	pyfcgi_log(LOG_ERR, "Logger format parse error at chr %ld : %s",
173
+	           ptr - fmt, reason_err);
174
+exit_err:
175
+	//TODO free const str from fields
176
+	memset(fmt_data, 0, sizeof(pyfcgi_logger_format_t));
177
+	return PYFCGI_ERR;
178
+}
179
+
180
+int pyfcgi_logger_parse_field(const char** ptr, const char *start,
181
+                              pyfcgi_logger_fmt_field_t* cur_field,
182
+			      char fail_reason[PYFCGI_LOGGER_FMT_PARSE_ERRSZ])
183
+{
184
+	
185
+	const char *str_start, *field_start;
186
+	int ret;
187
+	char **name, **format;
188
+	size_t default_len;
189
+	pyfcgi_logger_field_type_e *type;
190
+	char *field_names[] = {"datetime", "level", "facility", "pid", "ident",
191
+	                       "msg", NULL};
192
+	pyfcgi_logger_field_type_e field_types[] = {
193
+		pyfcgi_logger_field_datetime, pyfcgi_logger_field_level,
194
+		pyfcgi_logger_field_facility, pyfcgi_logger_field_pid,
195
+		pyfcgi_logger_field_ident, pyfcgi_logger_field_msg};
196
+
197
+	str_start = *ptr;
198
+	field_start = *ptr;
199
+	name = field_names;
200
+	type = field_types;
201
+	while(*name)
202
+	{
203
+		if(**name != **ptr)
204
+		{
205
+			name++;
206
+			type++;
207
+			continue;
208
+		}
209
+		// Name found ?
210
+		while(**ptr && **name && **ptr != ':')
211
+		{
212
+			if(**ptr != **name)
213
+			{
214
+				break;
215
+			}
216
+			(*ptr)++;
217
+			(*name)++;
218
+		}
219
+		if(**ptr == ':' || **ptr == '}')
220
+		{
221
+			cur_field->type = *type;
222
+		}
223
+		else
224
+		{
225
+			snprintf(fail_reason, PYFCGI_LOGGER_FMT_PARSE_ERRSZ,
226
+				 "unknown field type '%s' chr %ld",
227
+				 str_start, str_start - start);
228
+			return 1;
229
+		}
230
+	}
231
+	(*ptr)++; //on field size or ':' or '}'
232
+	str_start = *ptr;
233
+	if(pyfcgi_logger_parse_field_sz(ptr, &(cur_field->len)))
234
+	{
235
+		snprintf(fail_reason, PYFCGI_LOGGER_FMT_PARSE_ERRSZ,
236
+			 "Unable to parse field size chr %ld '%s'",
237
+			 str_start - start, str_start);
238
+		return 1;
239
+	}
240
+	if(**ptr != ':' && **ptr != '}')
241
+	{
242
+		snprintf(fail_reason, PYFCGI_LOGGER_FMT_PARSE_ERRSZ,
243
+			 "Unable to parse field '%s' at chr %ld",
244
+			 field_start , *ptr - start);
245
+		return 1;
246
+	}
247
+	switch(cur_field->type)
248
+	{
249
+		case pyfcgi_logger_field_datetime:
250
+			default_len = 10;
251
+			cur_field->val = (void*)strftime;
252
+			format = &(cur_field->args.datetime.format);
253
+			ret = pyfcgi_logger_parse_field_dtfmt(ptr,format);
254
+			if(ret){
255
+				return ret;
256
+			}
257
+			break;
258
+		case pyfcgi_logger_field_level:
259
+			default_len = 7;
260
+			break;
261
+		case pyfcgi_logger_field_facility:
262
+			default_len = 6;
263
+			break;
264
+		case pyfcgi_logger_field_pid:
265
+			default_len = 5;
266
+			break;
267
+		case pyfcgi_logger_field_ident:
268
+			default_len = 0;
269
+			break;
270
+		case pyfcgi_logger_field_msg:
271
+			default_len = 0;
272
+			break;
273
+		default:
274
+			snprintf(fail_reason, PYFCGI_LOGGER_FMT_PARSE_ERRSZ,
275
+				 "Unknown error parsing field '%s' at chr %ld",
276
+				 field_start , *ptr - start);
277
+			return 1;
278
+	}
279
+	if(!cur_field->len && default_len)
280
+	{
281
+		cur_field->len = default_len;
282
+	}
283
+	return 0;
284
+}
285
+
286
+int pyfcgi_logger_parse_field_dtfmt(const char** ptr, char** format)
287
+{
288
+	const char *fmt;
289
+	size_t fmt_len;
290
+
291
+	fmt_len = 0;
292
+	fmt = *ptr;
293
+printf("FUCKOFF");
294
+	while(**ptr && **ptr != '}' && *((*ptr)+1) != '}')
295
+	{
296
+		ptr++;
297
+		fmt_len++;
298
+	}
299
+	if(!fmt_len)
300
+	{
301
+		fmt_len = sizeof(PYFCGI_LOGGER_TIME_FMT_DEFAULT);
302
+		fmt = PYFCGI_LOGGER_TIME_FMT_DEFAULT;
303
+	}
304
+	if(!(*format = strndup(fmt, fmt_len)))
305
+	{
306
+		pyfcgi_log(LOG_ALERT,
307
+		           "Unable to strdup field datetime format : %s",
308
+			   strerror(errno));
309
+		return PYFCGI_FATAL;
310
+	}
311
+	return 0;
312
+}
313
+
314
+int pyfcgi_logger_parse_field_sz(const char **ptr, size_t *size)
315
+{
316
+	char *end;
317
+	if(**ptr == '}' || **ptr == ':')
318
+	{
319
+		return (*size = 0);
320
+	}
321
+	if(**ptr < '0' && **ptr > 9)
322
+	{
323
+		return 1; // parse error
324
+	}
325
+	*size = strtoull(*ptr, &end, 10);
326
+	return 0;
327
+}
328
+
93 329
 int pyfcgi_logger_open(pyfcgi_logger_t *logger)
94 330
 {
95 331
 	if( (logger->fd = open(logger->filename,

+ 1
- 0
src/pyworker.c View File

@@ -584,6 +584,7 @@ void update_python_path()
584 584
 			syslog(LOG_ALERT, "Unable to fetch python path, got NULL.");
585 585
 		}
586 586
 		err_fmt = NULL;
587
+		err = 0;
587 588
 		goto update_python_path_err_wcwd;
588 589
 	}
589 590
 	ppath = wcsdup(ppath);

Loading…
Cancel
Save