Nginx is awesome, but it’s missing some common features. For instance, a common thing to add to access logs is a unique ID per request, so that you can track the flow of a single request through multiple services. Another thing it’s missing is the ability to log request_time in milliseconds, rather than seconds with a millisecond granularity. Using Lua, we can add these features ourselves.

I’ll show the whole solution, then I’ll break it down into parts:

http {
... map $host $request_time_ms {
default '';
}
map $host $uuid {
default '';
} lua_package_path '/etc/nginx/uuid4.lua';
init_by_lua '
uuid4 = require "uuid4"
math = require "math"
'; log_by_lua '
ngx.var.request_time_ms = math.floor(tonumber(ngx.var.request_time) * 1000)
'; log_format mycustomformat '[$time_local] "$request" $status $request_length $bytes_sent $request_time_ms $uuid';
access_log /var/log/nginx/access.log mycustomformat; ...
} server {
... set_by_lua $uuid '
if ngx.var.http_x_request_id == nil then
return uuid4.getUUID()
else
return ngx.var.http_x_request_id
end
'; ...
}

It’s necessary to set variables before we use them in Lua. Using map is a trick to set variables in the http context (you can’t use set $variable ” in http). For the case of uuid, we are going to set it in the server section (during the rewrite context), but in case it’s not set, we want to avoid throwing errors. Here’s how we set these variables:

        map $host $request_time_ms {
default '';
}
map $host $uuid {
default '';
}

Next we add a uuid4 library to our path, and include the libraries into our context:

        lua_package_path '/etc/nginx/uuid4.lua';
init_by_lua '
uuid4 = require "uuid4"
math = require "math"
';

Using the log_by_lua function, we’ll set the request_time_ms variable we’ll use in the log_format config. This Lua function is called in the log context, before logs are written, allowing us to make the variables available to it:

        log_by_lua '
ngx.var.request_time_ms = math.floor(tonumber(ngx.var.request_time) * 1000)
';

Next we set the log format, and use it for the access log:

        log_format mycustomformat '[$time_local] "$request" $status $request_length $bytes_sent $request_time_ms $uuid';
access_log /var/log/nginx/access.log mycustomformat;

Lastly, we set the uuid during the rewrite context in the server section, using set_by_lua. To facilitate following a request across services, we’ll reuse the header if it’s already set. If the header isn’t set, then this request didn’t come from another service, so we’ll generate a UUID:

server {
... set_by_lua $uuid '
if ngx.var.http_x_request_id == nil then
return uuid4.getUUID()
else
return ngx.var.http_x_request_id
end
' ...
}

If you’re trusting this header data in any way, you should be sure to filter/restrict that header appropriately so that the client can’t change it.

Update (Thursday December 11 2014): Edited the post to move the uuid generation into the server section and using set_by_lua, so that the uuid can be set to/from the header to flow through the stacks properly. Shout out to Asher Feldman for working out a better solution with me.

参考资料:

Using Lua in Nginx for unique request IDs and millisecond times in logs:http://ryandlane.com/blog/2014/12/11/using-lua-in-nginx-for-unique-request-ids-and-millisecond-times-in-logs/

Simple nginx lua script to add UUID to each request for end to end request tracking:https://gist.github.com/erikcw/e999e1fb438dbbb91533

Is there a way to log a per request unique id for nginx?:http://serverfault.com/questions/580394/is-there-a-way-to-log-a-per-request-unique-id-for-nginx

http://stackoverflow.com/questions/17748735/setting-a-trace-id-in-nginx-load-balancer

https://github.com/kali/nginx-operationid

