xref: /unit/src/nxt_router_access_log.c (revision 2166:64a3527f65ad)
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_var_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_var_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_var_compile(&str, rtcf->mem_pool, rtcf->var_fields,
129                              NXT_VAR_LOGGING);
130     if (nxt_slow_path(format == NULL)) {
131         return NXT_ERROR;
132     }
133 
134     rtcf->access_log = access_log;
135     rtcf->log_format = format;
136 
137     return NXT_OK;
138 }
139 
140 
141 static void
nxt_router_access_log_writer(nxt_task_t * task,nxt_http_request_t * r,nxt_router_access_log_t * access_log,nxt_var_t * format)142 nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
143     nxt_router_access_log_t *access_log, nxt_var_t *format)
144 {
145     nxt_int_t                    ret;
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_var_is_const(format)) {
156         nxt_var_raw(format, &ctx->text);
157 
158         nxt_router_access_log_write_ready(task, r, ctx);
159 
160     } else {
161         ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
162         if (nxt_slow_path(ret != NXT_OK)) {
163             return;
164         }
165 
166         nxt_var_query(task, r->var_query, format, &ctx->text);
167         nxt_var_query_resolve(task, r->var_query, ctx,
168                               nxt_router_access_log_write_ready,
169                               nxt_router_access_log_write_error);
170      }
171 }
172 
173 
174 static void
nxt_router_access_log_write_ready(nxt_task_t * task,void * obj,void * data)175 nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data)
176 {
177     nxt_http_request_t           *r;
178     nxt_router_access_log_ctx_t  *ctx;
179 
180     r = obj;
181     ctx = data;
182 
183     nxt_fd_write(ctx->access_log->fd, ctx->text.start, ctx->text.length);
184 
185     nxt_http_request_close_handler(task, r, r->proto.any);
186 }
187 
188 
189 static void
nxt_router_access_log_write_error(nxt_task_t * task,void * obj,void * data)190 nxt_router_access_log_write_error(nxt_task_t *task, void *obj, void *data)
191 {
192 
193 }
194 
195 
196 void
nxt_router_access_log_open(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)197 nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
198 {
199     uint32_t                 stream;
200     nxt_int_t                ret;
201     nxt_buf_t                *b;
202     nxt_port_t               *main_port, *router_port;
203     nxt_runtime_t            *rt;
204     nxt_router_access_log_t  *access_log;
205 
206     access_log = tmcf->router_conf->access_log;
207 
208     b = nxt_buf_mem_alloc(tmcf->mem_pool, access_log->path.length + 1, 0);
209     if (nxt_slow_path(b == NULL)) {
210         goto fail;
211     }
212 
213     b->completion_handler = nxt_buf_dummy_completion;
214 
215     nxt_buf_cpystr(b, &access_log->path);
216     *b->mem.free++ = '\0';
217 
218     rt = task->thread->runtime;
219     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
220     router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
221 
222     stream = nxt_port_rpc_register_handler(task, router_port,
223                                            nxt_router_access_log_ready,
224                                            nxt_router_access_log_error,
225                                            -1, tmcf);
226     if (nxt_slow_path(stream == 0)) {
227         goto fail;
228     }
229 
230     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
231                                 stream, router_port->id, b);
232 
233     if (nxt_slow_path(ret != NXT_OK)) {
234         nxt_port_rpc_cancel(task, router_port, stream);
235         goto fail;
236     }
237 
238     return;
239 
240 fail:
241 
242     nxt_router_conf_error(task, tmcf);
243 }
244 
245 
246 static void
nxt_router_access_log_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)247 nxt_router_access_log_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
248     void *data)
249 {
250     nxt_router_temp_conf_t   *tmcf;
251     nxt_router_access_log_t  *access_log;
252 
253     tmcf = data;
254 
255     access_log = tmcf->router_conf->access_log;
256 
257     access_log->fd = msg->fd[0];
258 
259     nxt_work_queue_add(&task->thread->engine->fast_work_queue,
260                        nxt_router_conf_apply, task, tmcf, NULL);
261 }
262 
263 
264 static void
nxt_router_access_log_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)265 nxt_router_access_log_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
266     void *data)
267 {
268     nxt_router_temp_conf_t  *tmcf;
269 
270     tmcf = data;
271 
272     nxt_router_conf_error(task, tmcf);
273 }
274 
275 
276 void
nxt_router_access_log_use(nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)277 nxt_router_access_log_use(nxt_thread_spinlock_t *lock,
278     nxt_router_access_log_t *access_log)
279 {
280     if (access_log == NULL) {
281         return;
282     }
283 
284     nxt_thread_spin_lock(lock);
285 
286     access_log->count++;
287 
288     nxt_thread_spin_unlock(lock);
289 }
290 
291 
292 void
nxt_router_access_log_release(nxt_task_t * task,nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)293 nxt_router_access_log_release(nxt_task_t *task, nxt_thread_spinlock_t *lock,
294     nxt_router_access_log_t *access_log)
295 {
296     if (access_log == NULL) {
297         return;
298     }
299 
300     nxt_thread_spin_lock(lock);
301 
302     if (--access_log->count != 0) {
303         access_log = NULL;
304     }
305 
306     nxt_thread_spin_unlock(lock);
307 
308     if (access_log != NULL) {
309 
310         if (access_log->fd != -1) {
311             nxt_fd_close(access_log->fd);
312         }
313 
314         nxt_free(access_log);
315     }
316 }
317 
318 
319 typedef struct {
320     nxt_mp_t                 *mem_pool;
321     nxt_router_access_log_t  *access_log;
322 } nxt_router_access_log_reopen_t;
323 
324 
325 void
nxt_router_access_log_reopen_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)326 nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
327 {
328     nxt_mp_t                        *mp;
329     uint32_t                        stream;
330     nxt_int_t                       ret;
331     nxt_buf_t                       *b;
332     nxt_port_t                      *main_port, *router_port;
333     nxt_runtime_t                   *rt;
334     nxt_router_access_log_t         *access_log;
335     nxt_router_access_log_reopen_t  *reopen;
336 
337     access_log = nxt_router->access_log;
338 
339     if (access_log == NULL) {
340         return;
341     }
342 
343     mp = nxt_mp_create(1024, 128, 256, 32);
344     if (nxt_slow_path(mp == NULL)) {
345         return;
346     }
347 
348     reopen = nxt_mp_get(mp, sizeof(nxt_router_access_log_reopen_t));
349     if (nxt_slow_path(reopen == NULL)) {
350         goto fail;
351     }
352 
353     reopen->mem_pool = mp;
354     reopen->access_log = access_log;
355 
356     b = nxt_buf_mem_alloc(mp, access_log->path.length + 1, 0);
357     if (nxt_slow_path(b == NULL)) {
358         goto fail;
359     }
360 
361     b->completion_handler = nxt_router_access_log_reopen_completion;
362 
363     nxt_buf_cpystr(b, &access_log->path);
364     *b->mem.free++ = '\0';
365 
366     rt = task->thread->runtime;
367     main_port = rt->port_by_type[NXT_PROCESS_MAIN];
368     router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
369 
370     stream = nxt_port_rpc_register_handler(task, router_port,
371                                            nxt_router_access_log_reopen_ready,
372                                            nxt_router_access_log_reopen_error,
373                                            -1, reopen);
374     if (nxt_slow_path(stream == 0)) {
375         goto fail;
376     }
377 
378     ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
379                                 stream, router_port->id, b);
380 
381     if (nxt_slow_path(ret != NXT_OK)) {
382         nxt_port_rpc_cancel(task, router_port, stream);
383         goto fail;
384     }
385 
386     nxt_mp_retain(mp);
387 
388     return;
389 
390 fail:
391 
392     nxt_mp_destroy(mp);
393 }
394 
395 
396 static void
nxt_router_access_log_reopen_completion(nxt_task_t * task,void * obj,void * data)397 nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj, void *data)
398 {
399     nxt_mp_t   *mp;
400     nxt_buf_t  *b;
401 
402     b = obj;
403     mp = b->data;
404 
405     nxt_mp_release(mp);
406 }
407 
408 
409 static void
nxt_router_access_log_reopen_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)410 nxt_router_access_log_reopen_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
411     void *data)
412 {
413     nxt_router_access_log_t         *access_log;
414     nxt_router_access_log_reopen_t  *reopen;
415 
416     reopen = data;
417 
418     access_log = reopen->access_log;
419 
420     if (access_log == nxt_router->access_log) {
421 
422         if (nxt_slow_path(dup2(msg->fd[0], access_log->fd) == -1)) {
423             nxt_alert(task, "dup2(%FD, %FD) failed %E",
424                       msg->fd[0], access_log->fd, nxt_errno);
425         }
426     }
427 
428     nxt_fd_close(msg->fd[0]);
429     nxt_mp_release(reopen->mem_pool);
430 }
431 
432 
433 static void
nxt_router_access_log_reopen_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)434 nxt_router_access_log_reopen_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
435     void *data)
436 {
437     nxt_router_access_log_reopen_t  *reopen;
438 
439     reopen = data;
440 
441     nxt_mp_release(reopen->mem_pool);
442 }
443