comparison mod_prometheus/mod_prometheus.lua @ 3125:07a2ba55de4d

mod_prometheus: Add a new statistics export module, for Prometheus.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 21 Jun 2018 21:37:13 +0200
parents
children 888375de933c
comparison
equal deleted inserted replaced
3124:cabe58ae17c9 3125:07a2ba55de4d
1 -- Log common stats to statsd
2 --
3 -- Copyright (C) 2014 Daurnimator
4 --
5 -- This module is MIT/X11 licensed.
6
7 module:set_global();
8 module:depends "http";
9
10 local s_format = string.format;
11 local t_insert = table.insert;
12 local socket = require "socket";
13 local mt = require "util.multitable";
14
15 local meta = mt.new(); meta.data = module:shared"meta";
16 local data = mt.new(); data.data = module:shared"data";
17
18 local function escape(text)
19 return text:gsub("\\", "\\\\"):gsub("\"", "\\\""):gsub("\n", "\\n");
20 end
21
22 local function escape_name(name)
23 return name:gsub("[^A-Za-z0-9_]", "_"):gsub("^[^A-Za-z_]", "_%1");
24 end
25
26 local function get_timestamp()
27 -- Using LuaSocket for that because os.time() only has second precision.
28 return math.floor(socket.gettime() * 1000);
29 end
30
31 local function repr_help(metric, docstring)
32 docstring = docstring:gsub("\\", "\\\\"):gsub("\n", "\\n");
33 return "# HELP "..escape_name(metric).." "..docstring.."\n";
34 end
35
36 -- local allowed_types = { counter = true, gauge = true, histogram = true, summary = true, untyped = true };
37 -- local allowed_types = { "counter", "gauge", "histogram", "summary", "untyped" };
38 local function repr_type(metric, type_)
39 -- if not allowed_types:contains(type_) then
40 -- return;
41 -- end
42 return "# TYPE "..escape_name(metric).." "..type_.."\n";
43 end
44
45 local function repr_label(key, value)
46 return key.."=\""..escape(value).."\"";
47 end
48
49 local function repr_labels(labels)
50 local values = {}
51 for key, value in pairs(labels) do
52 t_insert(values, repr_label(escape_name(key), escape(value)));
53 end
54 if #values == 0 then
55 return "";
56 end
57 return "{"..table.concat(values, ", ").."}";
58 end
59
60 local function repr_sample(metric, labels, value, timestamp)
61 return escape_name(metric)..repr_labels(labels).." "..value.." "..timestamp.."\n";
62 end
63
64 module:hook("stats-updated", function (event)
65 local all_stats, this = event.stats_extra;
66 local host, sect, name, typ, key;
67 for stat, value in pairs(event.changed_stats) do
68 this = all_stats[stat];
69 -- module:log("debug", "changed_stats[%q] = %s", stat, tostring(value));
70 host, sect, name, typ = stat:match("^/([^/]+)/([^/]+)/(.+):(%a+)$");
71 if host == nil then
72 sect, name, typ, host = stat:match("^([^.]+)%.([^:]+):(%a+)$");
73 elseif host == "*" then
74 host = nil;
75 end
76 if sect:find("^mod_measure_.") then
77 sect = sect:sub(13);
78 elseif sect:find("^mod_statistics_.") then
79 sect = sect:sub(16);
80 end
81 key = escape_name(s_format("%s_%s_%s", host or "global", sect, typ));
82
83 if not meta:get(key) then
84 if host then
85 meta:set(key, "", "graph_title", s_format("%s %s on %s", sect, typ, host));
86 else
87 meta:set(key, "", "graph_title", s_format("Global %s %s", sect, typ, host));
88 end
89 meta:set(key, "", "graph_vlabel", this and this.units or typ);
90 meta:set(key, "", "graph_category", sect);
91
92 meta:set(key, name, "label", name);
93 elseif not meta:get(key, name, "label") then
94 meta:set(key, name, "label", name);
95 end
96
97 data:set(key, name, value);
98 end
99 end);
100
101 local function get_metrics(event)
102 local response = event.response;
103 response.headers.content_type = "text/plain; version=0.4.4";
104
105 local response = {};
106 local timestamp = tostring(get_timestamp());
107 for section, data in pairs(data.data) do
108 for key, value in pairs(data) do
109 local name = section.."_"..key;
110 t_insert(response, repr_help(name, "TODO: add a description here."));
111 t_insert(response, repr_type(name, "gauge"));
112 t_insert(response, repr_sample(name, {}, value, timestamp));
113 end
114 end
115 return table.concat(response, "");
116 end
117
118 function module.add_host(module)
119 module:provides("http", {
120 default_path = "metrics";
121 route = {
122 GET = get_metrics;
123 };
124 });
125 end