comparison src/stream/ngx_stream_log_module.c @ 6678:0125b151c9a5

Stream: log module.
author Vladimir Homutov <vl@nginx.com>
date Mon, 05 Sep 2016 17:50:16 +0300
parents src/http/modules/ngx_http_log_module.c@b3682580c1bd
children 3908156a51fa
comparison
equal deleted inserted replaced
6677:c02290241cbe 6678:0125b151c9a5
1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_stream.h>
11
12 #if (NGX_ZLIB)
13 #include <zlib.h>
14 #endif
15
16
17 typedef struct ngx_stream_log_op_s ngx_stream_log_op_t;
18
19 typedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s,
20 u_char *buf, ngx_stream_log_op_t *op);
21
22 typedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s,
23 uintptr_t data);
24
25
26 struct ngx_stream_log_op_s {
27 size_t len;
28 ngx_stream_log_op_getlen_pt getlen;
29 ngx_stream_log_op_run_pt run;
30 uintptr_t data;
31 };
32
33
34 typedef struct {
35 ngx_str_t name;
36 ngx_array_t *flushes;
37 ngx_array_t *ops; /* array of ngx_stream_log_op_t */
38 } ngx_stream_log_fmt_t;
39
40
41 typedef struct {
42 ngx_array_t formats; /* array of ngx_stream_log_fmt_t */
43 } ngx_stream_log_main_conf_t;
44
45
46 typedef struct {
47 u_char *start;
48 u_char *pos;
49 u_char *last;
50
51 ngx_event_t *event;
52 ngx_msec_t flush;
53 ngx_int_t gzip;
54 } ngx_stream_log_buf_t;
55
56
57 typedef struct {
58 ngx_array_t *lengths;
59 ngx_array_t *values;
60 } ngx_stream_log_script_t;
61
62
63 typedef struct {
64 ngx_open_file_t *file;
65 ngx_stream_log_script_t *script;
66 time_t disk_full_time;
67 time_t error_log_time;
68 ngx_syslog_peer_t *syslog_peer;
69 ngx_stream_log_fmt_t *format;
70 ngx_stream_complex_value_t *filter;
71 } ngx_stream_log_t;
72
73
74 typedef struct {
75 ngx_array_t *logs; /* array of ngx_stream_log_t */
76
77 ngx_open_file_cache_t *open_file_cache;
78 time_t open_file_cache_valid;
79 ngx_uint_t open_file_cache_min_uses;
80
81 ngx_uint_t off; /* unsigned off:1 */
82 } ngx_stream_log_srv_conf_t;
83
84
85 typedef struct {
86 ngx_str_t name;
87 size_t len;
88 ngx_stream_log_op_run_pt run;
89 } ngx_stream_log_var_t;
90
91
92 static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
93 u_char *buf, size_t len);
94 static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s,
95 ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len);
96
97 #if (NGX_ZLIB)
98 static ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
99 ngx_int_t level, ngx_log_t *log);
100
101 static void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size);
102 static void ngx_stream_log_gzip_free(void *opaque, void *address);
103 #endif
104
105 static void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log);
106 static void ngx_stream_log_flush_handler(ngx_event_t *ev);
107
108 static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf,
109 ngx_stream_log_op_t *op, ngx_str_t *value);
110 static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s,
111 uintptr_t data);
112 static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
113 ngx_stream_log_op_t *op);
114 static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size);
115
116
117 static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf);
118 static void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf);
119 static char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent,
120 void *child);
121 static char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
122 void *conf);
123 static char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
124 void *conf);
125 static char *ngx_stream_log_compile_format(ngx_conf_t *cf,
126 ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
127 static char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
128 void *conf);
129 static ngx_int_t ngx_stream_log_init(ngx_conf_t *cf);
130
131
132 static ngx_command_t ngx_stream_log_commands[] = {
133
134 { ngx_string("log_format"),
135 NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE,
136 ngx_stream_log_set_format,
137 NGX_STREAM_MAIN_CONF_OFFSET,
138 0,
139 NULL },
140
141 { ngx_string("access_log"),
142 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
143 ngx_stream_log_set_log,
144 NGX_STREAM_SRV_CONF_OFFSET,
145 0,
146 NULL },
147
148 { ngx_string("open_log_file_cache"),
149 NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234,
150 ngx_stream_log_open_file_cache,
151 NGX_STREAM_SRV_CONF_OFFSET,
152 0,
153 NULL },
154
155 ngx_null_command
156 };
157
158
159 static ngx_stream_module_t ngx_stream_log_module_ctx = {
160 NULL, /* preconfiguration */
161 ngx_stream_log_init, /* postconfiguration */
162
163 ngx_stream_log_create_main_conf, /* create main configuration */
164 NULL, /* init main configuration */
165
166 ngx_stream_log_create_srv_conf, /* create server configuration */
167 ngx_stream_log_merge_srv_conf /* merge server configuration */
168 };
169
170
171 ngx_module_t ngx_stream_log_module = {
172 NGX_MODULE_V1,
173 &ngx_stream_log_module_ctx, /* module context */
174 ngx_stream_log_commands, /* module directives */
175 NGX_STREAM_MODULE, /* module type */
176 NULL, /* init master */
177 NULL, /* init module */
178 NULL, /* init process */
179 NULL, /* init thread */
180 NULL, /* exit thread */
181 NULL, /* exit process */
182 NULL, /* exit master */
183 NGX_MODULE_V1_PADDING
184 };
185
186
187 static ngx_int_t
188 ngx_stream_log_handler(ngx_stream_session_t *s)
189 {
190 u_char *line, *p;
191 size_t len, size;
192 ssize_t n;
193 ngx_str_t val;
194 ngx_uint_t i, l;
195 ngx_stream_log_t *log;
196 ngx_stream_log_op_t *op;
197 ngx_stream_log_buf_t *buffer;
198 ngx_stream_log_srv_conf_t *lscf;
199
200 ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
201 "stream log handler");
202
203 lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);
204
205 if (lscf->off || lscf->logs == NULL) {
206 return NGX_OK;
207 }
208
209 log = lscf->logs->elts;
210 for (l = 0; l < lscf->logs->nelts; l++) {
211
212 if (log[l].filter) {
213 if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) {
214 return NGX_ERROR;
215 }
216
217 if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
218 continue;
219 }
220 }
221
222 if (ngx_time() == log[l].disk_full_time) {
223
224 /*
225 * on FreeBSD writing to a full filesystem with enabled softupdates
226 * may block process for much longer time than writing to non-full
227 * filesystem, so we skip writing to a log for one second
228 */
229
230 continue;
231 }
232
233 ngx_stream_script_flush_no_cacheable_variables(s,
234 log[l].format->flushes);
235
236 len = 0;
237 op = log[l].format->ops->elts;
238 for (i = 0; i < log[l].format->ops->nelts; i++) {
239 if (op[i].len == 0) {
240 len += op[i].getlen(s, op[i].data);
241
242 } else {
243 len += op[i].len;
244 }
245 }
246
247 if (log[l].syslog_peer) {
248
249 /* length of syslog's PRI and HEADER message parts */
250 len += sizeof("<255>Jan 01 00:00:00 ") - 1
251 + ngx_cycle->hostname.len + 1
252 + log[l].syslog_peer->tag.len + 2;
253
254 goto alloc_line;
255 }
256
257 len += NGX_LINEFEED_SIZE;
258
259 buffer = log[l].file ? log[l].file->data : NULL;
260
261 if (buffer) {
262
263 if (len > (size_t) (buffer->last - buffer->pos)) {
264
265 ngx_stream_log_write(s, &log[l], buffer->start,
266 buffer->pos - buffer->start);
267
268 buffer->pos = buffer->start;
269 }
270
271 if (len <= (size_t) (buffer->last - buffer->pos)) {
272
273 p = buffer->pos;
274
275 if (buffer->event && p == buffer->start) {
276 ngx_add_timer(buffer->event, buffer->flush);
277 }
278
279 for (i = 0; i < log[l].format->ops->nelts; i++) {
280 p = op[i].run(s, p, &op[i]);
281 }
282
283 ngx_linefeed(p);
284
285 buffer->pos = p;
286
287 continue;
288 }
289
290 if (buffer->event && buffer->event->timer_set) {
291 ngx_del_timer(buffer->event);
292 }
293 }
294
295 alloc_line:
296
297 line = ngx_pnalloc(s->connection->pool, len);
298 if (line == NULL) {
299 return NGX_ERROR;
300 }
301
302 p = line;
303
304 if (log[l].syslog_peer) {
305 p = ngx_syslog_add_header(log[l].syslog_peer, line);
306 }
307
308 for (i = 0; i < log[l].format->ops->nelts; i++) {
309 p = op[i].run(s, p, &op[i]);
310 }
311
312 if (log[l].syslog_peer) {
313
314 size = p - line;
315
316 n = ngx_syslog_send(log[l].syslog_peer, line, size);
317
318 if (n < 0) {
319 ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
320 "send() to syslog failed");
321
322 } else if ((size_t) n != size) {
323 ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
324 "send() to syslog has written only %z of %uz",
325 n, size);
326 }
327
328 continue;
329 }
330
331 ngx_linefeed(p);
332
333 ngx_stream_log_write(s, &log[l], line, p - line);
334 }
335
336 return NGX_OK;
337 }
338
339
340 static void
341 ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
342 u_char *buf, size_t len)
343 {
344 u_char *name;
345 time_t now;
346 ssize_t n;
347 ngx_err_t err;
348 #if (NGX_ZLIB)
349 ngx_stream_log_buf_t *buffer;
350 #endif
351
352 if (log->script == NULL) {
353 name = log->file->name.data;
354
355 #if (NGX_ZLIB)
356 buffer = log->file->data;
357
358 if (buffer && buffer->gzip) {
359 n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip,
360 s->connection->log);
361 } else {
362 n = ngx_write_fd(log->file->fd, buf, len);
363 }
364 #else
365 n = ngx_write_fd(log->file->fd, buf, len);
366 #endif
367
368 } else {
369 name = NULL;
370 n = ngx_stream_log_script_write(s, log->script, &name, buf, len);
371 }
372
373 if (n == (ssize_t) len) {
374 return;
375 }
376
377 now = ngx_time();
378
379 if (n == -1) {
380 err = ngx_errno;
381
382 if (err == NGX_ENOSPC) {
383 log->disk_full_time = now;
384 }
385
386 if (now - log->error_log_time > 59) {
387 ngx_log_error(NGX_LOG_ALERT, s->connection->log, err,
388 ngx_write_fd_n " to \"%s\" failed", name);
389
390 log->error_log_time = now;
391 }
392
393 return;
394 }
395
396 if (now - log->error_log_time > 59) {
397 ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
398 ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
399 name, n, len);
400
401 log->error_log_time = now;
402 }
403 }
404
405
406 static ssize_t
407 ngx_stream_log_script_write(ngx_stream_session_t *s,
408 ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len)
409 {
410 ssize_t n;
411 ngx_str_t log;
412 ngx_open_file_info_t of;
413 ngx_stream_log_srv_conf_t *lscf;
414
415 if (ngx_stream_script_run(s, &log, script->lengths->elts, 1,
416 script->values->elts)
417 == NULL)
418 {
419 /* simulate successful logging */
420 return len;
421 }
422
423 log.data[log.len - 1] = '\0';
424 *name = log.data;
425
426 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
427 "stream log \"%s\"", log.data);
428
429 lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);
430
431 ngx_memzero(&of, sizeof(ngx_open_file_info_t));
432
433 of.log = 1;
434 of.valid = lscf->open_file_cache_valid;
435 of.min_uses = lscf->open_file_cache_min_uses;
436 of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
437
438 if (ngx_open_cached_file(lscf->open_file_cache, &log, &of,
439 s->connection->pool)
440 != NGX_OK)
441 {
442 ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
443 "%s \"%s\" failed", of.failed, log.data);
444 /* simulate successful logging */
445 return len;
446 }
447
448 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
449 "stream log #%d", of.fd);
450
451 n = ngx_write_fd(of.fd, buf, len);
452
453 return n;
454 }
455
456
457 #if (NGX_ZLIB)
458
459 static ssize_t
460 ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
461 ngx_log_t *log)
462 {
463 int rc, wbits, memlevel;
464 u_char *out;
465 size_t size;
466 ssize_t n;
467 z_stream zstream;
468 ngx_err_t err;
469 ngx_pool_t *pool;
470
471 wbits = MAX_WBITS;
472 memlevel = MAX_MEM_LEVEL - 1;
473
474 while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
475 wbits--;
476 memlevel--;
477 }
478
479 /*
480 * This is a formula from deflateBound() for conservative upper bound of
481 * compressed data plus 18 bytes of gzip wrapper.
482 */
483
484 size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;
485
486 ngx_memzero(&zstream, sizeof(z_stream));
487
488 pool = ngx_create_pool(256, log);
489 if (pool == NULL) {
490 /* simulate successful logging */
491 return len;
492 }
493
494 pool->log = log;
495
496 zstream.zalloc = ngx_stream_log_gzip_alloc;
497 zstream.zfree = ngx_stream_log_gzip_free;
498 zstream.opaque = pool;
499
500 out = ngx_pnalloc(pool, size);
501 if (out == NULL) {
502 goto done;
503 }
504
505 zstream.next_in = buf;
506 zstream.avail_in = len;
507 zstream.next_out = out;
508 zstream.avail_out = size;
509
510 rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
511 Z_DEFAULT_STRATEGY);
512
513 if (rc != Z_OK) {
514 ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
515 goto done;
516 }
517
518 ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,
519 "deflate in: ni:%p no:%p ai:%ud ao:%ud",
520 zstream.next_in, zstream.next_out,
521 zstream.avail_in, zstream.avail_out);
522
523 rc = deflate(&zstream, Z_FINISH);
524
525 if (rc != Z_STREAM_END) {
526 ngx_log_error(NGX_LOG_ALERT, log, 0,
527 "deflate(Z_FINISH) failed: %d", rc);
528 goto done;
529 }
530
531 ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0,
532 "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
533 zstream.next_in, zstream.next_out,
534 zstream.avail_in, zstream.avail_out,
535 rc);
536
537 size -= zstream.avail_out;
538
539 rc = deflateEnd(&zstream);
540
541 if (rc != Z_OK) {
542 ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
543 goto done;
544 }
545
546 n = ngx_write_fd(fd, out, size);
547
548 if (n != (ssize_t) size) {
549 err = (n == -1) ? ngx_errno : 0;
550
551 ngx_destroy_pool(pool);
552
553 ngx_set_errno(err);
554 return -1;
555 }
556
557 done:
558
559 ngx_destroy_pool(pool);
560
561 /* simulate successful logging */
562 return len;
563 }
564
565
566 static void *
567 ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size)
568 {
569 ngx_pool_t *pool = opaque;
570
571 ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0,
572 "gzip alloc: n:%ud s:%ud", items, size);
573
574 return ngx_palloc(pool, items * size);
575 }
576
577
578 static void
579 ngx_stream_log_gzip_free(void *opaque, void *address)
580 {
581 #if 0
582 ngx_pool_t *pool = opaque;
583
584 ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0,
585 "gzip free: %p", address);
586 #endif
587 }
588
589 #endif
590
591
592 static void
593 ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log)
594 {
595 size_t len;
596 ssize_t n;
597 ngx_stream_log_buf_t *buffer;
598
599 buffer = file->data;
600
601 len = buffer->pos - buffer->start;
602
603 if (len == 0) {
604 return;
605 }
606
607 #if (NGX_ZLIB)
608 if (buffer->gzip) {
609 n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip,
610 log);
611 } else {
612 n = ngx_write_fd(file->fd, buffer->start, len);
613 }
614 #else
615 n = ngx_write_fd(file->fd, buffer->start, len);
616 #endif
617
618 if (n == -1) {
619 ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
620 ngx_write_fd_n " to \"%s\" failed",
621 file->name.data);
622
623 } else if ((size_t) n != len) {
624 ngx_log_error(NGX_LOG_ALERT, log, 0,
625 ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
626 file->name.data, n, len);
627 }
628
629 buffer->pos = buffer->start;
630
631 if (buffer->event && buffer->event->timer_set) {
632 ngx_del_timer(buffer->event);
633 }
634 }
635
636
637 static void
638 ngx_stream_log_flush_handler(ngx_event_t *ev)
639 {
640 ngx_open_file_t *file;
641 ngx_stream_log_buf_t *buffer;
642
643 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
644 "stream log buffer flush handler");
645
646 if (ev->timedout) {
647 ngx_stream_log_flush(ev->data, ev->log);
648 return;
649 }
650
651 /* cancel the flush timer for graceful shutdown */
652
653 file = ev->data;
654 buffer = file->data;
655
656 buffer->event = NULL;
657 }
658
659
660 static u_char *
661 ngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf,
662 ngx_stream_log_op_t *op)
663 {
664 size_t len;
665 uintptr_t data;
666
667 len = op->len;
668 data = op->data;
669
670 while (len--) {
671 *buf++ = (u_char) (data & 0xff);
672 data >>= 8;
673 }
674
675 return buf;
676 }
677
678
679 static u_char *
680 ngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf,
681 ngx_stream_log_op_t *op)
682 {
683 return ngx_cpymem(buf, (u_char *) op->data, op->len);
684 }
685
686
687 static ngx_int_t
688 ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op,
689 ngx_str_t *value)
690 {
691 ngx_int_t index;
692
693 index = ngx_stream_get_variable_index(cf, value);
694 if (index == NGX_ERROR) {
695 return NGX_ERROR;
696 }
697
698 op->len = 0;
699 op->getlen = ngx_stream_log_variable_getlen;
700 op->run = ngx_stream_log_variable;
701 op->data = index;
702
703 return NGX_OK;
704 }
705
706
707 static size_t
708 ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
709 {
710 uintptr_t len;
711 ngx_stream_variable_value_t *value;
712
713 value = ngx_stream_get_indexed_variable(s, data);
714
715 if (value == NULL || value->not_found) {
716 return 1;
717 }
718
719 len = ngx_stream_log_escape(NULL, value->data, value->len);
720
721 value->escape = len ? 1 : 0;
722
723 return value->len + len * 3;
724 }
725
726
727 static u_char *
728 ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
729 ngx_stream_log_op_t *op)
730 {
731 ngx_stream_variable_value_t *value;
732
733 value = ngx_stream_get_indexed_variable(s, op->data);
734
735 if (value == NULL || value->not_found) {
736 *buf = '-';
737 return buf + 1;
738 }
739
740 if (value->escape == 0) {
741 return ngx_cpymem(buf, value->data, value->len);
742
743 } else {
744 return (u_char *) ngx_stream_log_escape(buf, value->data, value->len);
745 }
746 }
747
748
749 static uintptr_t
750 ngx_stream_log_escape(u_char *dst, u_char *src, size_t size)
751 {
752 ngx_uint_t n;
753 static u_char hex[] = "0123456789ABCDEF";
754
755 static uint32_t escape[] = {
756 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
757
758 /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
759 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */
760
761 /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
762 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */
763
764 /* ~}| {zyx wvut srqp onml kjih gfed cba` */
765 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
766
767 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
768 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
769 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
770 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
771 };
772
773
774 if (dst == NULL) {
775
776 /* find the number of the characters to be escaped */
777
778 n = 0;
779
780 while (size) {
781 if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
782 n++;
783 }
784 src++;
785 size--;
786 }
787
788 return (uintptr_t) n;
789 }
790
791 while (size) {
792 if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
793 *dst++ = '\\';
794 *dst++ = 'x';
795 *dst++ = hex[*src >> 4];
796 *dst++ = hex[*src & 0xf];
797 src++;
798
799 } else {
800 *dst++ = *src++;
801 }
802 size--;
803 }
804
805 return (uintptr_t) dst;
806 }
807
808
809 static void *
810 ngx_stream_log_create_main_conf(ngx_conf_t *cf)
811 {
812 ngx_stream_log_main_conf_t *conf;
813
814 conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t));
815 if (conf == NULL) {
816 return NULL;
817 }
818
819 if (ngx_array_init(&conf->formats, cf->pool, 4,
820 sizeof(ngx_stream_log_fmt_t))
821 != NGX_OK)
822 {
823 return NULL;
824 }
825
826 return conf;
827 }
828
829
830 static void *
831 ngx_stream_log_create_srv_conf(ngx_conf_t *cf)
832 {
833 ngx_stream_log_srv_conf_t *conf;
834
835 conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t));
836 if (conf == NULL) {
837 return NULL;
838 }
839
840 conf->open_file_cache = NGX_CONF_UNSET_PTR;
841
842 return conf;
843 }
844
845
846 static char *
847 ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
848 {
849 ngx_stream_log_srv_conf_t *prev = parent;
850 ngx_stream_log_srv_conf_t *conf = child;
851
852 if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
853
854 conf->open_file_cache = prev->open_file_cache;
855 conf->open_file_cache_valid = prev->open_file_cache_valid;
856 conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
857
858 if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
859 conf->open_file_cache = NULL;
860 }
861 }
862
863 if (conf->logs || conf->off) {
864 return NGX_CONF_OK;
865 }
866
867 conf->logs = prev->logs;
868 conf->off = prev->off;
869
870 return NGX_CONF_OK;
871 }
872
873
874 static char *
875 ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
876 {
877 ngx_stream_log_srv_conf_t *lscf = conf;
878
879 ssize_t size;
880 ngx_int_t gzip;
881 ngx_uint_t i, n;
882 ngx_msec_t flush;
883 ngx_str_t *value, name, s;
884 ngx_stream_log_t *log;
885 ngx_syslog_peer_t *peer;
886 ngx_stream_log_buf_t *buffer;
887 ngx_stream_log_fmt_t *fmt;
888 ngx_stream_script_compile_t sc;
889 ngx_stream_log_main_conf_t *lmcf;
890 ngx_stream_compile_complex_value_t ccv;
891
892 value = cf->args->elts;
893
894 if (ngx_strcmp(value[1].data, "off") == 0) {
895 lscf->off = 1;
896 if (cf->args->nelts == 2) {
897 return NGX_CONF_OK;
898 }
899
900 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
901 "invalid parameter \"%V\"", &value[2]);
902 return NGX_CONF_ERROR;
903 }
904
905 if (lscf->logs == NULL) {
906 lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t));
907 if (lscf->logs == NULL) {
908 return NGX_CONF_ERROR;
909 }
910 }
911
912 lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module);
913
914 log = ngx_array_push(lscf->logs);
915 if (log == NULL) {
916 return NGX_CONF_ERROR;
917 }
918
919 ngx_memzero(log, sizeof(ngx_stream_log_t));
920
921
922 if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
923
924 peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
925 if (peer == NULL) {
926 return NGX_CONF_ERROR;
927 }
928
929 if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
930 return NGX_CONF_ERROR;
931 }
932
933 log->syslog_peer = peer;
934
935 goto process_formats;
936 }
937
938 n = ngx_stream_script_variables_count(&value[1]);
939
940 if (n == 0) {
941 log->file = ngx_conf_open_file(cf->cycle, &value[1]);
942 if (log->file == NULL) {
943 return NGX_CONF_ERROR;
944 }
945
946 } else {
947 if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
948 return NGX_CONF_ERROR;
949 }
950
951 log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t));
952 if (log->script == NULL) {
953 return NGX_CONF_ERROR;
954 }
955
956 ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));
957
958 sc.cf = cf;
959 sc.source = &value[1];
960 sc.lengths = &log->script->lengths;
961 sc.values = &log->script->values;
962 sc.variables = n;
963 sc.complete_lengths = 1;
964 sc.complete_values = 1;
965
966 if (ngx_stream_script_compile(&sc) != NGX_OK) {
967 return NGX_CONF_ERROR;
968 }
969 }
970
971 process_formats:
972
973 if (cf->args->nelts >= 3) {
974 name = value[2];
975
976 } else {
977 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
978 "log format is not specified");
979 return NGX_CONF_ERROR;
980 }
981
982 fmt = lmcf->formats.elts;
983 for (i = 0; i < lmcf->formats.nelts; i++) {
984 if (fmt[i].name.len == name.len
985 && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
986 {
987 log->format = &fmt[i];
988 break;
989 }
990 }
991
992 if (log->format == NULL) {
993 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
994 "unknown log format \"%V\"", &name);
995 return NGX_CONF_ERROR;
996 }
997
998 size = 0;
999 flush = 0;
1000 gzip = 0;
1001
1002 for (i = 3; i < cf->args->nelts; i++) {
1003
1004 if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
1005 s.len = value[i].len - 7;
1006 s.data = value[i].data + 7;
1007
1008 size = ngx_parse_size(&s);
1009
1010 if (size == NGX_ERROR || size == 0) {
1011 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1012 "invalid buffer size \"%V\"", &s);
1013 return NGX_CONF_ERROR;
1014 }
1015
1016 continue;
1017 }
1018
1019 if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
1020 s.len = value[i].len - 6;
1021 s.data = value[i].data + 6;
1022
1023 flush = ngx_parse_time(&s, 0);
1024
1025 if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
1026 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1027 "invalid flush time \"%V\"", &s);
1028 return NGX_CONF_ERROR;
1029 }
1030
1031 continue;
1032 }
1033
1034 if (ngx_strncmp(value[i].data, "gzip", 4) == 0
1035 && (value[i].len == 4 || value[i].data[4] == '='))
1036 {
1037 #if (NGX_ZLIB)
1038 if (size == 0) {
1039 size = 64 * 1024;
1040 }
1041
1042 if (value[i].len == 4) {
1043 gzip = Z_BEST_SPEED;
1044 continue;
1045 }
1046
1047 s.len = value[i].len - 5;
1048 s.data = value[i].data + 5;
1049
1050 gzip = ngx_atoi(s.data, s.len);
1051
1052 if (gzip < 1 || gzip > 9) {
1053 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1054 "invalid compression level \"%V\"", &s);
1055 return NGX_CONF_ERROR;
1056 }
1057
1058 continue;
1059
1060 #else
1061 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1062 "nginx was built without zlib support");
1063 return NGX_CONF_ERROR;
1064 #endif
1065 }
1066
1067 if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
1068 s.len = value[i].len - 3;
1069 s.data = value[i].data + 3;
1070
1071 ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
1072
1073 ccv.cf = cf;
1074 ccv.value = &s;
1075 ccv.complex_value = ngx_palloc(cf->pool,
1076 sizeof(ngx_stream_complex_value_t));
1077 if (ccv.complex_value == NULL) {
1078 return NGX_CONF_ERROR;
1079 }
1080
1081 if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
1082 return NGX_CONF_ERROR;
1083 }
1084
1085 log->filter = ccv.complex_value;
1086
1087 continue;
1088 }
1089
1090 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1091 "invalid parameter \"%V\"", &value[i]);
1092 return NGX_CONF_ERROR;
1093 }
1094
1095 if (flush && size == 0) {
1096 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1097 "no buffer is defined for access_log \"%V\"",
1098 &value[1]);
1099 return NGX_CONF_ERROR;
1100 }
1101
1102 if (size) {
1103
1104 if (log->script) {
1105 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1106 "buffered logs cannot have variables in name");
1107 return NGX_CONF_ERROR;
1108 }
1109
1110 if (log->syslog_peer) {
1111 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1112 "logs to syslog cannot be buffered");
1113 return NGX_CONF_ERROR;
1114 }
1115
1116 if (log->file->data) {
1117 buffer = log->file->data;
1118
1119 if (buffer->last - buffer->start != size
1120 || buffer->flush != flush
1121 || buffer->gzip != gzip)
1122 {
1123 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1124 "access_log \"%V\" already defined "
1125 "with conflicting parameters",
1126 &value[1]);
1127 return NGX_CONF_ERROR;
1128 }
1129
1130 return NGX_CONF_OK;
1131 }
1132
1133 buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t));
1134 if (buffer == NULL) {
1135 return NGX_CONF_ERROR;
1136 }
1137
1138 buffer->start = ngx_pnalloc(cf->pool, size);
1139 if (buffer->start == NULL) {
1140 return NGX_CONF_ERROR;
1141 }
1142
1143 buffer->pos = buffer->start;
1144 buffer->last = buffer->start + size;
1145
1146 if (flush) {
1147 buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
1148 if (buffer->event == NULL) {
1149 return NGX_CONF_ERROR;
1150 }
1151
1152 buffer->event->data = log->file;
1153 buffer->event->handler = ngx_stream_log_flush_handler;
1154 buffer->event->log = &cf->cycle->new_log;
1155 buffer->event->cancelable = 1;
1156
1157 buffer->flush = flush;
1158 }
1159
1160 buffer->gzip = gzip;
1161
1162 log->file->flush = ngx_stream_log_flush;
1163 log->file->data = buffer;
1164 }
1165
1166 return NGX_CONF_OK;
1167 }
1168
1169
1170 static char *
1171 ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1172 {
1173 ngx_stream_log_main_conf_t *lmcf = conf;
1174
1175 ngx_str_t *value;
1176 ngx_uint_t i;
1177 ngx_stream_log_fmt_t *fmt;
1178
1179 value = cf->args->elts;
1180
1181 fmt = lmcf->formats.elts;
1182 for (i = 0; i < lmcf->formats.nelts; i++) {
1183 if (fmt[i].name.len == value[1].len
1184 && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
1185 {
1186 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1187 "duplicate \"log_format\" name \"%V\"",
1188 &value[1]);
1189 return NGX_CONF_ERROR;
1190 }
1191 }
1192
1193 fmt = ngx_array_push(&lmcf->formats);
1194 if (fmt == NULL) {
1195 return NGX_CONF_ERROR;
1196 }
1197
1198 fmt->name = value[1];
1199
1200 fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
1201 if (fmt->flushes == NULL) {
1202 return NGX_CONF_ERROR;
1203 }
1204
1205 fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t));
1206 if (fmt->ops == NULL) {
1207 return NGX_CONF_ERROR;
1208 }
1209
1210 return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops,
1211 cf->args, 2);
1212 }
1213
1214
1215 static char *
1216 ngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
1217 ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
1218 {
1219 u_char *data, *p, ch;
1220 size_t i, len;
1221 ngx_str_t *value, var;
1222 ngx_int_t *flush;
1223 ngx_uint_t bracket;
1224 ngx_stream_log_op_t *op;
1225
1226 value = args->elts;
1227
1228 for ( /* void */ ; s < args->nelts; s++) {
1229
1230 i = 0;
1231
1232 while (i < value[s].len) {
1233
1234 op = ngx_array_push(ops);
1235 if (op == NULL) {
1236 return NGX_CONF_ERROR;
1237 }
1238
1239 data = &value[s].data[i];
1240
1241 if (value[s].data[i] == '$') {
1242
1243 if (++i == value[s].len) {
1244 goto invalid;
1245 }
1246
1247 if (value[s].data[i] == '{') {
1248 bracket = 1;
1249
1250 if (++i == value[s].len) {
1251 goto invalid;
1252 }
1253
1254 var.data = &value[s].data[i];
1255
1256 } else {
1257 bracket = 0;
1258 var.data = &value[s].data[i];
1259 }
1260
1261 for (var.len = 0; i < value[s].len; i++, var.len++) {
1262 ch = value[s].data[i];
1263
1264 if (ch == '}' && bracket) {
1265 i++;
1266 bracket = 0;
1267 break;
1268 }
1269
1270 if ((ch >= 'A' && ch <= 'Z')
1271 || (ch >= 'a' && ch <= 'z')
1272 || (ch >= '0' && ch <= '9')
1273 || ch == '_')
1274 {
1275 continue;
1276 }
1277
1278 break;
1279 }
1280
1281 if (bracket) {
1282 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1283 "the closing bracket in \"%V\" "
1284 "variable is missing", &var);
1285 return NGX_CONF_ERROR;
1286 }
1287
1288 if (var.len == 0) {
1289 goto invalid;
1290 }
1291
1292 if (ngx_stream_log_variable_compile(cf, op, &var) != NGX_OK) {
1293 return NGX_CONF_ERROR;
1294 }
1295
1296 if (flushes) {
1297
1298 flush = ngx_array_push(flushes);
1299 if (flush == NULL) {
1300 return NGX_CONF_ERROR;
1301 }
1302
1303 *flush = op->data; /* variable index */
1304 }
1305
1306 continue;
1307 }
1308
1309 i++;
1310
1311 while (i < value[s].len && value[s].data[i] != '$') {
1312 i++;
1313 }
1314
1315 len = &value[s].data[i] - data;
1316
1317 if (len) {
1318
1319 op->len = len;
1320 op->getlen = NULL;
1321
1322 if (len <= sizeof(uintptr_t)) {
1323 op->run = ngx_stream_log_copy_short;
1324 op->data = 0;
1325
1326 while (len--) {
1327 op->data <<= 8;
1328 op->data |= data[len];
1329 }
1330
1331 } else {
1332 op->run = ngx_stream_log_copy_long;
1333
1334 p = ngx_pnalloc(cf->pool, len);
1335 if (p == NULL) {
1336 return NGX_CONF_ERROR;
1337 }
1338
1339 ngx_memcpy(p, data, len);
1340 op->data = (uintptr_t) p;
1341 }
1342 }
1343 }
1344 }
1345
1346 return NGX_CONF_OK;
1347
1348 invalid:
1349
1350 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);
1351
1352 return NGX_CONF_ERROR;
1353 }
1354
1355
1356 static char *
1357 ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1358 {
1359 ngx_stream_log_srv_conf_t *lscf = conf;
1360
1361 time_t inactive, valid;
1362 ngx_str_t *value, s;
1363 ngx_int_t max, min_uses;
1364 ngx_uint_t i;
1365
1366 if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) {
1367 return "is duplicate";
1368 }
1369
1370 value = cf->args->elts;
1371
1372 max = 0;
1373 inactive = 10;
1374 valid = 60;
1375 min_uses = 1;
1376
1377 for (i = 1; i < cf->args->nelts; i++) {
1378
1379 if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
1380
1381 max = ngx_atoi(value[i].data + 4, value[i].len - 4);
1382 if (max == NGX_ERROR) {
1383 goto failed;
1384 }
1385
1386 continue;
1387 }
1388
1389 if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
1390
1391 s.len = value[i].len - 9;
1392 s.data = value[i].data + 9;
1393
1394 inactive = ngx_parse_time(&s, 1);
1395 if (inactive == (time_t) NGX_ERROR) {
1396 goto failed;
1397 }
1398
1399 continue;
1400 }
1401
1402 if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
1403
1404 min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
1405 if (min_uses == NGX_ERROR) {
1406 goto failed;
1407 }
1408
1409 continue;
1410 }
1411
1412 if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
1413
1414 s.len = value[i].len - 6;
1415 s.data = value[i].data + 6;
1416
1417 valid = ngx_parse_time(&s, 1);
1418 if (valid == (time_t) NGX_ERROR) {
1419 goto failed;
1420 }
1421
1422 continue;
1423 }
1424
1425 if (ngx_strcmp(value[i].data, "off") == 0) {
1426
1427 lscf->open_file_cache = NULL;
1428
1429 continue;
1430 }
1431
1432 failed:
1433
1434 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1435 "invalid \"open_log_file_cache\" parameter \"%V\"",
1436 &value[i]);
1437 return NGX_CONF_ERROR;
1438 }
1439
1440 if (lscf->open_file_cache == NULL) {
1441 return NGX_CONF_OK;
1442 }
1443
1444 if (max == 0) {
1445 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1446 "\"open_log_file_cache\" must have \"max\" parameter");
1447 return NGX_CONF_ERROR;
1448 }
1449
1450 lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
1451
1452 if (lscf->open_file_cache) {
1453
1454 lscf->open_file_cache_valid = valid;
1455 lscf->open_file_cache_min_uses = min_uses;
1456
1457 return NGX_CONF_OK;
1458 }
1459
1460 return NGX_CONF_ERROR;
1461 }
1462
1463
1464 static ngx_int_t
1465 ngx_stream_log_init(ngx_conf_t *cf)
1466 {
1467 ngx_stream_core_main_conf_t *cmcf;
1468
1469 cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
1470
1471 cmcf->access_log_handler = ngx_stream_log_handler;
1472
1473 return NGX_OK;
1474 }