Riemannx |

A fully featured riemann client built on the reliability of poolboy and the awesome power of Elixir!
TL;DR
Riemannx is a riemann client built in elixir, currently it’s the only client in elixir that supports UDP and TLS (as well as TCP).
It has an experimental combined option that makes the best of both TCP and UDP - in the combined mode UDP is the favoured approach but if the message size exceeds the max udp size set TCP will be used.
As of 2.1.0 TLS connections are supported. As of 2.2.0 You can now query the index. As of 2.3.0 You can specify a host in config or we will work one out for you. As of 2.4.0 You can set a priority for the workers.
Contents
1. Prerequisites
As always there are prerequisites required before using Riemannx, most of these are obvious (elixir, erlang) but contain some information on which versions are tested and supported.
Erlang
Currently all erlang versions ~> 18 are supported. This includes 20, 20.1 is not yet tested but I foresee no great problems there.
Tested by travis:
- OTP:
18.0
- OTP:
19.3
- OTP:
20.0
Elixir
I have tried to ensure compatibility from 1.3.4 onwards and will continue to do so where appropriate. Tested combinations:
- OTP:
18.0
Elixir:1.3.4 / 1.4.5 / 1.5.1
- OTP:
19.3
Elixir:1.3.4 / 1.4.5 / 1.5.1
- OTP:
20.0
Elixir:1.4.5 / 1.5.1
Riemann
As is often the case, a client is fairly useless without it’s server counterpart - for more information about riemann visit http://riemann.io.
The client has only been battle tested on: 0.2.11
. It should work with the latest 0.2.14
but has not been tested.
2. Installation
Installation happens just like any other elixir library, add it to your mix file and the rest is history:
def deps do
[{:riemannx, "~> 2.3"}]
end
Make sure you add riemannx to the applications list in your mix.exs file also, this ensures it is started with your app and that it will be included in your releases (if you use a release manager):
applications: [:logger, :riemannx]
3. 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). Below is a comprehensive list of available options:
Config
config :riemannx, [
# Client settings
host: "127.0.0.1",
tcp_port: 5555,
udp_port: 5555,
max_udp_size: 16384, # Must be the same as server side, the default is riemann's default.
event_host: "test_host" # If you don't set this riemannx will use :inet.gethostname()
type: :combined, # A choice of :tcp, :udp, :combined or :tls
retry_count: 5, # How many times to re-attempt a TCP connection before crashing.
retry_interval: 1, # Interval to wait before the next TCP connection attempt.
priority: :normal, # Priority of workers.
ssl_opts: [], # Used for tls, see TLS section for details.
# Poolboy settings
pool_size: 5, # Pool size will be 5x2 (10) if you use a combined type.
max_overflow: 5, # Max overflow will be 5x2 (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()
Logger.warn("Error: #{error} Message: #{inspect msg}")
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
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:
config :riemannx, [
host: "127.0.0.1",
tcp_port: 5555,
type: :tls,
# SSL Opts are passed to the underlying ssl erlang interface
# See available options here: http://erlang.org/doc/man/ssl.html
ssl_opts: [
keyfile: "path/to/key",
certfile: "path/to/cert",
verify_peer: true
]
]
Assuming you have set up the server-side correctly this should be all you need to get started.
Querying the index
Riemann has the concept of a queryable index which allows you to search for specific events, indexes must be specially created in your config otherwise the server will return a “no index” error.
# Lets send an event that we can then query
Riemannx.send([service: "riemannx", metric: 5.0, attributes: [v: "2.2.0"]])
# Let's fish it out
events = Riemannx.query('service ~= "riemannx"')
# [%{attributes: %{"v" => "2.2.0"}, description: nil, host: _,
# metric: nil, service: "riemannx", state: nil, tags: [],
# time: _, ttl: _}]
For more information on querying and the language features have a look at the Core Concepts.
4. Special Notes
This section contains some notes on the behaviour of riemannx that may interest you or answer questions you have about certain things.
Host Injection
It sounds fancier than it is but basically describes the functionality that adds a host entry to your event if you haven’t specified one. There are 3 ways to specify a host:
Do it before you send the event (add a :host key to the keyword list)
Add the
:event_host
key to your config.Let riemannx do it using
:inet.gethostname()
- we only call that once and save the result, it is not called on every event.
The last 2 options are the most favourable as they will keep your code clean.
Process Priority
In this client there is the opportunity to set a priority for your workers allowing you to place higher or less priority on the sending of your stats to riemann.
The difference setting a priority makes depends heavily on the hardware and how you have set your other priorities in general, more info can be found here: http://erlang.org/doc/man/erlang.html#process_flag-2
If you try to set the priority to :max riemannx will raise a RuntimeError because that is a terrible idea. It will also raise a RuntimeError if you try :foo because that is also a terrible idea.
5. Contributions
Contributions are warmly received, check out the Projects section for some ideas I have written down and for the latest on what is underway.
Guidelines
This repository uses the Gitflow workflow meaning all PR’s should be pointed towards the develop branch!. Below are some things to consider before creating a PR:
I would like to maintain test coverage at 100%! - I may let this slide in urgent cases (bugs etc.)
To avoid congesting Travis unnecessarily it would be appreciated if you check the following locally first:
mix coveralls.html
(Aim for 100%)mix dialyzer
(Takes a while and I appreciate you can’t test all erlang/elixir versions)
I consider this client feature complete and fully compatible with 0.2.x versions of Riemann, if your PR adds something only 0.x.x can handle I’d appreciate a heads up.
6. Acknowledgements
A portion of code has been borrowed from the original elixir-riemann client. Most of the protobuf stuff comes from there.