xref: /unit/src/nxt_router_access_log.c (revision 2247:baa6b9879267)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Valentin V. Bartenev
5  * Copyright (C) NGINX, Inc.
6  */
7 
8 #include <nxt_router.h>
9 #include <nxt_conf.h>
10 #include <nxt_http.h>
11 
12 
13 typedef struct {
14     nxt_str_t                 path;
15     nxt_str_t                 format;
16 } nxt_router_access_log_conf_t;
17 
18 
19 typedef struct {
20     nxt_str_t                 text;
21     nxt_router_access_log_t   *access_log;
22 } nxt_router_access_log_ctx_t;
23 
24 
25 static void nxt_router_access_log_writer(nxt_task_t *task,
26     nxt_http_request_t *r, nxt_router_access_log_t *access_log,
27     nxt_tstr_t *format);
28 static void nxt_router_access_log_write_ready(nxt_task_t *task, void *obj,
29     void *data);
30 static void nxt_router_access_log_write_error(nxt_task_t *task, void *obj,
31     void *data);
32 static void nxt_router_access_log_ready(nxt_task_t *task,
33     nxt_port_recv_msg_t *msg, void *data);
34 static void nxt_router_access_log_error(nxt_task_t *task,
35     nxt_port_recv_msg_t *msg, void *data);
36 static void nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj,
37     void *data);
38 static void nxt_router_access_log_reopen_ready(nxt_task_t *task,
39     nxt_port_recv_msg_t *msg, void *data);
40 static void nxt_router_access_log_reopen_error(nxt_task_t *task,
41     nxt_port_recv_msg_t *msg, void *data);
42 
43 
44 static nxt_conf_map_t  nxt_router_access_log_conf[] = {
45     {
46         nxt_string("path"),
47         NXT_CONF_MAP_STR,
48         offsetof(nxt_router_access_log_conf_t, path),
49     },
50 
51     {
52         nxt_string("format"),
53         NXT_CONF_MAP_STR,
54         offsetof(nxt_router_access_log_conf_t, format),
55     },
56 };
57 
58 
59 nxt_int_t
nxt_router_access_log_create(nxt_task_t * task,nxt_router_conf_t * rtcf,nxt_conf_value_t * value)60 nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
61     nxt_conf_value_t *value)
62 {
63     u_char                        *p;
64     nxt_int_t                     ret;
65     nxt_str_t                     str;
66     nxt_tstr_t                    *format;
67     nxt_router_t                  *router;
68     nxt_router_access_log_t       *access_log;
69     nxt_router_access_log_conf_t  alcf;
70 
71     static nxt_str_t  log_format_str = nxt_string("$remote_addr - - "
72         "[$time_local] \"$request_line\" $status $body_bytes_sent "
73         "\"$header_referer\" \"$header_user_agent\"");
74 
75     alcf.format = log_format_str;
76 
77     if (nxt_conf_type(value) == NXT_CONF_STRING) {
78         nxt_conf_get_string(value, &alcf.path);
79 
80     } else {
81         ret = nxt_conf_map_object(rtcf->mem_pool, value,
82                                   nxt_router_access_log_conf,
83                                   nxt_nitems(nxt_router_access_log_conf),
84                                   &alcf);
85         if (ret != NXT_OK) {
86             nxt_alert(task, "access log map error");
87             return NXT_ERROR;
88         }
89     }
90 
91     router = nxt_router;
92 
93     access_log = router->access_log;
94 
95     if (access_log != NULL && nxt_strstr_eq(&alcf.path, &access_log->path)) {
96         nxt_router_access_log_use(&router->lock, access_log);
97 
98     } else {
99         access_log = nxt_malloc(sizeof(nxt_router_access_log_t)
100                                 + alcf.path.length);
101         if (access_log == NULL) {
102             nxt_alert(task, "failed to allocate access log structure");
103             return NXT_ERROR;
104         }
105 
106         access_log->fd = -1;
107         access_log->handler = &nxt_router_access_log_writer;
108         access_log->count = 1;
109 
110         access_log->path.length = alcf.path.length;
111         access_log->path.start = (u_char *) access_log
112                                  + sizeof(nxt_router_access_log_t);
113 
114         nxt_memcpy(access_log->path.start, alcf.path.start, alcf.path.length);
115     }
116 
117     str.length = alcf.format.length + 1;
118 
119     str.start = nxt_malloc(str.length);
120     if (str.start == NULL) {
121         nxt_alert(task, "failed to allocate log format structure");
122         return NXT_ERROR;
123     }
124 
125     p = nxt_cpymem(str.start, alcf.format.start, alcf.format.length);
126     *p = '\n';
127 
128     format = nxt_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_LOGGING);
129     if (nxt_slow_path(format == NULL)) {
130         return NXT_ERROR;
131     }
132 
133     rtcf->access_log = access_log;
134     rtcf->log_format = format;
135 
136     return NXT_OK;
137 }
138 
139 
140 static void
nxt_router_access_log_writer(nxt_task_t * task,nxt_http_request_t * r,nxt_router_access_log_t * access_log,nxt_tstr_t * format)141 nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
142     nxt_router_access_log_t *access_log, nxt_tstr_t *format)
143 {
144     nxt_int_t                    ret;
145     nxt_router_conf_t            *rtcf;
146     nxt_router_access_log_ctx_t  *ctx;
147 
148     ctx = nxt_mp_get(r->mem_pool, sizeof(nxt_router_access_log_ctx_t));
149     if (nxt_slow_path(ctx == NULL)) {
150         return;
151     }
152 
153     ctx->access_log = access_log;
154 
155     if (nxt_tstr_is_const(format)) {
156         nxt_tstr_str(format, &ctx->text);
157 
158         nxt_router_access_log_write_ready(task, r, ctx);
159 
160     } else {
161         rtcf = r->conf->socket_conf->router_conf;
162 
163         ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
164                                   &r->tstr_cache, r, r->mem_pool);
165         if (nxt_slow_path(ret != NXT_OK)) {
166             return;
167         }
168 
169         nxt_tstr_query(task, r->tstr_query, format, &ctx->text);
170         nxt_tstr_query_resolve(task, r->tstr_query, ctx,
171                                nxt_router_access_log_write_ready,
172                                nxt_router_access_log_write_error);
173      }
174 }
175 
176 
177 static void
nxt_router_access_log_write_ready(nxt_task_t * task,void * obj,void * data)178 nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data)
179 {
180     nxt_http_request_t           *r;
181     nxt_router_access_log_ctx_t  *ctx;
182 
183     r = obj;
184     ctx = data;
185 
186     nxt_fd_write(ctx->access_log->fd, ctx->text.start, ctx->text.length);
187 
188     nxt_http_request_close_handler(task, r, r->proto.any);
189 }
190 
191 
192 static void
nxt_router_access_log_write_error(nxt_task_t * task,void * obj,void * data)193 nxt_router_access_log_write_error(nxt_task_t *task, void *obj, void *data)
194 {
195 
196 }
197 
198 
199 void
nxt_router_access_log_open(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)200 nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
201 {
202     uint32_t                 stream;
203     nxt_int_t                ret;
204     nxt_buf_t                *b;
205     nxt_port_t               *main_port, *router_port;
206     nxt_runtime_t            *rt;
207     nxt_router_access_log_t  *access_log;
208 
209     access_log = tmcf->router_conf->access_log;
210 
211     b = nxt_buf_mem_alloc(tmcf->mem_pool, access_log->path.length + 1, 0);
212     if (nxt_slow_path(b == NULL)) {
213         goto fail;
214     }
215 
216     b->completion_handler = nxt_buf_dummy_completion;
217 
218     nxt_buf_cpystr(b, &access_log->path);
219     *b->mem.free++ = '\0';
220 
221     rt = task->thread->runtime;
222     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
223     router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
224 
225     stream = nxt_port_rpc_register_handler(task, router_port,
226                                            nxt_router_access_log_ready,
227                                            nxt_router_access_log_error,
228                                            -1, tmcf);
229     if (nxt_slow_path(stream == 0)) {
230         goto fail;
231     }
232 
233     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
234                                 stream, router_port->id, b);
235 
236     if (nxt_slow_path(ret != NXT_OK)) {
237         nxt_port_rpc_cancel(task, router_port, stream);
238         goto fail;
239     }
240 
241     return;
242 
243 fail:
244 
245     nxt_router_conf_error(task, tmcf);
246 }
247 
248 
249 static void
nxt_router_access_log_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)250 nxt_router_access_log_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
251     void *data)
252 {
253     nxt_router_temp_conf_t   *tmcf;
254     nxt_router_access_log_t  *access_log;
255 
256     tmcf = data;
257 
258     access_log = tmcf->router_conf->access_log;
259 
260     access_log->fd = msg->fd[0];
261 
262     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
263                        nxt_router_conf_apply, task, tmcf, NULL);
264 }
265 
266 
267 static void
nxt_router_access_log_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)268 nxt_router_access_log_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
269     void *data)
270 {
271     nxt_router_temp_conf_t  *tmcf;
272 
273     tmcf = data;
274 
275     nxt_router_conf_error(task, tmcf);
276 }
277 
278 
279 void
nxt_router_access_log_use(nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)280 nxt_router_access_log_use(nxt_thread_spinlock_t *lock,
281     nxt_router_access_log_t *access_log)
282 {
283     if (access_log == NULL) {
284         return;
285     }
286 
287     nxt_thread_spin_lock(lock);
288 
289     access_log->count++;
290 
291     nxt_thread_spin_unlock(lock);
292 }
293 
294 
295 void
nxt_router_access_log_release(nxt_task_t * task,nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)296 nxt_router_access_log_release(nxt_task_t *task, nxt_thread_spinlock_t *lock,
297     nxt_router_access_log_t *access_log)
298 {
299     if (access_log == NULL) {
300         return;
301     }
302 
303     nxt_thread_spin_lock(lock);
304 
305     if (--access_log->count != 0) {
306         access_log = NULL;
307     }
308 
309     nxt_thread_spin_unlock(lock);
310 
311     if (access_log != NULL) {
312 
313         if (access_log->fd != -1) {
314             nxt_fd_close(access_log->fd);
315         }
316 
317         nxt_free(access_log);
318     }
319 }
320 
321 
322 typedef struct {
323     nxt_mp_t                 *mem_pool;
324     nxt_router_access_log_t  *access_log;
325 } nxt_router_access_log_reopen_t;
326 
327 
328 void
nxt_router_access_log_reopen_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)329 nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
330 {
331     nxt_mp_t                        *mp;
332     uint32_t                        stream;
333     nxt_int_t                       ret;
334     nxt_buf_t                       *b;
335     nxt_port_t                      *main_port, *router_port;
336     nxt_runtime_t                   *rt;
337     nxt_router_access_log_t         *access_log;
338     nxt_router_access_log_reopen_t  *reopen;
339 
340     access_log = nxt_router->access_log;
341 
342     if (access_log == NULL) {
343         return;
344     }
345 
346     mp = nxt_mp_create(1024, 128, 256, 32);
347     if (nxt_slow_path(mp == NULL)) {
348         return;
349     }
350 
351     reopen = nxt_mp_get(mp, sizeof(nxt_router_access_log_reopen_t));
352     if (nxt_slow_path(reopen == NULL)) {
353         goto fail;
354     }
355 
356     reopen->mem_pool = mp;
357     reopen->access_log = access_log;
358 
359     b = nxt_buf_mem_alloc(mp, access_log->path.length + 1, 0);
360     if (nxt_slow_path(b == NULL)) {
361         goto fail;
362     }
363 
364     b->completion_handler = nxt_router_access_log_reopen_completion;
365 
366     nxt_buf_cpystr(b, &access_log->path);
367     *b->mem.free++ = '\0';
368 
369     rt = task->thread->runtime;
370     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
371     router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
372 
373     stream = nxt_port_rpc_register_handler(task, router_port,
374                                            nxt_router_access_log_reopen_ready,
375                                            nxt_router_access_log_reopen_error,
376                                            -1, reopen);
377     if (nxt_slow_path(stream == 0)) {
378         goto fail;
379     }
380 
381     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
382                                 stream, router_port->id, b);
383 
384     if (nxt_slow_path(ret != NXT_OK)) {
385         nxt_port_rpc_cancel(task, router_port, stream);
386         goto fail;
387     }
388 
389     nxt_mp_retain(mp);
390 
391     return;
392 
393 fail:
394 
395     nxt_mp_destroy(mp);
396 }
397 
398 
399 static void
nxt_router_access_log_reopen_completion(nxt_task_t * task,void * obj,void * data)400 nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj, void *data)
401 {
402     nxt_mp_t   *mp;
403     nxt_buf_t  *b;
404 
405     b = obj;
406     mp = b->data;
407 
408     nxt_mp_release(mp);
409 }
410 
411 
412 static void
nxt_router_access_log_reopen_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)413 nxt_router_access_log_reopen_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
414     void *data)
415 {
416     nxt_router_access_log_t         *access_log;
417     nxt_router_access_log_reopen_t  *reopen;
418 
419     reopen = data;
420 
421     access_log = reopen->access_log;
422 
423     if (access_log == nxt_router->access_log) {
424 
425         if (nxt_slow_path(dup2(msg->fd[0], access_log->fd) == -1)) {
426             nxt_alert(task, "dup2(%FD, %FD) failed %E",
427                       msg->fd[0], access_log->fd, nxt_errno);
428         }
429     }
430 
431     nxt_fd_close(msg->fd[0]);
432     nxt_mp_release(reopen->mem_pool);
433 }
434 
435 
436 static void
nxt_router_access_log_reopen_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)437 nxt_router_access_log_reopen_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
438     void *data)
439 {
440     nxt_router_access_log_reopen_t  *reopen;
441 
442     reopen = data;
443 
444     nxt_mp_release(reopen->mem_pool);
445 }
446