File: //usr/share/sysdig/chisels/statsd.lua
--[[
Copyright (C) 2013-2018 Draios Inc dba Sysdig.
This file is part of sysdig.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--]]
-- Statsd client
--
-- For statsd protocol info: https://github.com/b/statsd_spec
local math = require "math"
local os = require "os"
math.randomseed(os.time())
local function send_to_socket(self, string)
return sysdig.udp_send(string)
end
local function make_statsd_message(self, stat, delta, kind, sample_rate)
-- Build prefix
local prefix = ""
if self.namespace ~= nil then prefix = self.namespace.."." end
-- Escape the stat name
stat = stat:gsub("[:|@]", "_")
-- Append the sample rate
local rate = ""
if sample_rate ~= 1 then rate = "|@"..sample_rate end
return prefix..stat..":"..delta.."|"..kind..rate
end
local function send(self, stat, delta, kind, sample_rate, neg)
local packet_size = self.packet_size
local msg
local stat_type = type(stat)
if stat_type == 'table' then sample_rate = delta end
sample_rate = sample_rate or 1
if not (sample_rate == 1 or math.random() <= sample_rate) then
return
end
if stat_type == 'table' then
local t, size = {}, 0
for s, v in pairs(stat) do
if kind == 'c' then
if type(s) == 'number' then
-- this is array of keys ( increment{'register', 'register_accept'})
s, v = v, 1
end
v = neg and -v or v
end
msg = make_statsd_message(self, s, v, kind, sample_rate)
size = size + #msg
if t[1] and (size > packet_size) then
local msg = table.concat(t, "\n")
local ok, err = self:send_to_socket(msg)
if not ok then return nil, err end
t, size = {}, 0
end
t[#t + 1] = msg
end
msg = table.concat(t, "\n")
else
msg = make_statsd_message(self, stat, delta, kind, sample_rate)
end
return self:send_to_socket(msg)
end
-- Record an instantaneous measurement. It's different from a counter in that
-- the value is calculated by the client rather than the server.
local function gauge(self, stat, value, sample_rate)
return self:send(stat, value, "g", sample_rate)
end
local function counter_(self, stat, value, sample_rate, ...)
return self:send(stat, value, "c", sample_rate, ...)
end
-- A counter is a gauge whose value is calculated by the statsd server. The
-- client merely gives a delta value by which to change the gauge value.
local function counter(self, stat, value, sample_rate)
return counter_(self, stat, value, sample_rate)
end
-- Increment a counter by `value`.
local function increment(self, stat, value, sample_rate)
return counter_(self, stat, value or 1, sample_rate, false)
end
-- Decrement a counter by `value`.
local function decrement(self, stat, value, sample_rate)
value = value or 1
if type(stat) == 'string' then value = -value end
return counter_(self, stat, value, sample_rate, true)
end
-- A timer is a measure of the number of milliseconds elapsed between a start
-- and end time, for example the time to complete rendering of a web page for
-- a user.
local function timer(self, stat, ms)
return self:send(stat, ms, "ms")
end
-- A histogram is a measure of the distribution of timer values over time,
-- calculated by the statsd server. Not supported by all statsd implementations.
local function histogram(self, stat, value)
return self:send(stat, value, "h")
end
-- A meter measures the rate of events over time, calculated by the Statsd
-- server. Not supported by all statsd implementations.
local function meter(self, stat, value)
return self:send(stat, value, "m")
end
-- A set counts unique occurrences of events between flushes. Not supported by
-- all statsd implementations.
local function set(self, stat, value)
return self:send(stat, value, "s")
end
return function(options)
options = options or {}
local host = options.host or "127.0.0.1"
local port = options.port or 8125
local namespace = options.namespace or nil
local packet_size = options.packet_size or 508 -- RFC791
sysdig.udp_setpeername(host, port)
return {
namespace = namespace,
udp = udp,
packet_size = packet_size,
gauge = gauge,
counter = counter,
increment = increment,
decrement = decrement,
timer = timer,
histogram = histogram,
meter = meter,
send = send,
send_to_socket = send_to_socket
}
end