【架构】Nginx如何设置X-Request-ID请求头,记录请求时间:毫秒?的更多相关文章

  1. request获取请求头和请求数据

    package cn.itcast.request; import java.io.IOException; import java.io.InputStream; import java.io.Pr ...

  2. HTTP 请求头与请求体 - 某熊的全栈之路 - SegmentFault

    本文从属于笔者的HTTP 理解与实践系列文章,对于HTTP的学习主要包含HTTP 基础.HTTP 请求头与请求体.HTTP 响应头与状态码.HTTP 缓存这四个部分,而对于HTTP相关的扩展与引申,我 ...

  3. 【转载】HTTP 请求头与请求体

    原文地址: https://segmentfault.com/a/1190000006689767 HTTP Request HTTP 的请求报文分为三个部分 请求行.请求头和请求体,格式如图:一个典 ...

  4. HttpServletRequest对象,请求行、请求头、请求体

    HttpServletRequest 公共接口类HttpServletRequest继承自ServletRequest.客户端浏览器发出的请求被封装成为一个HttpServletRequest对象.对 ...

  5. Django中获取参数(路径,查询,请求头,请求体)

    一.通常HTTP协议向服务器传参有几种途径 : 提取URL的特定部分,如/weather/shanghai/2018,可以在服务器端的路由中用正则表达式截取: 查询字符串(query string), ...

  6. HTTP请求报文(请求行、请求头、请求体)

    HTTP协议 1.简介 HTTP协议(Hyper Text Transfer Protocol,超文本传输协议),是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的 ...

  7. iOS开发——网络篇——文件下载(NSMutableData、NSFileHandle、NSOutputStream)和上传、压缩和解压(三方框架ZipArchive),请求头和请求体格式,断点续传Range

    一.小文件下载 NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion ...

  8. HTTP请求行、请求头、请求体详解

    HTTP 请求头各参数具体含义 Header 解释 示例Accept 指定客户端能够接收的内容类型 Accept: text/plain, text/htmlAccept-Charset 浏览器可以接 ...

  9. HTTP请求行、请求头、请求体详解(转)

    转自 https://blog.csdn.net/u010256388/article/details/68491509/     HTTP请求报文解剖 HTTP请求报文由3部分组成(请求行+请求头+ ...

  10. 【校招面试 之 网络】第3题 HTTP请求行、请求头、请求体详解

    1.HTTP请求报文解剖 HTTP请求报文由3部分组成(请求行+请求头+请求体): 下面是一个实际的请求报文: ①是请求方法,GET和POST是最常见的HTTP方法,除此以外还包括DELETE.HEA ...

随机推荐

  1. AC日记——[ZJOI2012]网络 bzoj 2816

    2816 思路: 多个LCT: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 10005 #define l ...

  2. Sql server2008中merge用法

    /// <summary> /// 修改:添加条件: AND roleModule.FuncCode = tvpRoleModule.FuncCode /// </summary&g ...

  3. 前端读者 | 由setTimeout引发的JS引擎运行机制的研究

    本文来自 @xiaoyuze88 链接:http://xiaoyuze88.github.io/ 太久没碰代码了,那天想到关于循环调用setTimeout实现每隔一秒输出递增的数的那个问题,搞了搞,发 ...

  4. 【剑指offer】面试题 11. 旋转数组的最小数字

    面试题 11. 旋转数组的最小数字 题目描述 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4, ...

  5. 4.Spark Streaming事务处理

    首先,我们必须知道什么是事务及其一致性? 事务应该具有4个属性:原子性.一致性.隔离性.持久性.这四个属性通常称为ACID特性. 原子性(atomicity).一个事务是一个不可分割的工作单位,事务中 ...

  6. Windows 10 安装 Mongodb

    因为新换了Windows 10 电脑,需要在新电脑重新安装所有的软件,包括mongodb 下载文件:首先在mongodb的官方网站上下载最新版本的mongodb安装程序,https://www.mon ...

  7. CentOS 7 Docker

    安装docker yum install docker 查看docker docker version 这是因为docker还没有运行,需要启动docker 启动docker systemctl st ...

  8. ajaxReturn 之前dump调试,导致$.ajax不能正常运行

    ajaxReturn 之前dump调试,导致$.ajax不能正常运行 以后调试的时候,注意下这个情况

  9. LOJ #6278. 数列分块入门 2-分块(区间加法、查询区间内小于某个值x的元素个数)

    #6278. 数列分块入门 2 内存限制:256 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: hzwer 提交提交记录统计测试数据讨论 6   题目描述 给出 ...

  10. Java工具类-设置字符编码

    package common; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.Filter ...