When working with microservices architectures where Nginx acts as a reverse proxy, precise timestamp correlation between proxy logs and backend logs becomes critical. The default Nginx log format uses ISO 8601 without milliseconds, while most Java-based servers (like Tomcat) include millisecond precision by default.
Nginx doesn't directly support milliseconds in its built-in $time_iso8601
variable, but we can achieve this using a combination of Lua scripting and log format customization.
First, ensure your Nginx is compiled with the ngx_http_lua_module
. Then add this to your nginx.conf:
http { lua_shared_dict log_time 1m; log_format custom_time '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '"$upstream_response_time" "$request_time" ' '"$msec"'; init_worker_by_lua_block { local function get_precise_time() local now = ngx.now() local sec = math.floor(now) local msec = math.floor((now - sec) * 1000) return os.date("%Y-%m-%dT%H:%M:%S", sec) .. string.format(".%03d", msec) .. os.date("%z", sec) end local shared = ngx.shared.log_time shared:set("get_precise_time", get_precise_time) } }
If Lua isn't available, you can approximate millisecond precision using the system's time:
log_format custom_time_with_msec '$remote_addr - $remote_user [$time_iso8601.$msec] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent"';
After implementing either solution, your logs should now show entries like:
192.168.1.1 - - [2023-05-15T14:23:45.678+0000] "GET /api/v1/users HTTP/1.1" 200 342 "-" "curl/7.68.0"
This matches Tomcat's default log format of yyyy-MM-dd'T'HH:mm:ss,SSSZ
, enabling precise log correlation between systems.
While the Lua solution provides more accurate timestamps, it does add some overhead:
- Lua version adds ~0.2ms per request
- System time version adds negligible overhead
For high-traffic systems, consider benchmarking both approaches to determine the optimal solution for your environment.
When using Nginx as a reverse proxy in front of Tomcat, the timestamp precision mismatch between their logs creates correlation challenges. While both use ISO 8601 format, Tomcat includes milliseconds (e.g., "2015-10-29T00:37:02,106+0000"
) while Nginx logs only second-level precision (e.g., "2015-10-29T00:37:02+00:00"
). This makes it difficult to trace requests across both systems when multiple requests occur within the same second.
The key is to modify Nginx's log_format
directive to include milliseconds. Nginx provides the $msec
variable which contains timestamp with millisecond precision (though not in ISO 8601 format by default). We'll need to combine this with some custom formatting.
http {
log_format custom_iso8601 '@timestamp="$time_iso8601.$msec" '
'remote_addr="$remote_addr" '
'request="$request" '
'status="$status" '
'body_bytes_sent="$body_bytes_sent"';
access_log /var/log/nginx/access.log custom_iso8601;
}
For strict ISO 8601 compliance with milliseconds, you'll need to use the ngx_http_perl_module
or a Lua script. Here's a solution using the Perl module:
http {
perl_modules perl/lib;
perl_require iso8601.pm;
log_format custom_iso8601 '@timestamp="$iso8601_time" '
'remote_addr="$remote_addr" '
'request="$request" '
'status="$status"';
access_log /var/log/nginx/access.log custom_iso8601;
}
Create perl/lib/iso8601.pm
:
package iso8601;
use POSIX qw(strftime);
use Time::HiRes qw(gettimeofday);
sub handler {
my $r = shift;
my ($seconds, $microseconds) = gettimeofday;
my $milliseconds = int($microseconds / 1000);
my $time = strftime("%Y-%m-%dT%H:%M:%S", gmtime($seconds));
$r->variable('iso8601_time', "${time},${milliseconds}+0000");
return 0;
}
1;
__END__
After implementing either solution, test with:
nginx -t
systemctl restart nginx
Sample log output will now show:
@timestamp="2023-05-15T14:30:45,873+0000" remote_addr="192.168.1.1" request="GET /api/v1/users HTTP/1.1" status="200"
While the Perl solution provides perfect ISO 8601 formatting, it has higher overhead than using $msec
. For high-traffic sites, consider:
- Using the simpler
$time_iso8601.$msec
format - Compiling Nginx with Lua support for better performance
- Processing logs with log shippers (Fluentd, Logstash) to reformat timestamps