2016-11-23 19:40:22 +01:00
local MetricsGraphite = { }
MetricsGraphite.__index = MetricsGraphite
2017-03-01 23:10:09 +01:00
function MetricsGraphite . init ( carbon_hosts , interval , mbase )
2016-11-23 19:40:22 +01:00
local self = setmetatable ( { } , MetricsGraphite )
ngx.log ( ngx.INFO , " nginx-metrics-graphite initializing on nginx version " .. ngx.config . nginx_version .. " with ngx_lua version " .. ngx.config . ngx_lua_version )
2017-03-01 23:10:09 +01:00
self.carbon_hosts = carbon_hosts
2016-11-23 19:40:22 +01:00
self.interval = interval
self.mbase = mbase
-- metadata tables for more flexible metric creation
self.query_status = {
status_5xx = 500 ,
status_4xx = 400 ,
status_3xx = 300 ,
status_2xx = 200 ,
status_1xx = 100
}
self.query_method = {
method_get = " GET " ,
method_head = " HEAD " ,
method_put = " PUT " ,
method_post = " POST " ,
method_delete = " DELETE " ,
method_options = " OPTIONS " ,
method_other = " "
}
2016-11-29 20:26:43 +01:00
self.query_http = {
http_09 = 0.9 ,
http_10 = 1.0 ,
http_11 = 1.1 ,
http_20 = 2.0
}
2016-11-23 19:40:22 +01:00
-- initialize/reset counters
2018-11-05 13:34:43 +01:00
-- Note: ngx.shared.DICT is thread-safe, see https://github.com/openresty/lua-nginx-module#ngxshareddict
self.stats = ngx.shared . metrics_graphite
2016-11-23 19:40:22 +01:00
self.stats : set ( " main_loop_worker " , 0 )
2016-11-29 20:26:43 +01:00
self.stats : set ( " requests " , 0 ) -- total number
self.stats : set ( " upstream_requests " , 0 ) -- requests which used an upstream server
self.stats : set ( " gzip_requests " , 0 ) -- responses which used gzip
self.stats : set ( " ssl_requests " , 0 ) -- requests which used ssl
2016-11-23 19:40:22 +01:00
self.stats : set ( " request_length " , 0 )
self.stats : set ( " bytes_sent " , 0 )
self.stats : set ( " request_time_sum " , 0 )
self.stats : set ( " request_time_num " , 0 )
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_status ) do
2016-11-23 19:40:22 +01:00
self.stats : set ( k , 0 )
end
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_method ) do
2016-11-23 19:40:22 +01:00
self.stats : set ( k , 0 )
end
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_http ) do
2016-11-29 20:26:43 +01:00
self.stats : set ( k , 0 )
end
2016-11-23 19:40:22 +01:00
return self
end
function MetricsGraphite : worker ( )
2018-11-05 13:34:43 +01:00
-- determine which worker should handle the main loop, relies on the atomicity of ngx.shared.DICT:incr
-- see https://github.com/openresty/lua-nginx-module#ngxshareddict
2018-11-23 21:15:41 +01:00
-- caveeat: if the main loop worker dies no further metrics will be sent!
2016-11-23 19:40:22 +01:00
if self.stats : incr ( " main_loop_worker " , 1 ) ~= 1 then
return
end
ngx.log ( ngx.INFO , " nginx-metrics-graphite main loop worker PID is " .. ngx.worker . pid ( ) )
local this = self
local callback
callback = function ( premature )
-- first create the new timer to keep our intervals as good as possible
-- (not when called premature since nginx is going to shut down soon)
if not premature then
local ok , err = ngx.timer . at ( this.interval , callback )
if not ok then
ngx.log ( ngx.ERR , " nginx-metrics-graphite callback failed to create interval timer: " , err )
return
end
end
-- then do the work which might incur delays
2017-03-01 23:10:09 +01:00
-- submit the metrics to each configured carbon host
2017-03-01 23:31:25 +01:00
for i , carbon_host in ipairs ( this.carbon_hosts ) do
2017-03-01 23:10:09 +01:00
local sock , err = ngx.socket . tcp ( )
if err then
ngx.log ( ngx.ERR , " nginx-metrics-graphite callback failed to create carbon host # " .. i .. " socket: " , err )
return
end
2016-11-23 19:40:22 +01:00
2017-03-01 23:10:09 +01:00
-- connect to carbon host with submission port via TCP
2018-11-09 14:48:39 +01:00
local ok , err2 = sock : connect ( carbon_host , 2003 )
2017-03-01 23:10:09 +01:00
if not ok then
2018-11-09 14:48:39 +01:00
ngx.log ( ngx.ERR , " nginx-metrics-graphite callback failed to connect carbon host # " .. i .. " socket: " , err2 )
2017-03-01 23:10:09 +01:00
return
end
2016-11-23 19:40:22 +01:00
2017-03-01 23:10:09 +01:00
local avg_request_time = this.stats : get ( " request_time_sum " ) / this.stats : get ( " request_time_num " )
self.stats : set ( " request_time_sum " , 0 )
self.stats : set ( " request_time_num " , 0 )
2016-11-23 19:40:22 +01:00
2017-03-01 23:10:09 +01:00
-- submit metrics
sock : send ( this.mbase .. " .nginx_metrics.num_requests " .. this.stats : get ( " requests " ) .. " " .. ngx.time ( ) .. " \n " )
sock : send ( this.mbase .. " .nginx_metrics.num_upstream_requests " .. this.stats : get ( " upstream_requests " ) .. " " .. ngx.time ( ) .. " \n " )
sock : send ( this.mbase .. " .nginx_metrics.num_gzip_requests " .. this.stats : get ( " gzip_requests " ) .. " " .. ngx.time ( ) .. " \n " )
sock : send ( this.mbase .. " .nginx_metrics.num_ssl_requests " .. this.stats : get ( " ssl_requests " ) .. " " .. ngx.time ( ) .. " \n " )
2016-11-29 21:10:51 +01:00
2017-03-01 23:10:09 +01:00
sock : send ( this.mbase .. " .nginx_metrics.acc_request_length " .. this.stats : get ( " request_length " ) .. " " .. ngx.time ( ) .. " \n " )
sock : send ( this.mbase .. " .nginx_metrics.acc_bytes_sent " .. this.stats : get ( " bytes_sent " ) .. " " .. ngx.time ( ) .. " \n " )
2016-11-29 21:10:51 +01:00
2017-03-01 23:10:09 +01:00
sock : send ( this.mbase .. " .nginx_metrics.avg_request_time " .. avg_request_time .. " " .. ngx.time ( ) .. " \n " )
2016-11-23 19:40:22 +01:00
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_status ) do
2017-03-01 23:10:09 +01:00
sock : send ( this.mbase .. " .nginx_metrics.num_ " .. k .. " " .. this.stats : get ( k ) .. " " .. ngx.time ( ) .. " \n " )
end
2016-11-23 19:40:22 +01:00
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_method ) do
2017-03-01 23:10:09 +01:00
sock : send ( this.mbase .. " .nginx_metrics.num_ " .. k .. " " .. this.stats : get ( k ) .. " " .. ngx.time ( ) .. " \n " )
end
2016-11-29 20:26:43 +01:00
2018-11-09 14:48:39 +01:00
for k , _ in pairs ( self.query_http ) do
2017-03-01 23:10:09 +01:00
sock : send ( this.mbase .. " .nginx_metrics.num_ " .. k .. " " .. this.stats : get ( k ) .. " " .. ngx.time ( ) .. " \n " )
end
2016-11-23 19:40:22 +01:00
2017-03-01 23:10:09 +01:00
sock : close ( )
end
2016-11-23 19:40:22 +01:00
end
-- start first timer
local ok , err = ngx.timer . at ( this.interval , callback )
if not ok then
ngx.log ( ngx.ERR , " nginx-metrics-graphite callback failed to create interval timer: " , err )
return
end
end
function MetricsGraphite : log ( )
-- function by default called on every request,
-- should be fast and only do important calculations here
self.stats : incr ( " requests " , 1 )
2016-11-29 20:26:43 +01:00
if ngx.var . upstream_response_time ~= nil then
self.stats : incr ( " upstream_requests " , 1 )
end
if ngx.var . gzip_ratio ~= nil then
self.stats : incr ( " gzip_requests " , 1 )
end
if ngx.var . ssl_protocol ~= nil then
self.stats : incr ( " ssl_requests " , 1 )
end
2016-11-23 19:40:22 +01:00
for k , v in pairs ( self.query_status ) do
if ngx.status >= v and ngx.status < v + 100 then
self.stats : incr ( k , 1 )
break
end
end
local is_method_other = true
for k , v in pairs ( self.query_method ) do
if ngx.req . get_method ( ) == v then
self.stats : incr ( k , 1 )
is_method_other = false
break
end
end
if is_method_other then
self.stats : incr ( " method_other " , 1 )
end
2016-11-29 20:26:43 +01:00
for k , v in pairs ( self.query_http ) do
-- float equaliy
if math.abs ( v - ngx.req . http_version ( ) ) < 0.01 then
self.stats : incr ( k , 1 )
break
end
end
2016-11-23 19:40:22 +01:00
local request_length = ngx.var . request_length -- in bytes
self.stats : incr ( " request_length " , request_length )
local bytes_sent = ngx.var . bytes_sent -- in bytes
self.stats : incr ( " bytes_sent " , bytes_sent )
local request_time = ngx.now ( ) - ngx.req . start_time ( ) -- in seconds
self.stats : incr ( " request_time_sum " , request_time )
self.stats : incr ( " request_time_num " , 1 )
end
return MetricsGraphite