Current section
Files
Jump to
Current section
Files
lib/riemannx.ex
defmodule Riemannx do
@moduledoc """
Riemannx is a riemann client that supports UDP/TCP sockets and also supports
a hybrid connection where smaller packets are sent via UDP and the rest over
TCP.
## Examples
To use riemannx all you need to do is fill out some config entries - after
that everything just happens automagically (save for the actual sending of
course):
```
config :riemannx, [
# Client settings
host: "127.0.0.1",
tcp_port: 5555,
udp_port: 5555,
# Must be the same as server side, the default is riemann's default.
max_udp_size: 16384,
type: :combined,
# How many times to re-attempt a TCP connection before crashing.
retry_count: 5,
# Interval to wait before the next TCP connection attempt.
retry_interval: 1,
# Poolboy settings
pool_size: 5, # Pool size will be 10 if you use a combined type.
max_overflow: 5, # Max overflow will be 10 if you use a combined type.
strategy: :fifo # See Riemannx.Settings documentation for more info.
]
```
Riemannx supports two `send` methods, one asynchronous the other synchronous:
### Synchronous Send
Synchronous sending allows you to handle the errors that might occur during
send, below is an example showing both how this error looks and what happens
on a successful send:
```
event = [service: "riemannx-elixir",
metric: 1,
attributes: [a: 1],
description: "test"]
case Riemannx.send(event) do
:ok ->
"Success!"
[error: error, msg: encoded_msg] ->
# The error will always be a string so you can output it as it is.
#
# The encoded message is a binary blob but you can use the riemannx proto
# msg module to decode it if you wish to see it in human readable form.
msg = encoded_msg |> Riemannx.Proto.Msg.decode()
end
```
### Asynchronous Send
Asynchronous sending is much faster but you never really know if your message
made it, in a lot of cases this kind of sending is safe enough and for most
use cases the recommended choice. It's fairly simple to implement:
```
event = [service: "riemannx-elixir",
metric: 1,
attributes: [a: 1],
description: "test"]
Riemannx.send_async(event)
# Who knows if it made it? Who cares? 60% of the time it works everytime!
```
NOTE: If a worker is unable to send it will die and be restarted giving it
a chance to return to a 'correct' state. On an asynchronous send this is done
by pattern matching :ok with the send command, for synchronous sends if the
return value is an error we kill the worker before returning the result.
### TLS
TLS support allows you to use a secure TCP connection with your riemann
server, to learn more about how to set this up take a look here:
[Secure Riemann Traffic Using TLS](http://riemann.io/howto.html#securing-traffic-using-tls)
If you choose to use TLS you will be using a purely TCP setup, combined is not
supported (and shouldn't be either) with TLS:
```elixir
config :riemannx, [
host: "127.0.0.1",
tcp_port: 5555,
type: :tls,
key: "path/to/key",
cert: "path/to/cert",
verify_peer: true
]
```
Assuming you have set up the server-side correctly this should be all you need to get started.
"""
alias Riemannx.Proto.Event
alias Riemannx.Proto.Msg
alias Riemannx.Proto.Query
import Riemannx.Settings
@type events :: [Keyword.t()] | Keyword.t()
def send(events) do
events
|> create_events_msg()
|> enqueue_sync()
end
def send_async(events) do
events
|> create_events_msg()
|> enqueue()
end
def query(_q, _t \\ 5000)
def query(query, timeout) when is_list(query) do
query
|> :erlang.list_to_binary
|> query(timeout)
end
def query(query, timeout) when is_binary(query) do
query = [query: Query.new(string: query)]
query
|> Msg.new()
|> Msg.encode()
|> enqueue_query(timeout)
end
def create_events_msg(events) do
[events: Event.list_to_events(events)]
|> Msg.new
|> Msg.encode
end
defp enqueue_query(message, timeout) do
result = case type() do
type when type in [:tls, :tcp] ->
worker = Riemannx.Connection.get_worker(message)
if is_pid(worker), do: Riemannx.Connection.query(worker, message, self())
type when type in [:combined, :udp] ->
Riemannx.Connection.query(nil, message, self())
end
if result == :ok do
receive do
{:ok, []} -> []
{:ok, msg} -> Event.deconstruct(msg.events)
error -> error
after timeout -> [error: "Query timed out", message: message]
end
else
result
end
end
defp enqueue_sync(message) do
case Riemannx.Connection.get_worker(message) do
worker when is_pid(worker) ->
result = Riemannx.Connection.send(worker, message)
unless result == :ok, do: GenServer.stop(worker, :unable_to_send)
result
error ->
error
end
end
defp enqueue(message) do
case Riemannx.Connection.get_worker(message) do
worker when is_pid(worker) ->
Riemannx.Connection.send_async(worker, message)
_error ->
:ok
end
end
end