Mercurial > prosody-modules
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 |