Skip to content

Commit

Permalink
Add documentation (lexmag#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
whatyouhide authored and lexmag committed Nov 15, 2016
1 parent ba93d43 commit 592118b
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/_build
/deps
/doc
erl_crash.dump
*.ez
241 changes: 235 additions & 6 deletions lib/statix.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,236 @@
defmodule Statix do
@moduledoc """
Writer for [StatsD](https://github.com/etsy/statsd)-compatible servers.
To get started with Statix, you have to create a module that calls `use
Statix`, like this:
defmodule MyApp.Statix do
use Statix
end
This will make `MyApp.Statix` a Statix connection that implements the `Statix`
behaviour. This connection can be started with the `MyApp.Statix.connect/0`
function (see the `c:connect/0` callback) and a few functions can be called on
it to report metrics to the StatsD-compatible server read from the
configuration. Usually, `connect/0` is called in your application's
`c:Application.start/2` callback:
def start(_type, _args) do
MyApp.Statix.connect
# ...
end
## Configuration
Statix can be configured either globally or on a per-connection basis.
The global configuration will affect all Statix connections created with
`use Statix`; it can be specified by configuring the `:statix` application:
config :statix,
prefix: "sample",
host: "stats.tld",
port: 8181
The per-connection configuration can be specified by configuring each specific
connection module under the `:statix` application:
config :statix, MyApp.Statix,
port: 8123
The following is a list of all the supported options:
* `:prefix` - (binary or `nil`) all metrics sent to the StatsD-compatible
server through the configured Statix connection will be prefixed with the
value of this option. If `nil`, metrics will not be prefixed. Defaults to
`nil`.
* `:host` - (binary) the host where the StatsD-compatible server lives.
Defaults to `"127.0.0.1"`.
* `:port` - (integer) the port (on `:host`) where the StatsD-compatible
server is running. Defaults to `8125`.
By default, the configuration is evaluated once, at compile time. If you plan
on changing the configuration at runtime, you must specify the
`:runtime_config` option to be `true` when calling `use Statix`:
defmodule MyApp.Statix do
use Statix, runtime_config: true
end
## Tags
Tags are a way of adding dimensions to metrics:
MyApp.Statix.gauge("memory", 1, tags: ["region:east"])
In the example above, the `memory` measurement has been tagged with
`region:east`. Not all StatsD-compatible servers support this feature.
## Sampling
All the callbacks from the `Statix` behaviour that accept options support
sampling via the `:sample_rate` option (see also the `t:options/0` type).
MyApp.Statix.increment("page_view", 1, sample_rate: 0.5)
In the example above, the UDP packet will only be sent to the server about
half of the time, but the resulting value will be adjusted on the server
according to the given sample rate.
"""

alias __MODULE__.Conn

@type key :: iodata
@type options :: [sample_rate: float, tags: [String.t]]
@type on_send :: :ok | {:error, term}

@doc """
Opens the connection to the StatsD-compatible server.
The configuration is read from the configuration for the `:statix` application
(both globally and per connection).
"""
@callback connect() :: :ok

@doc """
Increments the StatsD counter identified by `key` by the given `value`.
`value` is supposed to be zero or positive and `c:decrement/3` should be
used for negative values.
## Examples
iex> MyApp.Statix.increment("hits", 1, [])
:ok
"""
@callback increment(key, value :: number, options) :: on_send

@doc """
Same as `increment(key, 1, [])`.
"""
@callback increment(key) :: on_send

@doc """
Same as `increment(key, value, [])`.
"""
@callback increment(key, value :: number) :: on_send

@doc """
Decrements the StatsD counter identified by `key` by the given `value`.
Works same as `c:increment/3` but subtracts `value` instead of adding it. For
this reason `value` should be zero or negative.
## Examples
iex> MyApp.Statix.decrement("open_connections", 1, [])
:ok
"""
@callback decrement(key, value :: number, options) :: on_send

@doc """
Same as `decrement(key, 1, [])`.
"""
@callback decrement(key) :: on_send

@doc """
Same as `decrement(key, value, [])`.
"""
@callback decrement(key, value :: number) :: on_send

@doc """
Writes to the StatsD gauge identified by `key`.
## Examples
iex> MyApp.Statix.gauge("cpu_usage", 0.83, [])
:ok
"""
@callback gauge(key, value :: String.Chars.t, options) :: on_send

@doc """
Same as `gauge(key, value, [])`.
"""
@callback gauge(key, value :: String.Chars.t) :: on_send

@doc """
Writes `value` to the histogram identified by `key`.
Not all StatsD-compatible servers support histograms. An example of a such
server [statsite](https://github.com/statsite/statsite).
## Examples
iex> MyApp.Statix.histogram("online_users", 123, [])
:ok
"""
@callback histogram(key, value :: String.Chars.t, options) :: on_send

@doc """
Same as `histogram(key, value, [])`.
"""
@callback histogram(key, value :: String.Chars.t) :: on_send

@doc """
Writes the given `value` to the StatsD timing identified by `key`.
`value` is expected in milliseconds.
## Examples
iex> MyApp.Statix.timing("rendering", 12, [])
:ok
"""
@callback timing(key, value :: String.Chars.t, options) :: on_send

@doc """
Same as `timing(key, value, [])`.
"""
@callback timing(key, value :: String.Chars.t) :: on_send

@doc """
Writes the given `value` to the StatsD set identified by `key`.
## Examples
iex> MyApp.Statix.set("unique_visitors", "user1", [])
:ok
"""
@callback set(key, value :: String.Chars.t, options) :: on_send

@doc """
Same as `set(key, value, [])`.
"""
@callback set(key, value :: String.Chars.t) :: on_send

@doc """
Measures the execution time of the given `function` and writes that to the
StatsD timing identified by `key`.
This function returns the value returned by `function`, making it suitable for
easily wrapping existing code.
## Examples
iex> MyApp.Statix.measure("integer_to_string", [], fn -> Integer.to_string(123) end)
"123"
"""
@callback measure(key, options, function :: (() -> result)) :: result when result: var

@doc """
Same as `measure(key, [], function)`.
"""
@callback measure(key, function :: (() -> result)) :: result when result: var

defmacro __using__(opts) do
current_conn =
if Keyword.get(opts, :runtime_config, false) do
Expand Down Expand Up @@ -48,6 +278,8 @@ defmodule Statix do
end

quote location: :keep do
@behaviour Statix

unquote(current_conn)

def increment(key, val \\ 1, options \\ []) when is_number(val) do
Expand All @@ -70,12 +302,6 @@ defmodule Statix do
Statix.transmit(current_conn(), :timing, key, val, options)
end

@doc """
Measure a function call.
It returns the result of the function call, making it suitable
for pipelining and easily wrapping existing code.
"""
def measure(key, options \\ [], fun) when is_function(fun, 0) do
{elapsed, result} = :timer.tc(fun)

Expand All @@ -90,18 +316,21 @@ defmodule Statix do
end
end

@doc false
def new_conn(module) do
{host, port, prefix} = load_config(module)
conn = Conn.new(host, port)
header = IO.iodata_to_binary([conn.header | prefix])
%{conn | header: header, sock: module}
end

@doc false
def open_conn(%Conn{sock: module} = conn) do
conn = Conn.open(conn)
Process.register(conn.sock, module)
end

@doc false
def transmit(conn, type, key, val, options \\ [])
when (is_binary(key) or is_list(key)) and is_list(options) do
if Keyword.get(options, :sample_rate, 1.0) >= :rand.uniform() do
Expand Down
2 changes: 2 additions & 0 deletions lib/statix/conn.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Statix.Conn do
@moduledoc false

defstruct [:sock, :header]

alias Statix.Packet
Expand Down
2 changes: 2 additions & 0 deletions lib/statix/packet.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
defmodule Statix.Packet do
@moduledoc false

use Bitwise

otp_release = :erlang.system_info(:otp_release)
Expand Down
7 changes: 6 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ defmodule Statix.Mixfile do
version: "0.8.0",
elixir: "~> 1.2",
description: description(),
package: package()]
package: package(),
deps: deps()]
end

def application() do
Expand All @@ -22,4 +23,8 @@ defmodule Statix.Mixfile do
licenses: ["ISC"],
links: %{"GitHub" => "https://github.com/lexmag/statix"}]
end

defp deps() do
[{:ex_doc, ">= 0.0.0", only: :docs}]
end
end
2 changes: 2 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
%{"earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []},
"ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}}

0 comments on commit 592118b

Please sign in to comment.