src/stream/ngx_stream_log_module.c - nginx source code

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_stream.h>

  8. #if (NGX_ZLIB)
  9. #include <zlib.h>
  10. #endif


  11. typedef struct ngx_stream_log_op_s  ngx_stream_log_op_t;

  12. typedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s,
  13.     u_char *buf, ngx_stream_log_op_t *op);

  14. typedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s,
  15.     uintptr_t data);


  16. struct ngx_stream_log_op_s {
  17.     size_t                       len;
  18.     ngx_stream_log_op_getlen_pt  getlen;
  19.     ngx_stream_log_op_run_pt     run;
  20.     uintptr_t                    data;
  21. };


  22. typedef struct {
  23.     ngx_str_t                    name;
  24.     ngx_array_t                 *flushes;
  25.     ngx_array_t                 *ops;        /* array of ngx_stream_log_op_t */
  26. } ngx_stream_log_fmt_t;


  27. typedef struct {
  28.     ngx_array_t                  formats;    /* array of ngx_stream_log_fmt_t */
  29. } ngx_stream_log_main_conf_t;


  30. typedef struct {
  31.     u_char                      *start;
  32.     u_char                      *pos;
  33.     u_char                      *last;

  34.     ngx_event_t                 *event;
  35.     ngx_msec_t                   flush;
  36.     ngx_int_t                    gzip;
  37. } ngx_stream_log_buf_t;


  38. typedef struct {
  39.     ngx_array_t                 *lengths;
  40.     ngx_array_t                 *values;
  41. } ngx_stream_log_script_t;


  42. typedef struct {
  43.     ngx_open_file_t             *file;
  44.     ngx_stream_log_script_t     *script;
  45.     time_t                       disk_full_time;
  46.     time_t                       error_log_time;
  47.     ngx_syslog_peer_t           *syslog_peer;
  48.     ngx_stream_log_fmt_t        *format;
  49.     ngx_stream_complex_value_t  *filter;
  50. } ngx_stream_log_t;


  51. typedef struct {
  52.     ngx_array_t                 *logs;       /* array of ngx_stream_log_t */

  53.     ngx_open_file_cache_t       *open_file_cache;
  54.     time_t                       open_file_cache_valid;
  55.     ngx_uint_t                   open_file_cache_min_uses;

  56.     ngx_uint_t                   off;        /* unsigned  off:1 */
  57. } ngx_stream_log_srv_conf_t;


  58. typedef struct {
  59.     ngx_str_t                    name;
  60.     size_t                       len;
  61.     ngx_stream_log_op_run_pt     run;
  62. } ngx_stream_log_var_t;


  63. #define NGX_STREAM_LOG_ESCAPE_DEFAULT  0
  64. #define NGX_STREAM_LOG_ESCAPE_JSON     1
  65. #define NGX_STREAM_LOG_ESCAPE_NONE     2


  66. static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
  67.     u_char *buf, size_t len);
  68. static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s,
  69.     ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len);

  70. #if (NGX_ZLIB)
  71. static ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
  72.     ngx_int_t level, ngx_log_t *log);

  73. static void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size);
  74. static void ngx_stream_log_gzip_free(void *opaque, void *address);
  75. #endif

  76. static void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log);
  77. static void ngx_stream_log_flush_handler(ngx_event_t *ev);

  78. static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf,
  79.     ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t escape);
  80. static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s,
  81.     uintptr_t data);
  82. static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
  83.     ngx_stream_log_op_t *op);
  84. static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size);
  85. static size_t ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s,
  86.     uintptr_t data);
  87. static u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s,
  88.     u_char *buf, ngx_stream_log_op_t *op);
  89. static size_t ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s,
  90.     uintptr_t data);
  91. static u_char *ngx_stream_log_unescaped_variable(ngx_stream_session_t *s,
  92.     u_char *buf, ngx_stream_log_op_t *op);


  93. static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf);
  94. static void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf);
  95. static char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent,
  96.     void *child);
  97. static char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
  98.     void *conf);
  99. static char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
  100.     void *conf);
  101. static char *ngx_stream_log_compile_format(ngx_conf_t *cf,
  102.     ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
  103. static char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
  104.     void *conf);
  105. static ngx_int_t ngx_stream_log_init(ngx_conf_t *cf);


  106. static ngx_command_t  ngx_stream_log_commands[] = {

  107.     { ngx_string("log_format"),
  108.       NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE,
  109.       ngx_stream_log_set_format,
  110.       NGX_STREAM_MAIN_CONF_OFFSET,
  111.       0,
  112.       NULL },

  113.     { ngx_string("access_log"),
  114.       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
  115.       ngx_stream_log_set_log,
  116.       NGX_STREAM_SRV_CONF_OFFSET,
  117.       0,
  118.       NULL },

  119.     { ngx_string("open_log_file_cache"),
  120.       NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234,
  121.       ngx_stream_log_open_file_cache,
  122.       NGX_STREAM_SRV_CONF_OFFSET,
  123.       0,
  124.       NULL },

  125.       ngx_null_command
  126. };


  127. static ngx_stream_module_t  ngx_stream_log_module_ctx = {
  128.     NULL,                                  /* preconfiguration */
  129.     ngx_stream_log_init,                   /* postconfiguration */

  130.     ngx_stream_log_create_main_conf,       /* create main configuration */
  131.     NULL,                                  /* init main configuration */

  132.     ngx_stream_log_create_srv_conf,        /* create server configuration */
  133.     ngx_stream_log_merge_srv_conf          /* merge server configuration */
  134. };


  135. ngx_module_t  ngx_stream_log_module = {
  136.     NGX_MODULE_V1,
  137.     &ngx_stream_log_module_ctx,            /* module context */
  138.     ngx_stream_log_commands,               /* module directives */
  139.     NGX_STREAM_MODULE,                     /* module type */
  140.     NULL,                                  /* init master */
  141.     NULL,                                  /* init module */
  142.     NULL,                                  /* init process */
  143.     NULL,                                  /* init thread */
  144.     NULL,                                  /* exit thread */
  145.     NULL,                                  /* exit process */
  146.     NULL,                                  /* exit master */
  147.     NGX_MODULE_V1_PADDING
  148. };


  149. static ngx_int_t
  150. ngx_stream_log_handler(ngx_stream_session_t *s)
  151. {
  152.     u_char                     *line, *p;
  153.     size_t                      len, size;
  154.     ssize_t                     n;
  155.     ngx_str_t                   val;
  156.     ngx_uint_t                  i, l;
  157.     ngx_stream_log_t           *log;
  158.     ngx_stream_log_op_t        *op;
  159.     ngx_stream_log_buf_t       *buffer;
  160.     ngx_stream_log_srv_conf_t  *lscf;

  161.     ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  162.                    "stream log handler");

  163.     lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);

  164.     if (lscf->off || lscf->logs == NULL) {
  165.         return NGX_OK;
  166.     }

  167.     log = lscf->logs->elts;
  168.     for (l = 0; l < lscf->logs->nelts; l++) {

  169.         if (log[l].filter) {
  170.             if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) {
  171.                 return NGX_ERROR;
  172.             }

  173.             if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
  174.                 continue;
  175.             }
  176.         }

  177.         if (ngx_time() == log[l].disk_full_time) {

  178.             /*
  179.              * on FreeBSD writing to a full filesystem with enabled softupdates
  180.              * may block process for much longer time than writing to non-full
  181.              * filesystem, so we skip writing to a log for one second
  182.              */

  183.             continue;
  184.         }

  185.         ngx_stream_script_flush_no_cacheable_variables(s,
  186.                                                        log[l].format->flushes);

  187.         len = 0;
  188.         op = log[l].format->ops->elts;
  189.         for (i = 0; i < log[l].format->ops->nelts; i++) {
  190.             if (op[i].len == 0) {
  191.                 len += op[i].getlen(s, op[i].data);

  192.             } else {
  193.                 len += op[i].len;
  194.             }
  195.         }

  196.         if (log[l].syslog_peer) {

  197.             /* length of syslog's PRI and HEADER message parts */
  198.             len += sizeof("<255>Jan 01 00:00:00 ") - 1
  199.                    + ngx_cycle->hostname.len + 1
  200.                    + log[l].syslog_peer->tag.len + 2;

  201.             goto alloc_line;
  202.         }

  203.         len += NGX_LINEFEED_SIZE;

  204.         buffer = log[l].file ? log[l].file->data : NULL;

  205.         if (buffer) {

  206.             if (len > (size_t) (buffer->last - buffer->pos)) {

  207.                 ngx_stream_log_write(s, &log[l], buffer->start,
  208.                                      buffer->pos - buffer->start);

  209.                 buffer->pos = buffer->start;
  210.             }

  211.             if (len <= (size_t) (buffer->last - buffer->pos)) {

  212.                 p = buffer->pos;

  213.                 if (buffer->event && p == buffer->start) {
  214.                     ngx_add_timer(buffer->event, buffer->flush);
  215.                 }

  216.                 for (i = 0; i < log[l].format->ops->nelts; i++) {
  217.                     p = op[i].run(s, p, &op[i]);
  218.                 }

  219.                 ngx_linefeed(p);

  220.                 buffer->pos = p;

  221.                 continue;
  222.             }

  223.             if (buffer->event && buffer->event->timer_set) {
  224.                 ngx_del_timer(buffer->event);
  225.             }
  226.         }

  227.     alloc_line:

  228.         line = ngx_pnalloc(s->connection->pool, len);
  229.         if (line == NULL) {
  230.             return NGX_ERROR;
  231.         }

  232.         p = line;

  233.         if (log[l].syslog_peer) {
  234.             p = ngx_syslog_add_header(log[l].syslog_peer, line);
  235.         }

  236.         for (i = 0; i < log[l].format->ops->nelts; i++) {
  237.             p = op[i].run(s, p, &op[i]);
  238.         }

  239.         if (log[l].syslog_peer) {

  240.             size = p - line;

  241.             n = ngx_syslog_send(log[l].syslog_peer, line, size);

  242.             if (n < 0) {
  243.                 ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
  244.                               "send() to syslog failed");

  245.             } else if ((size_t) n != size) {
  246.                 ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
  247.                               "send() to syslog has written only %z of %uz",
  248.                               n, size);
  249.             }

  250.             continue;
  251.         }

  252.         ngx_linefeed(p);

  253.         ngx_stream_log_write(s, &log[l], line, p - line);
  254.     }

  255.     return NGX_OK;
  256. }


  257. static void
  258. ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
  259.     u_char *buf, size_t len)
  260. {
  261.     u_char                *name;
  262.     time_t                 now;
  263.     ssize_t                n;
  264.     ngx_err_t              err;
  265. #if (NGX_ZLIB)
  266.     ngx_stream_log_buf_t  *buffer;
  267. #endif

  268.     if (log->script == NULL) {
  269.         name = log->file->name.data;

  270. #if (NGX_ZLIB)
  271.         buffer = log->file->data;

  272.         if (buffer && buffer->gzip) {
  273.             n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip,
  274.                                     s->connection->log);
  275.         } else {
  276.             n = ngx_write_fd(log->file->fd, buf, len);
  277.         }
  278. #else
  279.         n = ngx_write_fd(log->file->fd, buf, len);
  280. #endif

  281.     } else {
  282.         name = NULL;
  283.         n = ngx_stream_log_script_write(s, log->script, &name, buf, len);
  284.     }

  285.     if (n == (ssize_t) len) {
  286.         return;
  287.     }

  288.     now = ngx_time();

  289.     if (n == -1) {
  290.         err = ngx_errno;

  291.         if (err == NGX_ENOSPC) {
  292.             log->disk_full_time = now;
  293.         }

  294.         if (now - log->error_log_time > 59) {
  295.             ngx_log_error(NGX_LOG_ALERT, s->connection->log, err,
  296.                           ngx_write_fd_n " to \"%s\" failed", name);

  297.             log->error_log_time = now;
  298.         }

  299.         return;
  300.     }

  301.     if (now - log->error_log_time > 59) {
  302.         ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
  303.                       ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
  304.                       name, n, len);

  305.         log->error_log_time = now;
  306.     }
  307. }


  308. static ssize_t
  309. ngx_stream_log_script_write(ngx_stream_session_t *s,
  310.     ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len)
  311. {
  312.     ssize_t                     n;
  313.     ngx_str_t                   log;
  314.     ngx_open_file_info_t        of;
  315.     ngx_stream_log_srv_conf_t  *lscf;

  316.     if (ngx_stream_script_run(s, &log, script->lengths->elts, 1,
  317.                               script->values->elts)
  318.         == NULL)
  319.     {
  320.         /* simulate successful logging */
  321.         return len;
  322.     }

  323.     log.data[log.len - 1] = '\0';
  324.     *name = log.data;

  325.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  326.                    "stream log \"%s\"", log.data);

  327.     lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);

  328.     ngx_memzero(&of, sizeof(ngx_open_file_info_t));

  329.     of.log = 1;
  330.     of.valid = lscf->open_file_cache_valid;
  331.     of.min_uses = lscf->open_file_cache_min_uses;
  332.     of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;

  333.     if (ngx_open_cached_file(lscf->open_file_cache, &log, &of,
  334.                              s->connection->pool)
  335.         != NGX_OK)
  336.     {
  337.         if (of.err == 0) {
  338.             /* simulate successful logging */
  339.             return len;
  340.         }

  341.         ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
  342.                       "%s \"%s\" failed", of.failed, log.data);
  343.         /* simulate successful logging */
  344.         return len;
  345.     }

  346.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
  347.                    "stream log #%d", of.fd);

  348.     n = ngx_write_fd(of.fd, buf, len);

  349.     return n;
  350. }


  351. #if (NGX_ZLIB)

  352. static ssize_t
  353. ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
  354.     ngx_log_t *log)
  355. {
  356.     int          rc, wbits, memlevel;
  357.     u_char      *out;
  358.     size_t       size;
  359.     ssize_t      n;
  360.     z_stream     zstream;
  361.     ngx_err_t    err;
  362.     ngx_pool_t  *pool;

  363.     wbits = MAX_WBITS;
  364.     memlevel = MAX_MEM_LEVEL - 1;

  365.     while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
  366.         wbits--;
  367.         memlevel--;
  368.     }

  369.     /*
  370.      * This is a formula from deflateBound() for conservative upper bound of
  371.      * compressed data plus 18 bytes of gzip wrapper.
  372.      */

  373.     size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;

  374.     ngx_memzero(&zstream, sizeof(z_stream));

  375.     pool = ngx_create_pool(256, log);
  376.     if (pool == NULL) {
  377.         /* simulate successful logging */
  378.         return len;
  379.     }

  380.     pool->log = log;

  381.     zstream.zalloc = ngx_stream_log_gzip_alloc;
  382.     zstream.zfree = ngx_stream_log_gzip_free;
  383.     zstream.opaque = pool;

  384.     out = ngx_pnalloc(pool, size);
  385.     if (out == NULL) {
  386.         goto done;
  387.     }

  388.     zstream.next_in = buf;
  389.     zstream.avail_in = len;
  390.     zstream.next_out = out;
  391.     zstream.avail_out = size;

  392.     rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
  393.                       Z_DEFAULT_STRATEGY);

  394.     if (rc != Z_OK) {
  395.         ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
  396.         goto done;
  397.     }

  398.     ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,
  399.                    "deflate in: ni:%p no:%p ai:%ud ao:%ud",
  400.                    zstream.next_in, zstream.next_out,
  401.                    zstream.avail_in, zstream.avail_out);

  402.     rc = deflate(&zstream, Z_FINISH);

  403.     if (rc != Z_STREAM_END) {
  404.         ngx_log_error(NGX_LOG_ALERT, log, 0,
  405.                       "deflate(Z_FINISH) failed: %d", rc);
  406.         goto done;
  407.     }

  408.     ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0,
  409.                    "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
  410.                    zstream.next_in, zstream.next_out,
  411.                    zstream.avail_in, zstream.avail_out,
  412.                    rc);

  413.     size -= zstream.avail_out;

  414.     rc = deflateEnd(&zstream);

  415.     if (rc != Z_OK) {
  416.         ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
  417.         goto done;
  418.     }

  419.     n = ngx_write_fd(fd, out, size);

  420.     if (n != (ssize_t) size) {
  421.         err = (n == -1) ? ngx_errno : 0;

  422.         ngx_destroy_pool(pool);

  423.         ngx_set_errno(err);
  424.         return -1;
  425.     }

  426. done:

  427.     ngx_destroy_pool(pool);

  428.     /* simulate successful logging */
  429.     return len;
  430. }


  431. static void *
  432. ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size)
  433. {
  434.     ngx_pool_t *pool = opaque;

  435.     ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0,
  436.                    "gzip alloc: n:%ud s:%ud", items, size);

  437.     return ngx_palloc(pool, items * size);
  438. }


  439. static void
  440. ngx_stream_log_gzip_free(void *opaque, void *address)
  441. {
  442. #if 0
  443.     ngx_pool_t *pool = opaque;

  444.     ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0,
  445.                    "gzip free: %p", address);
  446. #endif
  447. }

  448. #endif


  449. static void
  450. ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log)
  451. {
  452.     size_t                 len;
  453.     ssize_t                n;
  454.     ngx_stream_log_buf_t  *buffer;

  455.     buffer = file->data;

  456.     len = buffer->pos - buffer->start;

  457.     if (len == 0) {
  458.         return;
  459.     }

  460. #if (NGX_ZLIB)
  461.     if (buffer->gzip) {
  462.         n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip,
  463.                                 log);
  464.     } else {
  465.         n = ngx_write_fd(file->fd, buffer->start, len);
  466.     }
  467. #else
  468.     n = ngx_write_fd(file->fd, buffer->start, len);
  469. #endif

  470.     if (n == -1) {
  471.         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
  472.                       ngx_write_fd_n " to \"%s\" failed",
  473.                       file->name.data);

  474.     } else if ((size_t) n != len) {
  475.         ngx_log_error(NGX_LOG_ALERT, log, 0,
  476.                       ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
  477.                       file->name.data, n, len);
  478.     }

  479.     buffer->pos = buffer->start;

  480.     if (buffer->event && buffer->event->timer_set) {
  481.         ngx_del_timer(buffer->event);
  482.     }
  483. }


  484. static void
  485. ngx_stream_log_flush_handler(ngx_event_t *ev)
  486. {
  487.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
  488.                    "stream log buffer flush handler");

  489.     ngx_stream_log_flush(ev->data, ev->log);
  490. }


  491. static u_char *
  492. ngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf,
  493.     ngx_stream_log_op_t *op)
  494. {
  495.     size_t     len;
  496.     uintptr_t  data;

  497.     len = op->len;
  498.     data = op->data;

  499.     while (len--) {
  500.         *buf++ = (u_char) (data & 0xff);
  501.         data >>= 8;
  502.     }

  503.     return buf;
  504. }


  505. static u_char *
  506. ngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf,
  507.     ngx_stream_log_op_t *op)
  508. {
  509.     return ngx_cpymem(buf, (u_char *) op->data, op->len);
  510. }


  511. static ngx_int_t
  512. ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op,
  513.     ngx_str_t *value, ngx_uint_t escape)
  514. {
  515.     ngx_int_t  index;

  516.     index = ngx_stream_get_variable_index(cf, value);
  517.     if (index == NGX_ERROR) {
  518.         return NGX_ERROR;
  519.     }

  520.     op->len = 0;

  521.     switch (escape) {
  522.     case NGX_STREAM_LOG_ESCAPE_JSON:
  523.         op->getlen = ngx_stream_log_json_variable_getlen;
  524.         op->run = ngx_stream_log_json_variable;
  525.         break;

  526.     case NGX_STREAM_LOG_ESCAPE_NONE:
  527.         op->getlen = ngx_stream_log_unescaped_variable_getlen;
  528.         op->run = ngx_stream_log_unescaped_variable;
  529.         break;

  530.     default: /* NGX_STREAM_LOG_ESCAPE_DEFAULT */
  531.         op->getlen = ngx_stream_log_variable_getlen;
  532.         op->run = ngx_stream_log_variable;
  533.     }

  534.     op->data = index;

  535.     return NGX_OK;
  536. }


  537. static size_t
  538. ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
  539. {
  540.     uintptr_t                     len;
  541.     ngx_stream_variable_value_t  *value;

  542.     value = ngx_stream_get_indexed_variable(s, data);

  543.     if (value == NULL || value->not_found) {
  544.         return 1;
  545.     }

  546.     len = ngx_stream_log_escape(NULL, value->data, value->len);

  547.     value->escape = len ? 1 : 0;

  548.     return value->len + len * 3;
  549. }


  550. static u_char *
  551. ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
  552.     ngx_stream_log_op_t *op)
  553. {
  554.     ngx_stream_variable_value_t  *value;

  555.     value = ngx_stream_get_indexed_variable(s, op->data);

  556.     if (value == NULL || value->not_found) {
  557.         *buf = '-';
  558.         return buf + 1;
  559.     }

  560.     if (value->escape == 0) {
  561.         return ngx_cpymem(buf, value->data, value->len);

  562.     } else {
  563.         return (u_char *) ngx_stream_log_escape(buf, value->data, value->len);
  564.     }
  565. }


  566. static uintptr_t
  567. ngx_stream_log_escape(u_char *dst, u_char *src, size_t size)
  568. {
  569.     ngx_uint_t      n;
  570.     static u_char   hex[] = "0123456789ABCDEF";

  571.     static uint32_t   escape[] = {
  572.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */

  573.                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
  574.         0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */

  575.                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
  576.         0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */

  577.                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
  578.         0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */

  579.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  580.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  581.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  582.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  583.     };


  584.     if (dst == NULL) {

  585.         /* find the number of the characters to be escaped */

  586.         n = 0;

  587.         while (size) {
  588.             if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
  589.                 n++;
  590.             }
  591.             src++;
  592.             size--;
  593.         }

  594.         return (uintptr_t) n;
  595.     }

  596.     while (size) {
  597.         if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
  598.             *dst++ = '\\';
  599.             *dst++ = 'x';
  600.             *dst++ = hex[*src >> 4];
  601.             *dst++ = hex[*src & 0xf];
  602.             src++;

  603.         } else {
  604.             *dst++ = *src++;
  605.         }
  606.         size--;
  607.     }

  608.     return (uintptr_t) dst;
  609. }


  610. static size_t
  611. ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
  612. {
  613.     uintptr_t                     len;
  614.     ngx_stream_variable_value_t  *value;

  615.     value = ngx_stream_get_indexed_variable(s, data);

  616.     if (value == NULL || value->not_found) {
  617.         return 0;
  618.     }

  619.     len = ngx_escape_json(NULL, value->data, value->len);

  620.     value->escape = len ? 1 : 0;

  621.     return value->len + len;
  622. }


  623. static u_char *
  624. ngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf,
  625.     ngx_stream_log_op_t *op)
  626. {
  627.     ngx_stream_variable_value_t  *value;

  628.     value = ngx_stream_get_indexed_variable(s, op->data);

  629.     if (value == NULL || value->not_found) {
  630.         return buf;
  631.     }

  632.     if (value->escape == 0) {
  633.         return ngx_cpymem(buf, value->data, value->len);

  634.     } else {
  635.         return (u_char *) ngx_escape_json(buf, value->data, value->len);
  636.     }
  637. }


  638. static size_t
  639. ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s,
  640.     uintptr_t data)
  641. {
  642.     ngx_stream_variable_value_t  *value;

  643.     value = ngx_stream_get_indexed_variable(s, data);

  644.     if (value == NULL || value->not_found) {
  645.         return 0;
  646.     }

  647.     value->escape = 0;

  648.     return value->len;
  649. }


  650. static u_char *
  651. ngx_stream_log_unescaped_variable(ngx_stream_session_t *s, u_char *buf,
  652.                                   ngx_stream_log_op_t *op)
  653. {
  654.     ngx_stream_variable_value_t  *value;

  655.     value = ngx_stream_get_indexed_variable(s, op->data);

  656.     if (value == NULL || value->not_found) {
  657.         return buf;
  658.     }

  659.     return ngx_cpymem(buf, value->data, value->len);
  660. }


  661. static void *
  662. ngx_stream_log_create_main_conf(ngx_conf_t *cf)
  663. {
  664.     ngx_stream_log_main_conf_t  *conf;

  665.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t));
  666.     if (conf == NULL) {
  667.         return NULL;
  668.     }

  669.     if (ngx_array_init(&conf->formats, cf->pool, 4,
  670.                        sizeof(ngx_stream_log_fmt_t))
  671.         != NGX_OK)
  672.     {
  673.         return NULL;
  674.     }

  675.     return conf;
  676. }


  677. static void *
  678. ngx_stream_log_create_srv_conf(ngx_conf_t *cf)
  679. {
  680.     ngx_stream_log_srv_conf_t  *conf;

  681.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t));
  682.     if (conf == NULL) {
  683.         return NULL;
  684.     }

  685.     conf->open_file_cache = NGX_CONF_UNSET_PTR;

  686.     return conf;
  687. }


  688. static char *
  689. ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
  690. {
  691.     ngx_stream_log_srv_conf_t *prev = parent;
  692.     ngx_stream_log_srv_conf_t *conf = child;

  693.     if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {

  694.         conf->open_file_cache = prev->open_file_cache;
  695.         conf->open_file_cache_valid = prev->open_file_cache_valid;
  696.         conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;

  697.         if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
  698.             conf->open_file_cache = NULL;
  699.         }
  700.     }

  701.     if (conf->logs || conf->off) {
  702.         return NGX_CONF_OK;
  703.     }

  704.     conf->logs = prev->logs;
  705.     conf->off = prev->off;

  706.     return NGX_CONF_OK;
  707. }


  708. static char *
  709. ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  710. {
  711.     ngx_stream_log_srv_conf_t *lscf = conf;

  712.     ssize_t                              size;
  713.     ngx_int_t                            gzip;
  714.     ngx_uint_t                           i, n;
  715.     ngx_msec_t                           flush;
  716.     ngx_str_t                           *value, name, s;
  717.     ngx_stream_log_t                    *log;
  718.     ngx_syslog_peer_t                   *peer;
  719.     ngx_stream_log_buf_t                *buffer;
  720.     ngx_stream_log_fmt_t                *fmt;
  721.     ngx_stream_script_compile_t          sc;
  722.     ngx_stream_log_main_conf_t          *lmcf;
  723.     ngx_stream_compile_complex_value_t   ccv;

  724.     value = cf->args->elts;

  725.     if (ngx_strcmp(value[1].data, "off") == 0) {
  726.         lscf->off = 1;
  727.         if (cf->args->nelts == 2) {
  728.             return NGX_CONF_OK;
  729.         }

  730.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  731.                            "invalid parameter \"%V\"", &value[2]);
  732.         return NGX_CONF_ERROR;
  733.     }

  734.     if (lscf->logs == NULL) {
  735.         lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t));
  736.         if (lscf->logs == NULL) {
  737.             return NGX_CONF_ERROR;
  738.         }
  739.     }

  740.     lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module);

  741.     log = ngx_array_push(lscf->logs);
  742.     if (log == NULL) {
  743.         return NGX_CONF_ERROR;
  744.     }

  745.     ngx_memzero(log, sizeof(ngx_stream_log_t));


  746.     if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {

  747.         peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
  748.         if (peer == NULL) {
  749.             return NGX_CONF_ERROR;
  750.         }

  751.         if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
  752.             return NGX_CONF_ERROR;
  753.         }

  754.         log->syslog_peer = peer;

  755.         goto process_formats;
  756.     }

  757.     n = ngx_stream_script_variables_count(&value[1]);

  758.     if (n == 0) {
  759.         log->file = ngx_conf_open_file(cf->cycle, &value[1]);
  760.         if (log->file == NULL) {
  761.             return NGX_CONF_ERROR;
  762.         }

  763.     } else {
  764.         if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
  765.             return NGX_CONF_ERROR;
  766.         }

  767.         log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t));
  768.         if (log->script == NULL) {
  769.             return NGX_CONF_ERROR;
  770.         }

  771.         ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));

  772.         sc.cf = cf;
  773.         sc.source = &value[1];
  774.         sc.lengths = &log->script->lengths;
  775.         sc.values = &log->script->values;
  776.         sc.variables = n;
  777.         sc.complete_lengths = 1;
  778.         sc.complete_values = 1;

  779.         if (ngx_stream_script_compile(&sc) != NGX_OK) {
  780.             return NGX_CONF_ERROR;
  781.         }
  782.     }

  783. process_formats:

  784.     if (cf->args->nelts >= 3) {
  785.         name = value[2];

  786.     } else {
  787.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  788.                            "log format is not specified");
  789.         return NGX_CONF_ERROR;
  790.     }

  791.     fmt = lmcf->formats.elts;
  792.     for (i = 0; i < lmcf->formats.nelts; i++) {
  793.         if (fmt[i].name.len == name.len
  794.             && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
  795.         {
  796.             log->format = &fmt[i];
  797.             break;
  798.         }
  799.     }

  800.     if (log->format == NULL) {
  801.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  802.                            "unknown log format \"%V\"", &name);
  803.         return NGX_CONF_ERROR;
  804.     }

  805.     size = 0;
  806.     flush = 0;
  807.     gzip = 0;

  808.     for (i = 3; i < cf->args->nelts; i++) {

  809.         if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
  810.             s.len = value[i].len - 7;
  811.             s.data = value[i].data + 7;

  812.             size = ngx_parse_size(&s);

  813.             if (size == NGX_ERROR || size == 0) {
  814.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  815.                                    "invalid buffer size \"%V\"", &s);
  816.                 return NGX_CONF_ERROR;
  817.             }

  818.             continue;
  819.         }

  820.         if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
  821.             s.len = value[i].len - 6;
  822.             s.data = value[i].data + 6;

  823.             flush = ngx_parse_time(&s, 0);

  824.             if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
  825.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  826.                                    "invalid flush time \"%V\"", &s);
  827.                 return NGX_CONF_ERROR;
  828.             }

  829.             continue;
  830.         }

  831.         if (ngx_strncmp(value[i].data, "gzip", 4) == 0
  832.             && (value[i].len == 4 || value[i].data[4] == '='))
  833.         {
  834. #if (NGX_ZLIB)
  835.             if (size == 0) {
  836.                 size = 64 * 1024;
  837.             }

  838.             if (value[i].len == 4) {
  839.                 gzip = Z_BEST_SPEED;
  840.                 continue;
  841.             }

  842.             s.len = value[i].len - 5;
  843.             s.data = value[i].data + 5;

  844.             gzip = ngx_atoi(s.data, s.len);

  845.             if (gzip < 1 || gzip > 9) {
  846.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  847.                                    "invalid compression level \"%V\"", &s);
  848.                 return NGX_CONF_ERROR;
  849.             }

  850.             continue;

  851. #else
  852.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  853.                                "nginx was built without zlib support");
  854.             return NGX_CONF_ERROR;
  855. #endif
  856.         }

  857.         if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
  858.             s.len = value[i].len - 3;
  859.             s.data = value[i].data + 3;

  860.             ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));

  861.             ccv.cf = cf;
  862.             ccv.value = &s;
  863.             ccv.complex_value = ngx_palloc(cf->pool,
  864.                                            sizeof(ngx_stream_complex_value_t));
  865.             if (ccv.complex_value == NULL) {
  866.                 return NGX_CONF_ERROR;
  867.             }

  868.             if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
  869.                 return NGX_CONF_ERROR;
  870.             }

  871.             log->filter = ccv.complex_value;

  872.             continue;
  873.         }

  874.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  875.                            "invalid parameter \"%V\"", &value[i]);
  876.         return NGX_CONF_ERROR;
  877.     }

  878.     if (flush && size == 0) {
  879.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  880.                            "no buffer is defined for access_log \"%V\"",
  881.                            &value[1]);
  882.         return NGX_CONF_ERROR;
  883.     }

  884.     if (size) {

  885.         if (log->script) {
  886.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  887.                                "buffered logs cannot have variables in name");
  888.             return NGX_CONF_ERROR;
  889.         }

  890.         if (log->syslog_peer) {
  891.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  892.                                "logs to syslog cannot be buffered");
  893.             return NGX_CONF_ERROR;
  894.         }

  895.         if (log->file->data) {
  896.             buffer = log->file->data;

  897.             if (buffer->last - buffer->start != size
  898.                 || buffer->flush != flush
  899.                 || buffer->gzip != gzip)
  900.             {
  901.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  902.                                    "access_log \"%V\" already defined "
  903.                                    "with conflicting parameters",
  904.                                    &value[1]);
  905.                 return NGX_CONF_ERROR;
  906.             }

  907.             return NGX_CONF_OK;
  908.         }

  909.         buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t));
  910.         if (buffer == NULL) {
  911.             return NGX_CONF_ERROR;
  912.         }

  913.         buffer->start = ngx_pnalloc(cf->pool, size);
  914.         if (buffer->start == NULL) {
  915.             return NGX_CONF_ERROR;
  916.         }

  917.         buffer->pos = buffer->start;
  918.         buffer->last = buffer->start + size;

  919.         if (flush) {
  920.             buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
  921.             if (buffer->event == NULL) {
  922.                 return NGX_CONF_ERROR;
  923.             }

  924.             buffer->event->data = log->file;
  925.             buffer->event->handler = ngx_stream_log_flush_handler;
  926.             buffer->event->log = &cf->cycle->new_log;
  927.             buffer->event->cancelable = 1;

  928.             buffer->flush = flush;
  929.         }

  930.         buffer->gzip = gzip;

  931.         log->file->flush = ngx_stream_log_flush;
  932.         log->file->data = buffer;
  933.     }

  934.     return NGX_CONF_OK;
  935. }


  936. static char *
  937. ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  938. {
  939.     ngx_stream_log_main_conf_t *lmcf = conf;

  940.     ngx_str_t             *value;
  941.     ngx_uint_t             i;
  942.     ngx_stream_log_fmt_t  *fmt;

  943.     value = cf->args->elts;

  944.     fmt = lmcf->formats.elts;
  945.     for (i = 0; i < lmcf->formats.nelts; i++) {
  946.         if (fmt[i].name.len == value[1].len
  947.             && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
  948.         {
  949.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  950.                                "duplicate \"log_format\" name \"%V\"",
  951.                                &value[1]);
  952.             return NGX_CONF_ERROR;
  953.         }
  954.     }

  955.     fmt = ngx_array_push(&lmcf->formats);
  956.     if (fmt == NULL) {
  957.         return NGX_CONF_ERROR;
  958.     }

  959.     fmt->name = value[1];

  960.     fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
  961.     if (fmt->flushes == NULL) {
  962.         return NGX_CONF_ERROR;
  963.     }

  964.     fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t));
  965.     if (fmt->ops == NULL) {
  966.         return NGX_CONF_ERROR;
  967.     }

  968.     return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops,
  969.                                          cf->args, 2);
  970. }


  971. static char *
  972. ngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
  973.     ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
  974. {
  975.     u_char                *data, *p, ch;
  976.     size_t                 i, len;
  977.     ngx_str_t             *value, var;
  978.     ngx_int_t             *flush;
  979.     ngx_uint_t             bracket, escape;
  980.     ngx_stream_log_op_t   *op;

  981.     escape = NGX_STREAM_LOG_ESCAPE_DEFAULT;
  982.     value = args->elts;

  983.     if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
  984.         data = value[s].data + 7;

  985.         if (ngx_strcmp(data, "json") == 0) {
  986.             escape = NGX_STREAM_LOG_ESCAPE_JSON;

  987.         } else if (ngx_strcmp(data, "none") == 0) {
  988.             escape = NGX_STREAM_LOG_ESCAPE_NONE;

  989.         } else if (ngx_strcmp(data, "default") != 0) {
  990.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  991.                                "unknown log format escaping \"%s\"", data);
  992.             return NGX_CONF_ERROR;
  993.         }

  994.         s++;
  995.     }

  996.     for ( /* void */ ; s < args->nelts; s++) {

  997.         i = 0;

  998.         while (i < value[s].len) {

  999.             op = ngx_array_push(ops);
  1000.             if (op == NULL) {
  1001.                 return NGX_CONF_ERROR;
  1002.             }

  1003.             data = &value[s].data[i];

  1004.             if (value[s].data[i] == '$') {

  1005.                 if (++i == value[s].len) {
  1006.                     goto invalid;
  1007.                 }

  1008.                 if (value[s].data[i] == '{') {
  1009.                     bracket = 1;

  1010.                     if (++i == value[s].len) {
  1011.                         goto invalid;
  1012.                     }

  1013.                     var.data = &value[s].data[i];

  1014.                 } else {
  1015.                     bracket = 0;
  1016.                     var.data = &value[s].data[i];
  1017.                 }

  1018.                 for (var.len = 0; i < value[s].len; i++, var.len++) {
  1019.                     ch = value[s].data[i];

  1020.                     if (ch == '}' && bracket) {
  1021.                         i++;
  1022.                         bracket = 0;
  1023.                         break;
  1024.                     }

  1025.                     if ((ch >= 'A' && ch <= 'Z')
  1026.                         || (ch >= 'a' && ch <= 'z')
  1027.                         || (ch >= '0' && ch <= '9')
  1028.                         || ch == '_')
  1029.                     {
  1030.                         continue;
  1031.                     }

  1032.                     break;
  1033.                 }

  1034.                 if (bracket) {
  1035.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1036.                                        "the closing bracket in \"%V\" "
  1037.                                        "variable is missing", &var);
  1038.                     return NGX_CONF_ERROR;
  1039.                 }

  1040.                 if (var.len == 0) {
  1041.                     goto invalid;
  1042.                 }

  1043.                 if (ngx_stream_log_variable_compile(cf, op, &var, escape)
  1044.                     != NGX_OK)
  1045.                 {
  1046.                     return NGX_CONF_ERROR;
  1047.                 }

  1048.                 if (flushes) {

  1049.                     flush = ngx_array_push(flushes);
  1050.                     if (flush == NULL) {
  1051.                         return NGX_CONF_ERROR;
  1052.                     }

  1053.                     *flush = op->data; /* variable index */
  1054.                 }

  1055.                 continue;
  1056.             }

  1057.             i++;

  1058.             while (i < value[s].len && value[s].data[i] != '$') {
  1059.                 i++;
  1060.             }

  1061.             len = &value[s].data[i] - data;

  1062.             if (len) {

  1063.                 op->len = len;
  1064.                 op->getlen = NULL;

  1065.                 if (len <= sizeof(uintptr_t)) {
  1066.                     op->run = ngx_stream_log_copy_short;
  1067.                     op->data = 0;

  1068.                     while (len--) {
  1069.                         op->data <<= 8;
  1070.                         op->data |= data[len];
  1071.                     }

  1072.                 } else {
  1073.                     op->run = ngx_stream_log_copy_long;

  1074.                     p = ngx_pnalloc(cf->pool, len);
  1075.                     if (p == NULL) {
  1076.                         return NGX_CONF_ERROR;
  1077.                     }

  1078.                     ngx_memcpy(p, data, len);
  1079.                     op->data = (uintptr_t) p;
  1080.                 }
  1081.             }
  1082.         }
  1083.     }

  1084.     return NGX_CONF_OK;

  1085. invalid:

  1086.     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);

  1087.     return NGX_CONF_ERROR;
  1088. }


  1089. static char *
  1090. ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1091. {
  1092.     ngx_stream_log_srv_conf_t *lscf = conf;

  1093.     time_t       inactive, valid;
  1094.     ngx_str_t   *value, s;
  1095.     ngx_int_t    max, min_uses;
  1096.     ngx_uint_t   i;

  1097.     if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) {
  1098.         return "is duplicate";
  1099.     }

  1100.     value = cf->args->elts;

  1101.     max = 0;
  1102.     inactive = 10;
  1103.     valid = 60;
  1104.     min_uses = 1;

  1105.     for (i = 1; i < cf->args->nelts; i++) {

  1106.         if (ngx_strncmp(value[i].data, "max=", 4) == 0) {

  1107.             max = ngx_atoi(value[i].data + 4, value[i].len - 4);
  1108.             if (max == NGX_ERROR) {
  1109.                 goto failed;
  1110.             }

  1111.             continue;
  1112.         }

  1113.         if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {

  1114.             s.len = value[i].len - 9;
  1115.             s.data = value[i].data + 9;

  1116.             inactive = ngx_parse_time(&s, 1);
  1117.             if (inactive == (time_t) NGX_ERROR) {
  1118.                 goto failed;
  1119.             }

  1120.             continue;
  1121.         }

  1122.         if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {

  1123.             min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
  1124.             if (min_uses == NGX_ERROR) {
  1125.                 goto failed;
  1126.             }

  1127.             continue;
  1128.         }

  1129.         if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {

  1130.             s.len = value[i].len - 6;
  1131.             s.data = value[i].data + 6;

  1132.             valid = ngx_parse_time(&s, 1);
  1133.             if (valid == (time_t) NGX_ERROR) {
  1134.                 goto failed;
  1135.             }

  1136.             continue;
  1137.         }

  1138.         if (ngx_strcmp(value[i].data, "off") == 0) {

  1139.             lscf->open_file_cache = NULL;

  1140.             continue;
  1141.         }

  1142.     failed:

  1143.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1144.                            "invalid \"open_log_file_cache\" parameter \"%V\"",
  1145.                            &value[i]);
  1146.         return NGX_CONF_ERROR;
  1147.     }

  1148.     if (lscf->open_file_cache == NULL) {
  1149.         return NGX_CONF_OK;
  1150.     }

  1151.     if (max == 0) {
  1152.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1153.                         "\"open_log_file_cache\" must have \"max\" parameter");
  1154.         return NGX_CONF_ERROR;
  1155.     }

  1156.     lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);

  1157.     if (lscf->open_file_cache) {

  1158.         lscf->open_file_cache_valid = valid;
  1159.         lscf->open_file_cache_min_uses = min_uses;

  1160.         return NGX_CONF_OK;
  1161.     }

  1162.     return NGX_CONF_ERROR;
  1163. }


  1164. static ngx_int_t
  1165. ngx_stream_log_init(ngx_conf_t *cf)
  1166. {
  1167.     ngx_stream_handler_pt        *h;
  1168.     ngx_stream_core_main_conf_t  *cmcf;

  1169.     cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);

  1170.     h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers);
  1171.     if (h == NULL) {
  1172.         return NGX_ERROR;
  1173.     }

  1174.     *h = ngx_stream_log_handler;

  1175.     return NGX_OK;
  1176. }