Skip to content

Commit

Permalink
Add client for InfluxDB
Browse files Browse the repository at this point in the history
  • Loading branch information
jnbt committed Nov 24, 2015
1 parent 43628d8 commit 8b525d8
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 0 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,41 @@ A simple UDP client for [InfluxDB](https://influxdb.com)
:construction: :warning:
**This project is in very stage. Things will change!**

## Install

$ [sudo] gem install linr

Or add it to your Gemfile, etc.

## Usage

Connect to a InfluxDB host via
[UDP](https://influxdb.com/docs/v0.9/write_protocols/udp.html)
and send a series:

```ruby
require "linr"

client = Linr::Client.new(host: "127.0.0.1", port: 8836)
client.write(
measurement: "cpu_load_short",
tags: { host: "server01", region: "us-west" },
fields: { value: 0.64 },
timestamp: 1434055562
)
```

## References

* [Line Protocol Syntax](https://influxdb.com/docs/v0.9/write_protocols/write_syntax.html)
* [InfluxDB UDP](https://influxdb.com/docs/v0.9/write_protocols/udp.html)

## Testing

Just run

$ rake


## Contributing

Expand Down
3 changes: 3 additions & 0 deletions lib/linr.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
require "linr/version"
require "linr/config"
require "linr/client"
require "linr/data"
require "linr/encoder"
require "linr/connection"
require "linr/logger"

# A simple UDP client for InfluxDB
module Linr
Expand Down
60 changes: 60 additions & 0 deletions lib/linr/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module Linr
# Simple UDP-based InfluxDB client
# @example
# client = Client.new(host: "127.0.0.1", port: 8063)
# client.write(
# measurement: "cpu_load_short",
# tags: { host: "server01", region: "us-west" },
# fields: { value: 0.64 },
# timestamp: 1434055562
# )
class Client
attr_reader :config

# Initialize a new client.
# @param opts [Hash] See {Config} for opts
# @see Config
def initialize(opts = {})
@config = Config.new(opts)
connect!
end

# Write one or more series to the InfluxDB connection
# @param series [Array<Hash>] see {Data} for values
def write(*series)
payload = build_payload(series)
send(payload)
end

private

def connect!
@connection = Connection::UDP.new(config.host, config.port)
info("Connected to #{config.host}:#{config.port}")
end

def build_payload(series)
series.map do |serie|
data = Data.new(serie)
encode(data)
end.join("\n")
end

def send(payload)
debug(payload)
@connection.send(payload)
end

def encode(data)
config.encoder.dump(data)
end

def info(matter)
config.logger.info("Linr") { matter }
end

def debug(matter)
config.logger.debug("Linr") { matter }
end
end
end
24 changes: 24 additions & 0 deletions lib/linr/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Linr
# Holds the config of a {Client}
# @example
# Config.new(host: "db.domain.com", logger: Logger.new($stdout))
class Config
attr_reader :host
attr_reader :port
attr_reader :logger
attr_reader :encoder

# Build a config based on opts
# @param opts [Hash] the options to create the config
# @option opts [String] :host
# @option opts [Fixnum] :port
# @option opts [::Logger] :logger
# @option opts [Object] :encoder
def initialize(opts = {})
@host = opts.fetch(:host, "127.0.0.1")
@port = opts.fetch(:port, 8089)
@logger = opts.fetch(:logger, Logger::Null.new)
@encoder = opts.fetch(:encoder, Encoder::Line.new)
end
end
end
9 changes: 9 additions & 0 deletions lib/linr/data.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
module Linr
# Describes series send to InfluxDB
# @see https://influxdb.com/docs/v0.9/guides/writing_data.html
# @example
# Data.new(
# measurement: cpu_load_short,
# tags: { host: "server01", region: "us-west" },
# fields: { value: 0.64 },
# timestamp: 1434055562
# )
class Data
attr_reader :measurement
attr_reader :fields
Expand Down
1 change: 1 addition & 0 deletions lib/linr/logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require "linr/logger/null"
13 changes: 13 additions & 0 deletions lib/linr/logger/null.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Linr
module Logger
# A simple null logger without any output
# @api private
class Null < ::Logger
def initialize(*args)
end

def add(*args, &block)
end
end
end
end
47 changes: 47 additions & 0 deletions spec/client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require "spec_helper"

describe ::Linr::Client do
subject { ::Linr::Client.new(config) }
let(:config) do
{ port: port, logger: logger }
end
let(:logger) do
::Logger.new(output).tap { |l| l.level = Logger::DEBUG }
end
let(:output) { StringIO.new }
let(:socket) { UDPSocket.new }
let(:port) { 44_001 }

before do
socket.bind("127.0.0.1", port)
end

after do
socket.close
end

it "holds a config" do
subject.config.logger.must_equal logger
end

it "writes a single series to socket" do
subject.write(measurement: "m", fields: { a: 1.0 })
socket.recvfrom(60).first.must_equal "m a=1.0"
end

it "writes multiple series to socket" do
serie1 = { measurement: "m", fields: { a: 1.0 } }
serie2 = { measurement: "n", fields: { b: true }, timestamp: 1_234_567 }
subject.write(serie1, serie2)

socket.recvfrom(80).first.must_equal "m a=1.0\nn b=true 1234567"
end

it "logs payloads" do
subject.write(measurement: "m", fields: { a: 1.0 })
lines = output.string.split("\n")
lines.size.must_equal 2
lines.shift.must_match(/Connected to 127\.0\.0\.1:44001/)
lines.shift.must_match("m a=1")
end
end
42 changes: 42 additions & 0 deletions spec/config_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require "spec_helper"

describe ::Linr::Config do
describe "no options" do
subject { ::Linr::Config.new }

it "has defaults" do
subject.host.must_equal "127.0.0.1"
subject.port.must_equal 8089
end
end

describe "with options" do
subject { ::Linr::Config.new(options) }

describe "minimal options" do
let(:options) { {} }

it "has defaults" do
subject.host.must_equal "127.0.0.1"
subject.port.must_equal 8089
end
end

describe "maximal options" do
let(:options) do
{
host: "localhost",
port: 1337,
logger: logger
}
end
let(:logger) { Logger.new($stdout) }

it "holds config" do
subject.host.must_equal "localhost"
subject.port.must_equal 1337
subject.logger.must_equal logger
end
end
end
end
4 changes: 4 additions & 0 deletions spec/connection/udp_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
socket.bind(host, port)
end

after do
socket.close
end

it "sends data as passed" do
payload = "measurement,foo=bar,bat=baz value=12,otherval=21 1439587925"
subject.send(payload)
Expand Down
10 changes: 10 additions & 0 deletions spec/logger/null_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require "spec_helper"

describe ::Linr::Logger::Null do
subject { ::Linr::Logger::Null.new }

it "does nothing" do
subject.debug "foo"
subject.add "bar"
end
end

0 comments on commit 8b525d8

Please sign in to comment.