Current section
Files
Jump to
Current section
Files
lib/dogstatsd/statsd.ex
defmodule DogStatsd.Statsd do
defmacro __using__(_opts) do
quote do
require Logger
def gauge(dogstatsd, stat, value, opts \\ %{}) do
send_stats dogstatsd, stat, value, :g, opts
end
def event(dogstatsd, title, text, opts \\ %{}) do
event_string = format_event(title, text, opts)
if byte_size(event_string) > 8 * 1024 do
Logger.warn "Event #{title} payload is too big (more that 8KB), event discarded"
end
send_to_socket dogstatsd, event_string
end
def format_event(title, text, opts \\ %{}) do
title = escape_event_content(title)
text = escape_event_content(text)
add_opts("_e{#{String.length(title)},#{String.length(text)}}:#{title}|#{text}", opts)
end
def add_opts(event, %{:date_happened => opt} = opts), do: add_opts("#{event}|d:#{rm_pipes(opt)}", Map.delete(opts, :date_happened))
def add_opts(event, %{:hostname => opt} = opts), do: add_opts("#{event}|h:#{rm_pipes(opt)}", Map.delete(opts, :hostname))
def add_opts(event, %{:aggregation_key => opt} = opts), do: add_opts("#{event}|k:#{rm_pipes(opt)}", Map.delete(opts, :aggregation_key))
def add_opts(event, %{:priority => opt} = opts), do: add_opts("#{event}|p:#{rm_pipes(opt)}", Map.delete(opts, :priority))
def add_opts(event, %{:source_type_name => opt} = opts), do: add_opts("#{event}|s:#{rm_pipes(opt)}", Map.delete(opts, :source_type_name))
def add_opts(event, %{:alert_type => opt} = opts), do: add_opts("#{event}|t:#{rm_pipes(opt)}", Map.delete(opts, :alert_type))
def add_opts(event, %{} = opts), do: add_tags(event, opts[:tags])
def add_tags(event, nil), do: event
def add_tags(event, []), do: event
def add_tags(event, tags) do
tags = tags
|> Enum.map(&rm_pipes/1)
|> Enum.join(",")
"#{event}|##{tags}"
end
def increment(dogstatsd, stat, opts \\ %{}) do
count dogstatsd, stat, 1, opts
end
def decrement(dogstatsd, stat, opts \\ %{}) do
count dogstatsd, stat, -1, opts
end
def count(dogstatsd, stat, count, opts \\ %{}) do
send_stats dogstatsd, stat, count, :c, opts
end
def histogram(dogstatsd, stat, value, opts \\ %{}) do
send_stats dogstatsd, stat, value, :h, opts
end
def timing(dogstatsd, stat, ms, opts \\ %{}) do
send_stats dogstatsd, stat, ms, :ms, opts
end
defmacro time(dogstatsd, stat, opts \\ Macro.escape(%{}), do_block) do
quote do
function = fn -> unquote do_block[:do] end
{elapsed, result} = :timer.tc(DogStatsd, :_time_apply, [function])
DogStatsd.timing(unquote(dogstatsd), unquote(stat), trunc(elapsed / 1000), unquote(opts))
result
end
end
def _time_apply(function), do: function.()
def set(dogstatsd, stat, value, opts \\ %{}) do
send_stats dogstatsd, stat, value, :s, opts
end
def batch(dogstatsd, function) do
function.(DogStatsd.Batched)
DogStatsd.flush_buffer(dogstatsd)
end
def send_stats(dogstatsd, stat, delta, type, opts \\ %{})
def send_stats(dogstatsd, stat, delta, type, %{:sample_rate => _sample_rate} = opts) do
opts = Map.put(opts, :sample, :rand.uniform)
send_to_socket dogstatsd, get_global_tags_and_format_stats(dogstatsd, stat, delta, type, opts)
end
def send_stats(dogstatsd, stat, delta, type, opts) do
send_to_socket dogstatsd, get_global_tags_and_format_stats(dogstatsd, stat, delta, type, opts)
end
def get_global_tags_and_format_stats(dogstatsd, stat, delta, type, opts) do
opts = update_in opts, [:tags], &((DogStatsd.tags(dogstatsd) ++ (&1 || [])) |> Enum.uniq)
format_stats(dogstatsd, stat, delta, type, opts)
end
def format_stats(_dogstatsd, _stat, _delta, _type, %{:sample_rate => sr, :sample => s}) when s > sr, do: nil
def format_stats(dogstatsd, stat, delta, type, %{:sample => s} = opts), do: format_stats(dogstatsd, stat, delta, type, Map.delete(opts, :sample))
def format_stats(dogstatsd, stat, delta, type, %{:sample_rate => sr} = opts) do
"#{DogStatsd.prefix(dogstatsd)}#{format_stat(stat)}:#{delta}|#{type}|@#{sr}"
|> add_tags(opts[:tags])
end
def format_stats(dogstatsd, stat, delta, type, opts) do
"#{DogStatsd.prefix(dogstatsd)}#{format_stat(stat)}:#{delta}|#{type}"
|> add_tags(opts[:tags])
end
def format_stat(stat) do
String.replace stat, ~r/[:|@]/, "_"
end
def send_to_socket(_dogstatsd, nil), do: nil
def send_to_socket(_dogstatsd, []), do: nil
def send_to_socket(_dogstatsd, message) when byte_size(message) > 8 * 1024, do: nil
def send_to_socket(dogstatsd, message) do
Logger.debug "DogStatsd: #{message}"
{:ok, socket} = :gen_udp.open(0)
:gen_udp.send(socket,
host(dogstatsd) |> String.to_char_list,
port(dogstatsd),
message |> String.to_char_list)
:gen_udp.close(socket)
end
def escape_event_content(msg) do
String.replace(msg, "\n", "\\n")
end
def rm_pipes(msg) do
String.replace(msg, "|", "")
end
defoverridable [send_to_socket: 2]
end
end
end