--- /dev/null
+.. _mod-graphite:
+
+Graphite module
+---------------
+
+The module sends statistics over the Graphite_ protocol to either Graphite_, Metronome_, InfluxDB_ or any compatible storage. This allows powerful visualization over metrics collected by Knot DNS Resolver.
+
+.. tip:: The Graphite server is challenging to get up and running, InfluxDB_ combined with Grafana_ are much easier, and provide richer set of options and available front-ends. Metronome_ by PowerDNS alternatively provides a mini-graphite server for much simpler setups.
+
+Example configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+Only the ``host`` parameter is mandatory.
+
+.. warning:: It uses UDP so it doesn't guarantee the delivery, make sure the target server supports UDP.
+
+.. code-block:: lua
+
+ modules = {
+ graphite = {
+ prefix = hostname(), -- optional metric prefix
+ host = '127.0.0.1', -- graphite server address
+ port = 2003, -- graphite server port
+ interval = 5 * sec -- publish interval
+ }
+ }
+
+The module support sending data to multiple servers at once.
+
+.. code-block:: lua
+
+ modules = {
+ graphite = {
+ host = { '127.0.0.1', '1.2.3.4', '::1' },
+ }
+ }
+
+Dependencies
+^^^^^^^^^^^^
+
+* `luasocket <http://w3.impa.br/~diego/software/luasocket/>`_ available in LuaRocks
+
+ ``$ luarocks install socket``
+
+.. _Graphite: http://graphite.readthedocs.org/en/latest/feeding-carbon.html
+.. _InfluxDB: http://influxdb.com/
+.. _Metronome: https://github.com/ahuPowerDNS/metronome
+.. _Grafana: http://grafana.org/
\ No newline at end of file
--- /dev/null
+--- @module graphite
+local graphite = {}
+
+function graphite.init(module)
+ graphite.socket = require('socket')
+ graphite.ev = nil
+ graphite.cli = {}
+ graphite.prefix = nil
+ return 0
+end
+
+function graphite.deinit(module)
+ if graphite.ev then event.cancel(graphite.ev) end
+ return 0
+end
+
+-- @function Publish results to the Graphite server(s)
+function graphite.publish()
+ local now = os.time()
+ if not graphite.cli then error("no graphite server configured") end
+ local now_metrics = stats.list()
+ if type(now_metrics) ~= 'table' then
+ return 0 -- No metrics to watch
+ end
+ for key,val in pairs(now_metrics) do
+ for i in pairs(graphite.cli) do
+ msg = key..' '..val..' '..now..'\n'
+ if graphite.prefix then
+ msg = prefix..'.'..msg
+ end
+ graphite.cli[i]:send(msg)
+ end
+ end
+ return 0
+end
+
+-- @function Make connection to Graphite server.
+function graphite.add_server(graphite, host, port)
+ local cli, err, status
+ if host:find(':') then
+ cli, err = graphite.socket.udp6()
+ else
+ cli, err = graphite.socket.udp()
+ end
+ if not cli then
+ error(err)
+ end
+ status, err = cli:setpeername(host, port)
+ if not status then
+ error(err)
+ end
+ table.insert(graphite.cli, cli)
+ return 0
+end
+
+function graphite.config(conf)
+ -- config defaults
+ if not conf.port then conf.port = 2003 end
+ if not conf.interval then conf.interval = 5 * sec end
+ if conf.prefix then graphite.prefix = conf.prefix end
+ -- connect to host(s)
+ if type(conf.host) == 'table' then
+ for key, val in pairs(conf.host) do
+ graphite:add_server(val, conf.port)
+ end
+ else
+ graphite:add_server(conf.host, conf.port)
+ end
+ -- start publishing stats
+ if graphite.ev then event.cancel(graphite.ev) end
+ graphite.ev = event.recurrent(conf.interval, graphite.publish)
+ return 0
+end
+
+return graphite