-
Notifications
You must be signed in to change notification settings - Fork 0
/
metrics_graphite.lua
203 lines (169 loc) · 6.64 KB
/
metrics_graphite.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
local MetricsGraphite = {}
MetricsGraphite.__index = MetricsGraphite
function MetricsGraphite.init(carbon_hosts, interval, mbase)
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)
self.carbon_hosts = carbon_hosts
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 = ""
}
self.query_http = {
http_09 = 0.9,
http_10 = 1.0,
http_11 = 1.1,
http_20 = 2.0
}
-- initialize/reset counters
-- Note: ngx.shared.DICT is thread-safe, see https://github.com/openresty/lua-nginx-module#ngxshareddict
self.stats = ngx.shared.metrics_graphite
self.stats:set("main_loop_worker", 0)
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
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)
for k,_ in pairs(self.query_status) do
self.stats:set(k, 0)
end
for k,_ in pairs(self.query_method) do
self.stats:set(k, 0)
end
for k,_ in pairs(self.query_http) do
self.stats:set(k, 0)
end
return self
end
function MetricsGraphite:worker()
-- 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
-- caveeat: if the main loop worker dies no further metrics will be sent!
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
-- submit the metrics to each configured carbon host
for i,carbon_host in ipairs(this.carbon_hosts) do
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
-- connect to carbon host with submission port via TCP
local ok, err2 = sock:connect(carbon_host, 2003)
if not ok then
ngx.log(ngx.ERR, "nginx-metrics-graphite callback failed to connect carbon host #" .. i .. " socket: ", err2)
return
end
local avg_request_time = nil
if this.stats:get("request_time_num") > 0 then
avg_request_time = this.stats:get("request_time_sum") / this.stats:get("request_time_num")
end
self.stats:set("request_time_sum", 0)
self.stats:set("request_time_num", 0)
-- 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")
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")
if avg_request_time then
sock:send(this.mbase .. ".nginx_metrics.avg_request_time " .. avg_request_time .. " " .. ngx.time() .. "\n")
end
for k,_ in pairs(self.query_status) do
sock:send(this.mbase .. ".nginx_metrics.num_" .. k .. " " .. this.stats:get(k) .. " " .. ngx.time() .. "\n")
end
for k,_ in pairs(self.query_method) do
sock:send(this.mbase .. ".nginx_metrics.num_" .. k .. " " .. this.stats:get(k) .. " " .. ngx.time() .. "\n")
end
for k,_ in pairs(self.query_http) do
sock:send(this.mbase .. ".nginx_metrics.num_" .. k .. " " .. this.stats:get(k) .. " " .. ngx.time() .. "\n")
end
sock:close()
end
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)
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
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
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
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