Integrate TLDK with NGINX
[tldk.git] / app / nginx / src / http / modules / ngx_http_log_module.c
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_http.h>
11
12 #if (NGX_ZLIB)
13 #include <zlib.h>
14 #endif
15
16
17 typedef struct ngx_http_log_op_s  ngx_http_log_op_t;
18
19 typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,
20     ngx_http_log_op_t *op);
21
22 typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,
23     uintptr_t data);
24
25
26 struct ngx_http_log_op_s {
27     size_t                      len;
28     ngx_http_log_op_getlen_pt   getlen;
29     ngx_http_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_http_log_op_t */
38 } ngx_http_log_fmt_t;
39
40
41 typedef struct {
42     ngx_array_t                 formats;    /* array of ngx_http_log_fmt_t */
43     ngx_uint_t                  combined_used; /* unsigned  combined_used:1 */
44 } ngx_http_log_main_conf_t;
45
46
47 typedef struct {
48     u_char                     *start;
49     u_char                     *pos;
50     u_char                     *last;
51
52     ngx_event_t                *event;
53     ngx_msec_t                  flush;
54     ngx_int_t                   gzip;
55 } ngx_http_log_buf_t;
56
57
58 typedef struct {
59     ngx_array_t                *lengths;
60     ngx_array_t                *values;
61 } ngx_http_log_script_t;
62
63
64 typedef struct {
65     ngx_open_file_t            *file;
66     ngx_http_log_script_t      *script;
67     time_t                      disk_full_time;
68     time_t                      error_log_time;
69     ngx_syslog_peer_t          *syslog_peer;
70     ngx_http_log_fmt_t         *format;
71     ngx_http_complex_value_t   *filter;
72 } ngx_http_log_t;
73
74
75 typedef struct {
76     ngx_array_t                *logs;       /* array of ngx_http_log_t */
77
78     ngx_open_file_cache_t      *open_file_cache;
79     time_t                      open_file_cache_valid;
80     ngx_uint_t                  open_file_cache_min_uses;
81
82     ngx_uint_t                  off;        /* unsigned  off:1 */
83 } ngx_http_log_loc_conf_t;
84
85
86 typedef struct {
87     ngx_str_t                   name;
88     size_t                      len;
89     ngx_http_log_op_run_pt      run;
90 } ngx_http_log_var_t;
91
92
93 static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,
94     u_char *buf, size_t len);
95 static ssize_t ngx_http_log_script_write(ngx_http_request_t *r,
96     ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);
97
98 #if (NGX_ZLIB)
99 static ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
100     ngx_int_t level, ngx_log_t *log);
101
102 static void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size);
103 static void ngx_http_log_gzip_free(void *opaque, void *address);
104 #endif
105
106 static void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log);
107 static void ngx_http_log_flush_handler(ngx_event_t *ev);
108
109 static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,
110     ngx_http_log_op_t *op);
111 static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,
112     ngx_http_log_op_t *op);
113 static u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf,
114     ngx_http_log_op_t *op);
115 static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,
116     ngx_http_log_op_t *op);
117 static u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
118     ngx_http_log_op_t *op);
119 static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,
120     ngx_http_log_op_t *op);
121 static u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
122     ngx_http_log_op_t *op);
123 static u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r,
124     u_char *buf, ngx_http_log_op_t *op);
125 static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
126     ngx_http_log_op_t *op);
127
128 static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,
129     ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t json);
130 static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,
131     uintptr_t data);
132 static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,
133     ngx_http_log_op_t *op);
134 static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);
135 static size_t ngx_http_log_json_variable_getlen(ngx_http_request_t *r,
136     uintptr_t data);
137 static u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
138     ngx_http_log_op_t *op);
139
140
141 static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
142 static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);
143 static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,
144     void *child);
145 static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
146     void *conf);
147 static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
148     void *conf);
149 static char *ngx_http_log_compile_format(ngx_conf_t *cf,
150     ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
151 static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
152     void *conf);
153 static ngx_int_t ngx_http_log_init(ngx_conf_t *cf);
154
155
156 static ngx_command_t  ngx_http_log_commands[] = {
157
158     { ngx_string("log_format"),
159       NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
160       ngx_http_log_set_format,
161       NGX_HTTP_MAIN_CONF_OFFSET,
162       0,
163       NULL },
164
165     { ngx_string("access_log"),
166       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
167                         |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,
168       ngx_http_log_set_log,
169       NGX_HTTP_LOC_CONF_OFFSET,
170       0,
171       NULL },
172
173     { ngx_string("open_log_file_cache"),
174       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
175       ngx_http_log_open_file_cache,
176       NGX_HTTP_LOC_CONF_OFFSET,
177       0,
178       NULL },
179
180       ngx_null_command
181 };
182
183
184 static ngx_http_module_t  ngx_http_log_module_ctx = {
185     NULL,                                  /* preconfiguration */
186     ngx_http_log_init,                     /* postconfiguration */
187
188     ngx_http_log_create_main_conf,         /* create main configuration */
189     NULL,                                  /* init main configuration */
190
191     NULL,                                  /* create server configuration */
192     NULL,                                  /* merge server configuration */
193
194     ngx_http_log_create_loc_conf,          /* create location configuration */
195     ngx_http_log_merge_loc_conf            /* merge location configuration */
196 };
197
198
199 ngx_module_t  ngx_http_log_module = {
200     NGX_MODULE_V1,
201     &ngx_http_log_module_ctx,              /* module context */
202     ngx_http_log_commands,                 /* module directives */
203     NGX_HTTP_MODULE,                       /* module type */
204     NULL,                                  /* init master */
205     NULL,                                  /* init module */
206     NULL,                                  /* init process */
207     NULL,                                  /* init thread */
208     NULL,                                  /* exit thread */
209     NULL,                                  /* exit process */
210     NULL,                                  /* exit master */
211     NGX_MODULE_V1_PADDING
212 };
213
214
215 static ngx_str_t  ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH);
216
217
218 static ngx_str_t  ngx_http_combined_fmt =
219     ngx_string("$remote_addr - $remote_user [$time_local] "
220                "\"$request\" $status $body_bytes_sent "
221                "\"$http_referer\" \"$http_user_agent\"");
222
223
224 static ngx_http_log_var_t  ngx_http_log_vars[] = {
225     { ngx_string("pipe"), 1, ngx_http_log_pipe },
226     { ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1,
227                           ngx_http_log_time },
228     { ngx_string("time_iso8601"), sizeof("1970-09-28T12:00:00+06:00") - 1,
229                           ngx_http_log_iso8601 },
230     { ngx_string("msec"), NGX_TIME_T_LEN + 4, ngx_http_log_msec },
231     { ngx_string("request_time"), NGX_TIME_T_LEN + 4,
232                           ngx_http_log_request_time },
233     { ngx_string("status"), NGX_INT_T_LEN, ngx_http_log_status },
234     { ngx_string("bytes_sent"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent },
235     { ngx_string("body_bytes_sent"), NGX_OFF_T_LEN,
236                           ngx_http_log_body_bytes_sent },
237     { ngx_string("request_length"), NGX_SIZE_T_LEN,
238                           ngx_http_log_request_length },
239
240     { ngx_null_string, 0, NULL }
241 };
242
243
244 static ngx_int_t
245 ngx_http_log_handler(ngx_http_request_t *r)
246 {
247     u_char                   *line, *p;
248     size_t                    len, size;
249     ssize_t                   n;
250     ngx_str_t                 val;
251     ngx_uint_t                i, l;
252     ngx_http_log_t           *log;
253     ngx_http_log_op_t        *op;
254     ngx_http_log_buf_t       *buffer;
255     ngx_http_log_loc_conf_t  *lcf;
256
257     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
258                    "http log handler");
259
260     lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
261
262     if (lcf->off) {
263         return NGX_OK;
264     }
265
266     log = lcf->logs->elts;
267     for (l = 0; l < lcf->logs->nelts; l++) {
268
269         if (log[l].filter) {
270             if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {
271                 return NGX_ERROR;
272             }
273
274             if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
275                 continue;
276             }
277         }
278
279         if (ngx_time() == log[l].disk_full_time) {
280
281             /*
282              * on FreeBSD writing to a full filesystem with enabled softupdates
283              * may block process for much longer time than writing to non-full
284              * filesystem, so we skip writing to a log for one second
285              */
286
287             continue;
288         }
289
290         ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);
291
292         len = 0;
293         op = log[l].format->ops->elts;
294         for (i = 0; i < log[l].format->ops->nelts; i++) {
295             if (op[i].len == 0) {
296                 len += op[i].getlen(r, op[i].data);
297
298             } else {
299                 len += op[i].len;
300             }
301         }
302
303         if (log[l].syslog_peer) {
304
305             /* length of syslog's PRI and HEADER message parts */
306             len += sizeof("<255>Jan 01 00:00:00 ") - 1
307                    + ngx_cycle->hostname.len + 1
308                    + log[l].syslog_peer->tag.len + 2;
309
310             goto alloc_line;
311         }
312
313         len += NGX_LINEFEED_SIZE;
314
315         buffer = log[l].file ? log[l].file->data : NULL;
316
317         if (buffer) {
318
319             if (len > (size_t) (buffer->last - buffer->pos)) {
320
321                 ngx_http_log_write(r, &log[l], buffer->start,
322                                    buffer->pos - buffer->start);
323
324                 buffer->pos = buffer->start;
325             }
326
327             if (len <= (size_t) (buffer->last - buffer->pos)) {
328
329                 p = buffer->pos;
330
331                 if (buffer->event && p == buffer->start) {
332                     ngx_add_timer(buffer->event, buffer->flush);
333                 }
334
335                 for (i = 0; i < log[l].format->ops->nelts; i++) {
336                     p = op[i].run(r, p, &op[i]);
337                 }
338
339                 ngx_linefeed(p);
340
341                 buffer->pos = p;
342
343                 continue;
344             }
345
346             if (buffer->event && buffer->event->timer_set) {
347                 ngx_del_timer(buffer->event);
348             }
349         }
350
351     alloc_line:
352
353         line = ngx_pnalloc(r->pool, len);
354         if (line == NULL) {
355             return NGX_ERROR;
356         }
357
358         p = line;
359
360         if (log[l].syslog_peer) {
361             p = ngx_syslog_add_header(log[l].syslog_peer, line);
362         }
363
364         for (i = 0; i < log[l].format->ops->nelts; i++) {
365             p = op[i].run(r, p, &op[i]);
366         }
367
368         if (log[l].syslog_peer) {
369
370             size = p - line;
371
372             n = ngx_syslog_send(log[l].syslog_peer, line, size);
373
374             if (n < 0) {
375                 ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
376                               "send() to syslog failed");
377
378             } else if ((size_t) n != size) {
379                 ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
380                               "send() to syslog has written only %z of %uz",
381                               n, size);
382             }
383
384             continue;
385         }
386
387         ngx_linefeed(p);
388
389         ngx_http_log_write(r, &log[l], line, p - line);
390     }
391
392     return NGX_OK;
393 }
394
395
396 static void
397 ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,
398     size_t len)
399 {
400     u_char              *name;
401     time_t               now;
402     ssize_t              n;
403     ngx_err_t            err;
404 #if (NGX_ZLIB)
405     ngx_http_log_buf_t  *buffer;
406 #endif
407
408     if (log->script == NULL) {
409         name = log->file->name.data;
410
411 #if (NGX_ZLIB)
412         buffer = log->file->data;
413
414         if (buffer && buffer->gzip) {
415             n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip,
416                                   r->connection->log);
417         } else {
418             n = ngx_write_fd(log->file->fd, buf, len);
419         }
420 #else
421         n = ngx_write_fd(log->file->fd, buf, len);
422 #endif
423
424     } else {
425         name = NULL;
426         n = ngx_http_log_script_write(r, log->script, &name, buf, len);
427     }
428
429     if (n == (ssize_t) len) {
430         return;
431     }
432
433     now = ngx_time();
434
435     if (n == -1) {
436         err = ngx_errno;
437
438         if (err == NGX_ENOSPC) {
439             log->disk_full_time = now;
440         }
441
442         if (now - log->error_log_time > 59) {
443             ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,
444                           ngx_write_fd_n " to \"%s\" failed", name);
445
446             log->error_log_time = now;
447         }
448
449         return;
450     }
451
452     if (now - log->error_log_time > 59) {
453         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
454                       ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
455                       name, n, len);
456
457         log->error_log_time = now;
458     }
459 }
460
461
462 static ssize_t
463 ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,
464     u_char **name, u_char *buf, size_t len)
465 {
466     size_t                     root;
467     ssize_t                    n;
468     ngx_str_t                  log, path;
469     ngx_open_file_info_t       of;
470     ngx_http_log_loc_conf_t   *llcf;
471     ngx_http_core_loc_conf_t  *clcf;
472
473     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
474
475     if (!r->root_tested) {
476
477         /* test root directory existence */
478
479         if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
480             /* simulate successful logging */
481             return len;
482         }
483
484         path.data[root] = '\0';
485
486         ngx_memzero(&of, sizeof(ngx_open_file_info_t));
487
488         of.valid = clcf->open_file_cache_valid;
489         of.min_uses = clcf->open_file_cache_min_uses;
490         of.test_dir = 1;
491         of.test_only = 1;
492         of.errors = clcf->open_file_cache_errors;
493         of.events = clcf->open_file_cache_events;
494
495         if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
496             /* simulate successful logging */
497             return len;
498         }
499
500         if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
501             != NGX_OK)
502         {
503             if (of.err == 0) {
504                 /* simulate successful logging */
505                 return len;
506             }
507
508             ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,
509                           "testing \"%s\" existence failed", path.data);
510
511             /* simulate successful logging */
512             return len;
513         }
514
515         if (!of.is_dir) {
516             ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,
517                           "testing \"%s\" existence failed", path.data);
518
519             /* simulate successful logging */
520             return len;
521         }
522     }
523
524     if (ngx_http_script_run(r, &log, script->lengths->elts, 1,
525                             script->values->elts)
526         == NULL)
527     {
528         /* simulate successful logging */
529         return len;
530     }
531
532     log.data[log.len - 1] = '\0';
533     *name = log.data;
534
535     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
536                    "http log \"%s\"", log.data);
537
538     llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);
539
540     ngx_memzero(&of, sizeof(ngx_open_file_info_t));
541
542     of.log = 1;
543     of.valid = llcf->open_file_cache_valid;
544     of.min_uses = llcf->open_file_cache_min_uses;
545     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
546
547     if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {
548         /* simulate successful logging */
549         return len;
550     }
551
552     if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)
553         != NGX_OK)
554     {
555         if (of.err == 0) {
556             /* simulate successful logging */
557             return len;
558         }
559
560         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
561                       "%s \"%s\" failed", of.failed, log.data);
562         /* simulate successful logging */
563         return len;
564     }
565
566     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
567                    "http log #%d", of.fd);
568
569     n = ngx_write_fd(of.fd, buf, len);
570
571     return n;
572 }
573
574
575 #if (NGX_ZLIB)
576
577 static ssize_t
578 ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
579     ngx_log_t *log)
580 {
581     int          rc, wbits, memlevel;
582     u_char      *out;
583     size_t       size;
584     ssize_t      n;
585     z_stream     zstream;
586     ngx_err_t    err;
587     ngx_pool_t  *pool;
588
589     wbits = MAX_WBITS;
590     memlevel = MAX_MEM_LEVEL - 1;
591
592     while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
593         wbits--;
594         memlevel--;
595     }
596
597     /*
598      * This is a formula from deflateBound() for conservative upper bound of
599      * compressed data plus 18 bytes of gzip wrapper.
600      */
601
602     size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;
603
604     ngx_memzero(&zstream, sizeof(z_stream));
605
606     pool = ngx_create_pool(256, log);
607     if (pool == NULL) {
608         /* simulate successful logging */
609         return len;
610     }
611
612     pool->log = log;
613
614     zstream.zalloc = ngx_http_log_gzip_alloc;
615     zstream.zfree = ngx_http_log_gzip_free;
616     zstream.opaque = pool;
617
618     out = ngx_pnalloc(pool, size);
619     if (out == NULL) {
620         goto done;
621     }
622
623     zstream.next_in = buf;
624     zstream.avail_in = len;
625     zstream.next_out = out;
626     zstream.avail_out = size;
627
628     rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
629                       Z_DEFAULT_STRATEGY);
630
631     if (rc != Z_OK) {
632         ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
633         goto done;
634     }
635
636     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,
637                    "deflate in: ni:%p no:%p ai:%ud ao:%ud",
638                    zstream.next_in, zstream.next_out,
639                    zstream.avail_in, zstream.avail_out);
640
641     rc = deflate(&zstream, Z_FINISH);
642
643     if (rc != Z_STREAM_END) {
644         ngx_log_error(NGX_LOG_ALERT, log, 0,
645                       "deflate(Z_FINISH) failed: %d", rc);
646         goto done;
647     }
648
649     ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0,
650                    "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
651                    zstream.next_in, zstream.next_out,
652                    zstream.avail_in, zstream.avail_out,
653                    rc);
654
655     size -= zstream.avail_out;
656
657     rc = deflateEnd(&zstream);
658
659     if (rc != Z_OK) {
660         ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
661         goto done;
662     }
663
664     n = ngx_write_fd(fd, out, size);
665
666     if (n != (ssize_t) size) {
667         err = (n == -1) ? ngx_errno : 0;
668
669         ngx_destroy_pool(pool);
670
671         ngx_set_errno(err);
672         return -1;
673     }
674
675 done:
676
677     ngx_destroy_pool(pool);
678
679     /* simulate successful logging */
680     return len;
681 }
682
683
684 static void *
685 ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size)
686 {
687     ngx_pool_t *pool = opaque;
688
689     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,
690                    "gzip alloc: n:%ud s:%ud", items, size);
691
692     return ngx_palloc(pool, items * size);
693 }
694
695
696 static void
697 ngx_http_log_gzip_free(void *opaque, void *address)
698 {
699 #if 0
700     ngx_pool_t *pool = opaque;
701
702     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "gzip free: %p", address);
703 #endif
704 }
705
706 #endif
707
708
709 static void
710 ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log)
711 {
712     size_t               len;
713     ssize_t              n;
714     ngx_http_log_buf_t  *buffer;
715
716     buffer = file->data;
717
718     len = buffer->pos - buffer->start;
719
720     if (len == 0) {
721         return;
722     }
723
724 #if (NGX_ZLIB)
725     if (buffer->gzip) {
726         n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log);
727     } else {
728         n = ngx_write_fd(file->fd, buffer->start, len);
729     }
730 #else
731     n = ngx_write_fd(file->fd, buffer->start, len);
732 #endif
733
734     if (n == -1) {
735         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
736                       ngx_write_fd_n " to \"%s\" failed",
737                       file->name.data);
738
739     } else if ((size_t) n != len) {
740         ngx_log_error(NGX_LOG_ALERT, log, 0,
741                       ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
742                       file->name.data, n, len);
743     }
744
745     buffer->pos = buffer->start;
746
747     if (buffer->event && buffer->event->timer_set) {
748         ngx_del_timer(buffer->event);
749     }
750 }
751
752
753 static void
754 ngx_http_log_flush_handler(ngx_event_t *ev)
755 {
756     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
757                    "http log buffer flush handler");
758
759     ngx_http_log_flush(ev->data, ev->log);
760 }
761
762
763 static u_char *
764 ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,
765     ngx_http_log_op_t *op)
766 {
767     size_t     len;
768     uintptr_t  data;
769
770     len = op->len;
771     data = op->data;
772
773     while (len--) {
774         *buf++ = (u_char) (data & 0xff);
775         data >>= 8;
776     }
777
778     return buf;
779 }
780
781
782 static u_char *
783 ngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf,
784     ngx_http_log_op_t *op)
785 {
786     return ngx_cpymem(buf, (u_char *) op->data, op->len);
787 }
788
789
790 static u_char *
791 ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
792 {
793     if (r->pipeline) {
794         *buf = 'p';
795     } else {
796         *buf = '.';
797     }
798
799     return buf + 1;
800 }
801
802
803 static u_char *
804 ngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
805 {
806     return ngx_cpymem(buf, ngx_cached_http_log_time.data,
807                       ngx_cached_http_log_time.len);
808 }
809
810 static u_char *
811 ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
812 {
813     return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data,
814                       ngx_cached_http_log_iso8601.len);
815 }
816
817 static u_char *
818 ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
819 {
820     ngx_time_t  *tp;
821
822     tp = ngx_timeofday();
823
824     return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec);
825 }
826
827
828 static u_char *
829 ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,
830     ngx_http_log_op_t *op)
831 {
832     ngx_time_t      *tp;
833     ngx_msec_int_t   ms;
834
835     tp = ngx_timeofday();
836
837     ms = (ngx_msec_int_t)
838              ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
839     ms = ngx_max(ms, 0);
840
841     return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000);
842 }
843
844
845 static u_char *
846 ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
847 {
848     ngx_uint_t  status;
849
850     if (r->err_status) {
851         status = r->err_status;
852
853     } else if (r->headers_out.status) {
854         status = r->headers_out.status;
855
856     } else if (r->http_version == NGX_HTTP_VERSION_9) {
857         status = 9;
858
859     } else {
860         status = 0;
861     }
862
863     return ngx_sprintf(buf, "%03ui", status);
864 }
865
866
867 static u_char *
868 ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,
869     ngx_http_log_op_t *op)
870 {
871     return ngx_sprintf(buf, "%O", r->connection->sent);
872 }
873
874
875 /*
876  * although there is a real $body_bytes_sent variable,
877  * this log operation code function is more optimized for logging
878  */
879
880 static u_char *
881 ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,
882     ngx_http_log_op_t *op)
883 {
884     off_t  length;
885
886     length = r->connection->sent - r->header_size;
887
888     if (length > 0) {
889         return ngx_sprintf(buf, "%O", length);
890     }
891
892     *buf = '0';
893
894     return buf + 1;
895 }
896
897
898 static u_char *
899 ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
900     ngx_http_log_op_t *op)
901 {
902     return ngx_sprintf(buf, "%O", r->request_length);
903 }
904
905
906 static ngx_int_t
907 ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
908     ngx_str_t *value, ngx_uint_t json)
909 {
910     ngx_int_t  index;
911
912     index = ngx_http_get_variable_index(cf, value);
913     if (index == NGX_ERROR) {
914         return NGX_ERROR;
915     }
916
917     op->len = 0;
918
919     if (json) {
920         op->getlen = ngx_http_log_json_variable_getlen;
921         op->run = ngx_http_log_json_variable;
922
923     } else {
924         op->getlen = ngx_http_log_variable_getlen;
925         op->run = ngx_http_log_variable;
926     }
927
928     op->data = index;
929
930     return NGX_OK;
931 }
932
933
934 static size_t
935 ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)
936 {
937     uintptr_t                   len;
938     ngx_http_variable_value_t  *value;
939
940     value = ngx_http_get_indexed_variable(r, data);
941
942     if (value == NULL || value->not_found) {
943         return 1;
944     }
945
946     len = ngx_http_log_escape(NULL, value->data, value->len);
947
948     value->escape = len ? 1 : 0;
949
950     return value->len + len * 3;
951 }
952
953
954 static u_char *
955 ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
956 {
957     ngx_http_variable_value_t  *value;
958
959     value = ngx_http_get_indexed_variable(r, op->data);
960
961     if (value == NULL || value->not_found) {
962         *buf = '-';
963         return buf + 1;
964     }
965
966     if (value->escape == 0) {
967         return ngx_cpymem(buf, value->data, value->len);
968
969     } else {
970         return (u_char *) ngx_http_log_escape(buf, value->data, value->len);
971     }
972 }
973
974
975 static uintptr_t
976 ngx_http_log_escape(u_char *dst, u_char *src, size_t size)
977 {
978     ngx_uint_t      n;
979     static u_char   hex[] = "0123456789ABCDEF";
980
981     static uint32_t   escape[] = {
982         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
983
984                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
985         0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */
986
987                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
988         0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */
989
990                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
991         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
992
993         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
994         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
995         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
996         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
997     };
998
999
1000     if (dst == NULL) {
1001
1002         /* find the number of the characters to be escaped */
1003
1004         n = 0;
1005
1006         while (size) {
1007             if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
1008                 n++;
1009             }
1010             src++;
1011             size--;
1012         }
1013
1014         return (uintptr_t) n;
1015     }
1016
1017     while (size) {
1018         if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
1019             *dst++ = '\\';
1020             *dst++ = 'x';
1021             *dst++ = hex[*src >> 4];
1022             *dst++ = hex[*src & 0xf];
1023             src++;
1024
1025         } else {
1026             *dst++ = *src++;
1027         }
1028         size--;
1029     }
1030
1031     return (uintptr_t) dst;
1032 }
1033
1034
1035 static size_t
1036 ngx_http_log_json_variable_getlen(ngx_http_request_t *r, uintptr_t data)
1037 {
1038     uintptr_t                   len;
1039     ngx_http_variable_value_t  *value;
1040
1041     value = ngx_http_get_indexed_variable(r, data);
1042
1043     if (value == NULL || value->not_found) {
1044         return 0;
1045     }
1046
1047     len = ngx_escape_json(NULL, value->data, value->len);
1048
1049     value->escape = len ? 1 : 0;
1050
1051     return value->len + len;
1052 }
1053
1054
1055 static u_char *
1056 ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,
1057     ngx_http_log_op_t *op)
1058 {
1059     ngx_http_variable_value_t  *value;
1060
1061     value = ngx_http_get_indexed_variable(r, op->data);
1062
1063     if (value == NULL || value->not_found) {
1064         return buf;
1065     }
1066
1067     if (value->escape == 0) {
1068         return ngx_cpymem(buf, value->data, value->len);
1069
1070     } else {
1071         return (u_char *) ngx_escape_json(buf, value->data, value->len);
1072     }
1073 }
1074
1075
1076 static void *
1077 ngx_http_log_create_main_conf(ngx_conf_t *cf)
1078 {
1079     ngx_http_log_main_conf_t  *conf;
1080
1081     ngx_http_log_fmt_t  *fmt;
1082
1083     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));
1084     if (conf == NULL) {
1085         return NULL;
1086     }
1087
1088     if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))
1089         != NGX_OK)
1090     {
1091         return NULL;
1092     }
1093
1094     fmt = ngx_array_push(&conf->formats);
1095     if (fmt == NULL) {
1096         return NULL;
1097     }
1098
1099     ngx_str_set(&fmt->name, "combined");
1100
1101     fmt->flushes = NULL;
1102
1103     fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
1104     if (fmt->ops == NULL) {
1105         return NULL;
1106     }
1107
1108     return conf;
1109 }
1110
1111
1112 static void *
1113 ngx_http_log_create_loc_conf(ngx_conf_t *cf)
1114 {
1115     ngx_http_log_loc_conf_t  *conf;
1116
1117     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));
1118     if (conf == NULL) {
1119         return NULL;
1120     }
1121
1122     conf->open_file_cache = NGX_CONF_UNSET_PTR;
1123
1124     return conf;
1125 }
1126
1127
1128 static char *
1129 ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1130 {
1131     ngx_http_log_loc_conf_t *prev = parent;
1132     ngx_http_log_loc_conf_t *conf = child;
1133
1134     ngx_http_log_t            *log;
1135     ngx_http_log_fmt_t        *fmt;
1136     ngx_http_log_main_conf_t  *lmcf;
1137
1138     if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
1139
1140         conf->open_file_cache = prev->open_file_cache;
1141         conf->open_file_cache_valid = prev->open_file_cache_valid;
1142         conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
1143
1144         if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
1145             conf->open_file_cache = NULL;
1146         }
1147     }
1148
1149     if (conf->logs || conf->off) {
1150         return NGX_CONF_OK;
1151     }
1152
1153     conf->logs = prev->logs;
1154     conf->off = prev->off;
1155
1156     if (conf->logs || conf->off) {
1157         return NGX_CONF_OK;
1158     }
1159
1160     conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
1161     if (conf->logs == NULL) {
1162         return NGX_CONF_ERROR;
1163     }
1164
1165     log = ngx_array_push(conf->logs);
1166     if (log == NULL) {
1167         return NGX_CONF_ERROR;
1168     }
1169
1170     ngx_memzero(log, sizeof(ngx_http_log_t));
1171
1172     log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log);
1173     if (log->file == NULL) {
1174         return NGX_CONF_ERROR;
1175     }
1176
1177     lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
1178     fmt = lmcf->formats.elts;
1179
1180     /* the default "combined" format */
1181     log->format = &fmt[0];
1182     lmcf->combined_used = 1;
1183
1184     return NGX_CONF_OK;
1185 }
1186
1187
1188 static char *
1189 ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1190 {
1191     ngx_http_log_loc_conf_t *llcf = conf;
1192
1193     ssize_t                            size;
1194     ngx_int_t                          gzip;
1195     ngx_uint_t                         i, n;
1196     ngx_msec_t                         flush;
1197     ngx_str_t                         *value, name, s;
1198     ngx_http_log_t                    *log;
1199     ngx_syslog_peer_t                 *peer;
1200     ngx_http_log_buf_t                *buffer;
1201     ngx_http_log_fmt_t                *fmt;
1202     ngx_http_log_main_conf_t          *lmcf;
1203     ngx_http_script_compile_t          sc;
1204     ngx_http_compile_complex_value_t   ccv;
1205
1206     value = cf->args->elts;
1207
1208     if (ngx_strcmp(value[1].data, "off") == 0) {
1209         llcf->off = 1;
1210         if (cf->args->nelts == 2) {
1211             return NGX_CONF_OK;
1212         }
1213
1214         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1215                            "invalid parameter \"%V\"", &value[2]);
1216         return NGX_CONF_ERROR;
1217     }
1218
1219     if (llcf->logs == NULL) {
1220         llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));
1221         if (llcf->logs == NULL) {
1222             return NGX_CONF_ERROR;
1223         }
1224     }
1225
1226     lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
1227
1228     log = ngx_array_push(llcf->logs);
1229     if (log == NULL) {
1230         return NGX_CONF_ERROR;
1231     }
1232
1233     ngx_memzero(log, sizeof(ngx_http_log_t));
1234
1235
1236     if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
1237
1238         peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
1239         if (peer == NULL) {
1240             return NGX_CONF_ERROR;
1241         }
1242
1243         if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
1244             return NGX_CONF_ERROR;
1245         }
1246
1247         log->syslog_peer = peer;
1248
1249         goto process_formats;
1250     }
1251
1252     n = ngx_http_script_variables_count(&value[1]);
1253
1254     if (n == 0) {
1255         log->file = ngx_conf_open_file(cf->cycle, &value[1]);
1256         if (log->file == NULL) {
1257             return NGX_CONF_ERROR;
1258         }
1259
1260     } else {
1261         if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
1262             return NGX_CONF_ERROR;
1263         }
1264
1265         log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));
1266         if (log->script == NULL) {
1267             return NGX_CONF_ERROR;
1268         }
1269
1270         ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
1271
1272         sc.cf = cf;
1273         sc.source = &value[1];
1274         sc.lengths = &log->script->lengths;
1275         sc.values = &log->script->values;
1276         sc.variables = n;
1277         sc.complete_lengths = 1;
1278         sc.complete_values = 1;
1279
1280         if (ngx_http_script_compile(&sc) != NGX_OK) {
1281             return NGX_CONF_ERROR;
1282         }
1283     }
1284
1285 process_formats:
1286
1287     if (cf->args->nelts >= 3) {
1288         name = value[2];
1289
1290         if (ngx_strcmp(name.data, "combined") == 0) {
1291             lmcf->combined_used = 1;
1292         }
1293
1294     } else {
1295         ngx_str_set(&name, "combined");
1296         lmcf->combined_used = 1;
1297     }
1298
1299     fmt = lmcf->formats.elts;
1300     for (i = 0; i < lmcf->formats.nelts; i++) {
1301         if (fmt[i].name.len == name.len
1302             && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
1303         {
1304             log->format = &fmt[i];
1305             break;
1306         }
1307     }
1308
1309     if (log->format == NULL) {
1310         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1311                            "unknown log format \"%V\"", &name);
1312         return NGX_CONF_ERROR;
1313     }
1314
1315     size = 0;
1316     flush = 0;
1317     gzip = 0;
1318
1319     for (i = 3; i < cf->args->nelts; i++) {
1320
1321         if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
1322             s.len = value[i].len - 7;
1323             s.data = value[i].data + 7;
1324
1325             size = ngx_parse_size(&s);
1326
1327             if (size == NGX_ERROR || size == 0) {
1328                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1329                                    "invalid buffer size \"%V\"", &s);
1330                 return NGX_CONF_ERROR;
1331             }
1332
1333             continue;
1334         }
1335
1336         if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
1337             s.len = value[i].len - 6;
1338             s.data = value[i].data + 6;
1339
1340             flush = ngx_parse_time(&s, 0);
1341
1342             if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
1343                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1344                                    "invalid flush time \"%V\"", &s);
1345                 return NGX_CONF_ERROR;
1346             }
1347
1348             continue;
1349         }
1350
1351         if (ngx_strncmp(value[i].data, "gzip", 4) == 0
1352             && (value[i].len == 4 || value[i].data[4] == '='))
1353         {
1354 #if (NGX_ZLIB)
1355             if (size == 0) {
1356                 size = 64 * 1024;
1357             }
1358
1359             if (value[i].len == 4) {
1360                 gzip = Z_BEST_SPEED;
1361                 continue;
1362             }
1363
1364             s.len = value[i].len - 5;
1365             s.data = value[i].data + 5;
1366
1367             gzip = ngx_atoi(s.data, s.len);
1368
1369             if (gzip < 1 || gzip > 9) {
1370                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1371                                    "invalid compression level \"%V\"", &s);
1372                 return NGX_CONF_ERROR;
1373             }
1374
1375             continue;
1376
1377 #else
1378             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1379                                "nginx was built without zlib support");
1380             return NGX_CONF_ERROR;
1381 #endif
1382         }
1383
1384         if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
1385             s.len = value[i].len - 3;
1386             s.data = value[i].data + 3;
1387
1388             ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
1389
1390             ccv.cf = cf;
1391             ccv.value = &s;
1392             ccv.complex_value = ngx_palloc(cf->pool,
1393                                            sizeof(ngx_http_complex_value_t));
1394             if (ccv.complex_value == NULL) {
1395                 return NGX_CONF_ERROR;
1396             }
1397
1398             if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
1399                 return NGX_CONF_ERROR;
1400             }
1401
1402             log->filter = ccv.complex_value;
1403
1404             continue;
1405         }
1406
1407         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1408                            "invalid parameter \"%V\"", &value[i]);
1409         return NGX_CONF_ERROR;
1410     }
1411
1412     if (flush && size == 0) {
1413         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1414                            "no buffer is defined for access_log \"%V\"",
1415                            &value[1]);
1416         return NGX_CONF_ERROR;
1417     }
1418
1419     if (size) {
1420
1421         if (log->script) {
1422             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1423                                "buffered logs cannot have variables in name");
1424             return NGX_CONF_ERROR;
1425         }
1426
1427         if (log->syslog_peer) {
1428             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1429                                "logs to syslog cannot be buffered");
1430             return NGX_CONF_ERROR;
1431         }
1432
1433         if (log->file->data) {
1434             buffer = log->file->data;
1435
1436             if (buffer->last - buffer->start != size
1437                 || buffer->flush != flush
1438                 || buffer->gzip != gzip)
1439             {
1440                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1441                                    "access_log \"%V\" already defined "
1442                                    "with conflicting parameters",
1443                                    &value[1]);
1444                 return NGX_CONF_ERROR;
1445             }
1446
1447             return NGX_CONF_OK;
1448         }
1449
1450         buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t));
1451         if (buffer == NULL) {
1452             return NGX_CONF_ERROR;
1453         }
1454
1455         buffer->start = ngx_pnalloc(cf->pool, size);
1456         if (buffer->start == NULL) {
1457             return NGX_CONF_ERROR;
1458         }
1459
1460         buffer->pos = buffer->start;
1461         buffer->last = buffer->start + size;
1462
1463         if (flush) {
1464             buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
1465             if (buffer->event == NULL) {
1466                 return NGX_CONF_ERROR;
1467             }
1468
1469             buffer->event->data = log->file;
1470             buffer->event->handler = ngx_http_log_flush_handler;
1471             buffer->event->log = &cf->cycle->new_log;
1472             buffer->event->cancelable = 1;
1473
1474             buffer->flush = flush;
1475         }
1476
1477         buffer->gzip = gzip;
1478
1479         log->file->flush = ngx_http_log_flush;
1480         log->file->data = buffer;
1481     }
1482
1483     return NGX_CONF_OK;
1484 }
1485
1486
1487 static char *
1488 ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1489 {
1490     ngx_http_log_main_conf_t *lmcf = conf;
1491
1492     ngx_str_t           *value;
1493     ngx_uint_t           i;
1494     ngx_http_log_fmt_t  *fmt;
1495
1496     value = cf->args->elts;
1497
1498     fmt = lmcf->formats.elts;
1499     for (i = 0; i < lmcf->formats.nelts; i++) {
1500         if (fmt[i].name.len == value[1].len
1501             && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
1502         {
1503             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1504                                "duplicate \"log_format\" name \"%V\"",
1505                                &value[1]);
1506             return NGX_CONF_ERROR;
1507         }
1508     }
1509
1510     fmt = ngx_array_push(&lmcf->formats);
1511     if (fmt == NULL) {
1512         return NGX_CONF_ERROR;
1513     }
1514
1515     fmt->name = value[1];
1516
1517     fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
1518     if (fmt->flushes == NULL) {
1519         return NGX_CONF_ERROR;
1520     }
1521
1522     fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));
1523     if (fmt->ops == NULL) {
1524         return NGX_CONF_ERROR;
1525     }
1526
1527     return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2);
1528 }
1529
1530
1531 static char *
1532 ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
1533     ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
1534 {
1535     u_char              *data, *p, ch;
1536     size_t               i, len;
1537     ngx_str_t           *value, var;
1538     ngx_int_t           *flush;
1539     ngx_uint_t           bracket, json;
1540     ngx_http_log_op_t   *op;
1541     ngx_http_log_var_t  *v;
1542
1543     json = 0;
1544     value = args->elts;
1545
1546     if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
1547         data = value[s].data + 7;
1548
1549         if (ngx_strcmp(data, "json") == 0) {
1550             json = 1;
1551
1552         } else if (ngx_strcmp(data, "default") != 0) {
1553             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1554                                "unknown log format escaping \"%s\"", data);
1555             return NGX_CONF_ERROR;
1556         }
1557
1558         s++;
1559     }
1560
1561     for ( /* void */ ; s < args->nelts; s++) {
1562
1563         i = 0;
1564
1565         while (i < value[s].len) {
1566
1567             op = ngx_array_push(ops);
1568             if (op == NULL) {
1569                 return NGX_CONF_ERROR;
1570             }
1571
1572             data = &value[s].data[i];
1573
1574             if (value[s].data[i] == '$') {
1575
1576                 if (++i == value[s].len) {
1577                     goto invalid;
1578                 }
1579
1580                 if (value[s].data[i] == '{') {
1581                     bracket = 1;
1582
1583                     if (++i == value[s].len) {
1584                         goto invalid;
1585                     }
1586
1587                     var.data = &value[s].data[i];
1588
1589                 } else {
1590                     bracket = 0;
1591                     var.data = &value[s].data[i];
1592                 }
1593
1594                 for (var.len = 0; i < value[s].len; i++, var.len++) {
1595                     ch = value[s].data[i];
1596
1597                     if (ch == '}' && bracket) {
1598                         i++;
1599                         bracket = 0;
1600                         break;
1601                     }
1602
1603                     if ((ch >= 'A' && ch <= 'Z')
1604                         || (ch >= 'a' && ch <= 'z')
1605                         || (ch >= '0' && ch <= '9')
1606                         || ch == '_')
1607                     {
1608                         continue;
1609                     }
1610
1611                     break;
1612                 }
1613
1614                 if (bracket) {
1615                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1616                                        "the closing bracket in \"%V\" "
1617                                        "variable is missing", &var);
1618                     return NGX_CONF_ERROR;
1619                 }
1620
1621                 if (var.len == 0) {
1622                     goto invalid;
1623                 }
1624
1625                 for (v = ngx_http_log_vars; v->name.len; v++) {
1626
1627                     if (v->name.len == var.len
1628                         && ngx_strncmp(v->name.data, var.data, var.len) == 0)
1629                     {
1630                         op->len = v->len;
1631                         op->getlen = NULL;
1632                         op->run = v->run;
1633                         op->data = 0;
1634
1635                         goto found;
1636                     }
1637                 }
1638
1639                 if (ngx_http_log_variable_compile(cf, op, &var, json)
1640                     != NGX_OK)
1641                 {
1642                     return NGX_CONF_ERROR;
1643                 }
1644
1645                 if (flushes) {
1646
1647                     flush = ngx_array_push(flushes);
1648                     if (flush == NULL) {
1649                         return NGX_CONF_ERROR;
1650                     }
1651
1652                     *flush = op->data; /* variable index */
1653                 }
1654
1655             found:
1656
1657                 continue;
1658             }
1659
1660             i++;
1661
1662             while (i < value[s].len && value[s].data[i] != '$') {
1663                 i++;
1664             }
1665
1666             len = &value[s].data[i] - data;
1667
1668             if (len) {
1669
1670                 op->len = len;
1671                 op->getlen = NULL;
1672
1673                 if (len <= sizeof(uintptr_t)) {
1674                     op->run = ngx_http_log_copy_short;
1675                     op->data = 0;
1676
1677                     while (len--) {
1678                         op->data <<= 8;
1679                         op->data |= data[len];
1680                     }
1681
1682                 } else {
1683                     op->run = ngx_http_log_copy_long;
1684
1685                     p = ngx_pnalloc(cf->pool, len);
1686                     if (p == NULL) {
1687                         return NGX_CONF_ERROR;
1688                     }
1689
1690                     ngx_memcpy(p, data, len);
1691                     op->data = (uintptr_t) p;
1692                 }
1693             }
1694         }
1695     }
1696
1697     return NGX_CONF_OK;
1698
1699 invalid:
1700
1701     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);
1702
1703     return NGX_CONF_ERROR;
1704 }
1705
1706
1707 static char *
1708 ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1709 {
1710     ngx_http_log_loc_conf_t *llcf = conf;
1711
1712     time_t       inactive, valid;
1713     ngx_str_t   *value, s;
1714     ngx_int_t    max, min_uses;
1715     ngx_uint_t   i;
1716
1717     if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {
1718         return "is duplicate";
1719     }
1720
1721     value = cf->args->elts;
1722
1723     max = 0;
1724     inactive = 10;
1725     valid = 60;
1726     min_uses = 1;
1727
1728     for (i = 1; i < cf->args->nelts; i++) {
1729
1730         if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
1731
1732             max = ngx_atoi(value[i].data + 4, value[i].len - 4);
1733             if (max == NGX_ERROR) {
1734                 goto failed;
1735             }
1736
1737             continue;
1738         }
1739
1740         if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
1741
1742             s.len = value[i].len - 9;
1743             s.data = value[i].data + 9;
1744
1745             inactive = ngx_parse_time(&s, 1);
1746             if (inactive == (time_t) NGX_ERROR) {
1747                 goto failed;
1748             }
1749
1750             continue;
1751         }
1752
1753         if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
1754
1755             min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
1756             if (min_uses == NGX_ERROR) {
1757                 goto failed;
1758             }
1759
1760             continue;
1761         }
1762
1763         if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
1764
1765             s.len = value[i].len - 6;
1766             s.data = value[i].data + 6;
1767
1768             valid = ngx_parse_time(&s, 1);
1769             if (valid == (time_t) NGX_ERROR) {
1770                 goto failed;
1771             }
1772
1773             continue;
1774         }
1775
1776         if (ngx_strcmp(value[i].data, "off") == 0) {
1777
1778             llcf->open_file_cache = NULL;
1779
1780             continue;
1781         }
1782
1783     failed:
1784
1785         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1786                            "invalid \"open_log_file_cache\" parameter \"%V\"",
1787                            &value[i]);
1788         return NGX_CONF_ERROR;
1789     }
1790
1791     if (llcf->open_file_cache == NULL) {
1792         return NGX_CONF_OK;
1793     }
1794
1795     if (max == 0) {
1796         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1797                         "\"open_log_file_cache\" must have \"max\" parameter");
1798         return NGX_CONF_ERROR;
1799     }
1800
1801     llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
1802
1803     if (llcf->open_file_cache) {
1804
1805         llcf->open_file_cache_valid = valid;
1806         llcf->open_file_cache_min_uses = min_uses;
1807
1808         return NGX_CONF_OK;
1809     }
1810
1811     return NGX_CONF_ERROR;
1812 }
1813
1814
1815 static ngx_int_t
1816 ngx_http_log_init(ngx_conf_t *cf)
1817 {
1818     ngx_str_t                  *value;
1819     ngx_array_t                 a;
1820     ngx_http_handler_pt        *h;
1821     ngx_http_log_fmt_t         *fmt;
1822     ngx_http_log_main_conf_t   *lmcf;
1823     ngx_http_core_main_conf_t  *cmcf;
1824
1825     lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);
1826
1827     if (lmcf->combined_used) {
1828         if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {
1829             return NGX_ERROR;
1830         }
1831
1832         value = ngx_array_push(&a);
1833         if (value == NULL) {
1834             return NGX_ERROR;
1835         }
1836
1837         *value = ngx_http_combined_fmt;
1838         fmt = lmcf->formats.elts;
1839
1840         if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)
1841             != NGX_CONF_OK)
1842         {
1843             return NGX_ERROR;
1844         }
1845     }
1846
1847     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1848
1849     h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
1850     if (h == NULL) {
1851         return NGX_ERROR;
1852     }
1853
1854     *h = ngx_http_log_handler;
1855
1856     return NGX_OK;
1857 }