Nginx + strace
I was debugging a issue where we were getting truncated logs in ElasticSearch in the context of a setup as follows:
Application Logs -> Fluentd (logging) -> Nginx -> ElasticSearch The original problem turned out to be on the application side, but my first point of investigation was what are we getting on the nginx side? Do we get the entire message that we are expecting and something is going on the ElasticSearch side? To do so, I used strace.
Setup
Install nginx using your distribution’s package manager. On Fedora, sudo dnf install nginx did it for me. Once installed, start nginx:
$ sudo systemctl start nginx Test if nginx is up and running:
$ curl localhost If the above request succeeds, we are good to proceed.
Install strace on your system using your package manager. On Fedora, sudo dnf install strace was sufficient.
Tracing nginx request and response
To trace system calls made by nginx in the context of handling a request, we will attach to the nginx process. However, nginx runs multiple worker processes, so which process should we attach to? The solution is to ask strace to attach to the master process and ask it to trace system calls made by any children forked by the master process.
Let’s find out the process ID of the nginx master process:
[vagrant@ip-10-0-2-15 ~]$ ps -ef --forest | grep nginx root 1536 1 0 02:28 ? 00:00:00 nginx: master process /usr/sbin/nginx nginx 1537 1536 0 02:28 ? 00:00:00 \_ nginx: worker process Now that we have process ID of the master, we will run strace:
$ sudo strace -p 1536 -s 10000 -v -f An explanation of the various switches are in order:
-p: Process ID to attach to-s: Maximum string size in bytes, useful for printing arguments in full-v: Enable unabbreviation of the various function calls, gives us a lot of the details we may want to look-f: Trace child processes created viafork()
(Learn more here)
On a new terminal, we will perform a nginx reload operation so that it kills the old worker process and creates a new one:
$ sudo systemctl reload nginx This is needed since strace can only trace children created after we attached to the master process.
Now, on the same new terminal, we can make a request to our nginx server via curl:
$ curl --request POST --data '{"key":"value"}' localhost <html> <head><title>405 Not Allowed</title></head> <body> <center><h1>405 Not Allowed</h1></center> <hr><center>nginx/1.16.0</center> </body> </html> Let’s see what we have on the terminal we have strace open. The most relevant system calls are:
[pid 1661] recvfrom(3, "POST / HTTP/1.1\r\nHost: localhost\r\nUser-Agent: curl/7.64.0\r\nAccept: */*\r\nContent-Length: 15\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\n{\"key\":\"value\"}", 1024, 0, NULL, NULL) = 158 [pid 1661] stat("/usr/share/nginx/html/index.html", {st_dev=makedev(0x8, 0x1), st_ino=923644, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=16, st_size=5683, st_atime=1560911325 /* 2019-06-19T02:28:45.355163487+0000 */, st_atime_nsec=355163487, st_mtime=1538675049 /* 2018-10-04T17:44:09+0000 */, st_mtime_nsec=0, st_ctime=1560910132 /* 2019-06-19T02:08:52.339205227+0000 */, st_ctime_nsec=339205227}) = 0 [pid 1661] openat(AT_FDCWD, "/usr/share/nginx/html/index.html", O_RDONLY|O_NONBLOCK) = 4 [pid 1661] fstat(4, {st_dev=makedev(0x8, 0x1), st_ino=923644, st_mode=S_IFREG|0644, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=16, st_size=5683, st_atime=1560911325 /* 2019-06-19T02:28:45.355163487+0000 */, st_atime_nsec=355163487, st_mtime=1538675049 /* 2018-10-04T17:44:09+0000 */, st_mtime_nsec=0, st_ctime=1560910132 /* 2019-06-19T02:08:52.339205227+0000 */, st_ctime_nsec=339205227}) = 0 [pid 1661] writev(3, [{iov_base="HTTP/1.1 405 Not Allowed\r\nServer: nginx/1.16.0\r\nDate: Wed, 19 Jun 2019 03:22:14 GMT\r\nContent-Type: text/html\r\nContent-Length: 157\r\nConnection: keep-alive\r\n\r\n", iov_len=157}, {iov_base="<html>\r\n<head><title>405 Not Allowed</title></head>\r\n<body>\r\n<center><h1>405 Not Allowed</h1></center>\r\n", iov_len=104}, {iov_base="<hr><center>nginx/1.16.0</center>\r\n</body>\r\n</html>\r\n", iov_len=53}], 3) = 314 The recvfrom() call has the HTTP request sent by our curl command and the writev() call has the HTTP response being sent to the client.
Summary
Hope you found this post useful and if you did, you may find this other post by a different author interesting as well.