How to Synchronize Nginx Error Log and Access Log Timestamp Formats


2 views

When working with Nginx logs, many administrators notice the discrepancy between error log and access log timestamp formats:

# Error log default format
2023/05/15 14:30:45 [error] 12345#0: *6789 upstream timed out

# Access log formats
$time_local: 15/May/2023:14:30:45 +0000
$time_iso8601: 2023-05-15T14:30:45+00:00

Nginx provides limited built-in variables for timestamp formatting:

  • $time_local - Common Log Format (CLF) style
  • $time_iso8601 - ISO 8601 format

The error log format (Y/m/d H:i:s) isn't available as a built-in variable for access logs.

For Nginx compiled with Lua support, you can create custom timestamp formats:

http {
    lua_shared_dict date_cache 1m;
    
    log_format custom_error_fmt '$remote_addr - $remote_user [$time_custom] '
                               '"$request" $status $body_bytes_sent '
                               '"$http_referer" "$http_user_agent"';
    
    server {
        set_by_lua $time_custom '
            local date_cache = ngx.shared.date_cache
            local time_key = ngx.time()
            local cached_date = date_cache:get(time_key)
            
            if cached_date then
                return cached_date
            end
            
            local date_str = os.date("%Y/%m/%d %H:%M:%S")
            date_cache:set(time_key, date_str, 1)
            return date_str
        ';
        
        access_log /var/log/nginx/access.log custom_error_fmt;
        error_log /var/log/nginx/error.log;
    }
}

If modifying Nginx configuration isn't possible, consider log processing tools:

# Using awk to reformat access logs
awk '{
    match($0, /$$([^]]+)$$/, m)
    split(m[1], t, /[/: ]/)
    month_map["Jan"]=01; month_map["Feb"]=02; # ... continue mapping
    
    new_date = t[3] "/" month_map[t[2]] "/" t[1] " " t[4] ":" t[5] ":" t[6]
    gsub(m[1], new_date)
    print
}' access.log > reformatted_access.log

Consistent timestamp formats enable:

  • Easier log correlation between access and error logs
  • Simpler parsing for log aggregation systems
  • Better compatibility with monitoring tools
  • Reduced cognitive load when debugging

For production systems, consider these best practices:

# Recommended minimal configuration
log_format main '$remote_addr - $remote_user [$time_iso8601] '
                '"$request" $status $body_bytes_sent '
                '"$http_referer" "$http_user_agent"';
                
access_log /var/log/nginx/access.log main buffer=32k flush=5m;
error_log /var/log/nginx/error.log warn;

When working with Nginx logs, many administrators notice that access logs and error logs use different timestamp formats by default:

# Access log formats (configurable):
[29/Sep/2016:10:20:48 +0100]   # $time_local
[2016-09-29T10:20:48+01:00]    # $time_iso8601

# Error log format (fixed):
2016/09/29 10:37:52 [error]     # Y/m/d H:i:s

Having consistent timestamp formats across logs is crucial for:

  • Correlating events between access and error logs
  • Automated log processing and analysis
  • Human readability during debugging

While Nginx doesn't natively support changing the error log format, we can achieve this using the ngx_lua module:

# Install lua-nginx-module (CentOS 7)
yum install lua-nginx-module

# nginx.conf configuration
http {
    lua_package_path "/path/to/lua/scripts/?.lua;;";
    
    init_by_lua_block {
        local file = require "pl.file"
        local path = require "pl.path"
        
        -- Custom error logger function
        function custom_error_log(msg, level)
            local timestamp = ngx.localtime()  -- Returns Y-m-d H:i:s format
            local log_line = string.format("[%s] [%s] %s", 
                timestamp, 
                level or "error",
                msg)
            
            -- Write to error log file
            local log_path = "/var/log/nginx/error_custom.log"
            local fd = io.open(log_path, "a")
            if fd then
                fd:write(log_line, "\n")
                fd:close()
            end
        end
    }
}

For systems where modifying Nginx isn't possible, consider post-processing:

# Using sed to reformat error logs
cat /var/log/nginx/error.log | sed -E 's|([0-9]{4})/([0-9]{2})/([0-9]{2}) ([0-9]{2}:[0-9]{2}:[0-9]{2})|[\3/\2/\1:\4 +0000]|' > error_reformatted.log

# Logstash configuration example
filter {
  grok {
    match => { "message" => "%{YEAR:year}/%{MONTHNUM:month}/%{MONTHDAY:day} %{TIME:time} $$%{LOGLEVEL:loglevel}$$" }
  }
  mutate {
    add_field => { "timestamp" => "%{day}/%{month}/%{year}:%{time} +0000" }
  }
}
  • Standardize on ISO8601 format ([YYYY-MM-DDThh:mm:ss±TZ]) for all logging
  • Include timezone information in all timestamps
  • Consider using centralized logging solutions (ELK, Graylog)
  • Document your logging format standards

For production systems, the Lua module approach provides the most reliable solution while maintaining native Nginx performance characteristics. The post-processing approach works well for existing log archives or when you can't modify the Nginx configuration.