├── README.md ├── conf ├── base.logging ├── base.urls └── nginx.conf ├── logging.lua └── lua-nginx-logging-v1.0-1.rockspec /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | lua-nginx-logging - logging utilities for nginx written in lua 5 | 6 | Table of Contents 7 | ================= 8 | 9 | * [Name](#name) 10 | * [Status](#status) 11 | * [Description](#description) 12 | * [Synopsis](#synopsis) 13 | * [Limitations](#limitations) 14 | * [Installation](#installation) 15 | * [TODO](#todo) 16 | * [Author](#author) 17 | * [Copyright and License](#copyright-and-license) 18 | * [See Also](#see-also) 19 | 20 | Status 21 | ====== 22 | 23 | This library is still under early development and considered experimental. 24 | 25 | Description 26 | =========== 27 | 28 | This Lua library is a logging utility for the ngx_lua nginx module: 29 | 30 | Synopsis 31 | ======== 32 | 33 | ``` 34 | lua_package_path "/path/to/lua-nginx-logging/lib/?.lua;;"; 35 | 36 | server { 37 | 38 | location /logging/health/ { 39 | add_header Access-Control-Allow-Origin *; 40 | content_by_lua ' 41 | local cjson = require "cjson" 42 | local logging = require("logging") 43 | if logging and cjson then 44 | ngx.say("healthy\n") 45 | return ngx.exit(ngx.OK) 46 | end 47 | '; 48 | } 49 | 50 | location /logging/response/summary/(?[\S]+)? { 51 | add_header Access-Control-Allow-Origin *; 52 | content_by_lua ' 53 | local logging = require("logging") 54 | local response = logging.get_response_summary(ngx.shared.log_dict) 55 | return response 56 | '; 57 | } 58 | 59 | log_by_lua ' 60 | local logging = require("logging") 61 | local request_time = ngx.now() - ngx.req.start_time() 62 | logging.log_response_time(ngx.shared.log_dict, request_time) 63 | '; 64 | 65 | } 66 | ``` 67 | 68 | [Back to TOC](#table-of-contents) 69 | 70 | Limitations 71 | =========== 72 | 73 | * only supports JSON and raw output 74 | 75 | [Back to TOC](#table-of-contents) 76 | 77 | Installation 78 | ============ 79 | You can install it with luarocks `luarocks install lua-nginx-logging` 80 | 81 | Otherwise you need to configure the lua_package_path directive to add the path of your lua-nginx-loggin source to ngx_lua's LUA_PATH search path, as in 82 | 83 | ```nginx 84 | # nginx.conf 85 | http { 86 | lua_package_path "/path/to/lua-nginx-logging/?.lua;;"; 87 | ... 88 | } 89 | ``` 90 | 91 | Ensure that the system account running your Nginx ''worker'' proceses have 92 | enough permission to read the `.lua` file. 93 | 94 | [Back to TOC](#table-of-contents) 95 | 96 | TODO 97 | ==== 98 | 99 | * Support other response formats 100 | * log other things 101 | 102 | [Back to TOC](#table-of-contents) 103 | 104 | Author 105 | ====== 106 | 107 | James Marlowe "jamesmarlowe" , Lumate LLC. 108 | 109 | [Back to TOC](#table-of-contents) 110 | 111 | Copyright and License 112 | ===================== 113 | 114 | This module is licensed under the BSD license. 115 | 116 | Copyright (C) 2012-2014, by James Marlowe (jamesmarlowe) , Lumate LLC. 117 | 118 | All rights reserved. 119 | 120 | Redistribution and use in source and binary forms, with or without 121 | modification, are permitted provided that the following conditions are met: 122 | 123 | * Redistributions of source code must retain the above copyright notice, this 124 | list of conditions and the following disclaimer. 125 | 126 | * Redistributions in binary form must reproduce the above copyright notice, 127 | this list of conditions and the following disclaimer in the documentation 128 | and/or other materials provided with the distribution. 129 | 130 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 131 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 132 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 133 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 134 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 135 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 136 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 137 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 138 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 139 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 140 | 141 | [Back to TOC](#table-of-contents) 142 | 143 | See Also 144 | ======== 145 | * the ngx_lua module: http://wiki.nginx.org/HttpLuaModule 146 | * log_by_lua: http://wiki.nginx.org/HttpLuaModule#log_by_lua 147 | 148 | [Back to TOC](#table-of-contents) 149 | -------------------------------------------------------------------------------- /conf/base.logging: -------------------------------------------------------------------------------- 1 | 2 | log_by_lua ' 3 | local logging = require("logging") 4 | local request_time = ngx.now() - ngx.req.start_time() 5 | logging.log_response_time(ngx.shared.log_dict, request_time) 6 | logging.log_response_status(ngx.shared.log_dict) 7 | '; 8 | -------------------------------------------------------------------------------- /conf/base.urls: -------------------------------------------------------------------------------- 1 | 2 | location /lua-nginx-logging/health/ { 3 | add_header Access-Control-Allow-Origin *; 4 | content_by_lua ' 5 | local cjson = require "cjson" 6 | local logging = require "logging" 7 | if logging and cjson then 8 | ngx.say("healthy") 9 | return ngx.exit(ngx.OK) 10 | end 11 | '; 12 | } 13 | 14 | location ~ /lua-nginx-logging/timings/summary/(?[\S]+)? { 15 | add_header Access-Control-Allow-Origin *; 16 | content_by_lua ' 17 | local cjson = require "cjson" 18 | local logging = require("logging") 19 | local response = logging.get_timing_summary(ngx.shared.log_dict) 20 | 21 | if ngx.var.response_format == "" then ngx.var.response_format = "json" end 22 | 23 | if ngx.var.response_format == "json" then 24 | ngx.say(response) 25 | return ngx.exit(ngx.OK) 26 | elseif ngx.var.response_format == "txt" then 27 | decoded_response = cjson.decode(response) 28 | ngx.say("Time since last measure:\t", decoded_response.elapsed_time, " secs") 29 | ngx.say("Request Count:\t\t", decoded_response.count) 30 | ngx.say("Mean request time:\t\t", decoded_response.mean, " secs") 31 | ngx.say("Requests per Seconds:\t", decoded_response.qps) 32 | ngx.say("Mode request time:\t\t", decoded_response.mode, " secs,", decoded_response.modecount, " times") 33 | ngx.say("Min request time:\t\t", decoded_response.mintime, " secs") 34 | ngx.say("Max request time:\t\t", decoded_response.maxtime, " secs") 35 | ngx.say("StdDev request time:\t", decoded_response.stdev, " secs") 36 | return ngx.exit(ngx.OK) 37 | else 38 | ngx.say("json and txt are only supported response formats") 39 | return ngx.exit(ngx.OK) 40 | end 41 | '; 42 | } 43 | 44 | location ~ /lua-nginx-logging/response/summary/(?[\S]+)? { 45 | add_header Access-Control-Allow-Origin *; 46 | content_by_lua ' 47 | local logging = require("logging") 48 | local response = logging.get_response_summary(ngx.shared.log_dict) 49 | 50 | if ngx.var.response_format == "" then ngx.var.response_format = "json" end 51 | 52 | if ngx.var.response_format == "json" then 53 | ngx.say(response) 54 | return ngx.exit(ngx.OK) 55 | else 56 | ngx.say("json is only supported response format") 57 | return ngx.exit(ngx.OK) 58 | end 59 | '; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /conf/nginx.conf: -------------------------------------------------------------------------------- 1 | http { 2 | 3 | lua_shared_dict log_dict 1M; 4 | 5 | lua_package_path "${prefix}/?.lua;;"; 6 | 7 | server { 8 | 9 | include *.urls; 10 | 11 | include *.logging; 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /logging.lua: -------------------------------------------------------------------------------- 1 | -- Lumate - logging 2 | -- usage: add the following to locations or globally in a server/http block 3 | -- log_by_lua ' 4 | -- local logging = require("logging") 5 | -- local request_time = ngx.now() - ngx.req.start_time() 6 | -- logging.log_response_time(ngx.shared.log_dict, request_time) 7 | -- '; 8 | 9 | local cjson = require "cjson" 10 | 11 | local logging = {} 12 | 13 | local function incr(dict, key, increment) 14 | increment = increment or 1 15 | local newval, err = dict:incr(key, increment) 16 | 17 | if not newval or err then 18 | dict:add(key, increment) 19 | newval = increment 20 | end 21 | 22 | return newval 23 | end 24 | 25 | function logging.log_response_time(dict, value) 26 | local sum_key = "request_time-sum" 27 | local count_key = "request_time-count" 28 | local start_time_key = "request_time-start_time" 29 | local request_time_key = value 30 | 31 | dict:add(start_time_key, ngx.now()) 32 | 33 | incr(dict, sum_key, value) 34 | incr(dict, count_key) 35 | incr(dict, request_time_key) 36 | 37 | return true 38 | end 39 | 40 | function logging.log_response_status(dict) 41 | local response_code = ngx.var.status or "" 42 | local response_key = "response_code-"..response_code 43 | 44 | incr(dict, response_key) 45 | 46 | return true 47 | end 48 | 49 | function logging.get_timing_values(dict) 50 | local keys = dict:get_keys(0) 51 | local response_times = {} 52 | 53 | for k,v in pairs(keys) do 54 | if tonumber(v) then 55 | val = dict:get(v) 56 | response_times[#response_times] = {response_time = v, count = val} 57 | end 58 | end 59 | 60 | dict:flush_all() 61 | 62 | return cjson.encode(response_times) 63 | end 64 | 65 | function logging.get_timing_summary(dict) 66 | local sum_key = "request_time-sum" 67 | local count_key = "request_time-count" 68 | local start_time_key = "request_time-start_time" 69 | local keys = dict:get_keys(0) 70 | local start_time = dict:get(start_time_key) 71 | local count = dict:get(count_key) or 0 72 | local sum = dict:get(sum_key) or 0 73 | local mean = 0 74 | local stdevsum = 0 75 | local qps = 0 76 | 77 | if count > 0 then mean = sum / count end 78 | 79 | for k,v in pairs(keys) do 80 | if tonumber(v) then 81 | val = dict:get(v) 82 | maxval = maxval and (maxval > val and maxval or val) or val 83 | if oldmax ~= maxval then mode = v modecount = val end 84 | oldmax = maxval 85 | mintime = mintime and (mintime < v and mintime or v) or v 86 | maxtime = maxtime and (maxtime > v and maxtime or v) or v 87 | stdevsum = stdevsum + (mean - v)^2 88 | end 89 | end 90 | 91 | local stdev = math.sqrt(stdevsum/count) 92 | 93 | local elapsed_time = 0 94 | 95 | if start_time then 96 | elapsed_time = ngx.now() - start_time 97 | end 98 | 99 | if elapsed_time > 0 then 100 | qps = count / elapsed_time 101 | end 102 | 103 | dict:flush_all() 104 | 105 | local summary = {count=count, 106 | qps=qps, 107 | mean=mean, 108 | mode=mode, 109 | modecount=modecount, 110 | mintime=mintime, 111 | maxtime=maxtime, 112 | stdev=stdev, 113 | elapsed_time=elapsed_time} 114 | 115 | return cjson.encode(summary) 116 | end 117 | 118 | function logging.get_response_summary(dict) 119 | local response_key_prefix = "response_code-" 120 | local response_codes = {} 121 | local keys = dict:get_keys(0) 122 | 123 | for k,v in pairs(keys) do 124 | if v:find(response_key_prefix, 1, true) then 125 | val = dict:get(v) 126 | response_codes[#response_codes] = {response_code = v, count = val} 127 | end 128 | end 129 | 130 | return cjson.encode(response_codes) 131 | end 132 | 133 | return logging 134 | -------------------------------------------------------------------------------- /lua-nginx-logging-v1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-nginx-logging" 2 | version = "v1.0-1" 3 | 4 | source = { 5 | url = "git://github.com/Lumate/lua-nginx-logging.git" 6 | } 7 | 8 | description = { 9 | summary = "Request logging library for Lua and OpenResty", 10 | homepage = "https://github.com/Lumate/lua-nginx-logging", 11 | license = "BSD", 12 | maintainer = "jameskmarlowe@gmail.com" 13 | } 14 | 15 | dependencies = { 16 | "lua >= 5.1", 17 | "lua-cjson" 18 | } 19 | 20 | build = { 21 | type = "builtin", 22 | modules = { 23 | ["nginx.logging"] = "logging.lua" 24 | } 25 | } 26 | --------------------------------------------------------------------------------