Skip to content

Commit

Permalink
Implement Pushgateway support
Browse files Browse the repository at this point in the history
  • Loading branch information
grobie committed May 20, 2014
1 parent f36e7b0 commit 28d4953
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 3 deletions.
27 changes: 24 additions & 3 deletions README.md
Expand Up @@ -12,7 +12,7 @@ through a JSON web services interface. Intended to be used together with a

## Usage

### Library
### Overview

```ruby
require 'prometheus/client'
Expand Down Expand Up @@ -56,6 +56,28 @@ Start the server and have a look at the metrics endpoint:
For further instructions and other scripts to get started, have a look at the
integrated [example application](examples/rack/README.md).

### Pushgateway

The Ruby client can also be used to push its collected metrics to a
[Pushgateway][8]. This comes in handy with batch jobs or in other scenarios
where it's not possible or feasible to let a Prometheus server scrape a Ruby
process.

```ruby
require 'prometheus/client'
require 'prometheus/client/push'

prometheus = Prometheus::Client.registry
# ... register some metrics, set/add/increment/etc. their values

# push the registry state to the default gateway
Prometheus::Client::Push.new('my-batch-job').push(prometheus)

# optional: specify the instance name (instead of IP) and gateway
Prometheus::Client::Push.new(
'my-job', 'instance-name', 'http://example.domain:1234').push(prometheus)
```

## Metrics

The following metric types are currently supported.
Expand Down Expand Up @@ -115,8 +137,6 @@ summary.get({ service: 'database' })

## Todo

* add push support to a vanilla prometheus exporter
* use a more performant JSON library
* add protobuf support

## Tests
Expand All @@ -135,3 +155,4 @@ rake
[5]: https://gemnasium.com/prometheus/client_ruby.svg
[6]: https://codeclimate.com/github/prometheus/client_ruby.png
[7]: https://coveralls.io/repos/prometheus/client_ruby/badge.png?branch=master
[8]: https://github.com/prometheus/pushgateway
51 changes: 51 additions & 0 deletions lib/prometheus/client/push.rb
@@ -0,0 +1,51 @@
# encoding: UTF-8

require 'net/http'
require 'uri'

require 'prometheus/client'
require 'prometheus/client/formats/text'

module Prometheus
# Client is a ruby implementation for a Prometheus compatible client.
module Client
# Push implements a simple way to transmit a given registry to a given
# Pushgateway.
class Push
DEFAULT_GATEWAY = 'http://localhost:9091'
PATH = '/metrics/jobs/%s'
INSTANCE_PATH = '/metrics/jobs/%s/instances/%s'
HEADER = { 'Content-Type' => Formats::Text::CONTENT_TYPE }

attr_reader :job, :instance, :gateway, :path

def initialize(job, instance = nil, gateway = nil)
@job, @instance, @gateway = job, instance, gateway || DEFAULT_GATEWAY

@uri = parse(@gateway)
@path = format(instance ? INSTANCE_PATH : PATH, job, instance)
end

def push(registry)
data = Formats::Text.marshal(registry)
http = Net::HTTP.new(@uri.host, @uri.port)

http.send_request('PUT', @path, data, HEADER)
end

private

def parse(url)
uri = URI.parse(url)

if uri.scheme == 'http'
uri
else
fail ArgumentError, 'only HTTP gateway URLs are supported currently.'
end
rescue URI::InvalidURIError => e
raise ArgumentError, "#{url} is not a valid URL: #{e}"
end
end
end
end
57 changes: 57 additions & 0 deletions spec/prometheus/client/push_spec.rb
@@ -0,0 +1,57 @@
# encoding: UTF-8

require 'prometheus/client/push'

describe Prometheus::Client::Push do
let(:registry) { Prometheus::Client.registry }
let(:push) { Prometheus::Client::Push.new('test-job') }

describe '.new' do
it 'returns a new push instance' do
expect(push).to be_a(Prometheus::Client::Push)
end

it 'uses localhost as default Pushgateway' do
expect(push.gateway).to eql('http://localhost:9091')
end

it 'allows to specify a custom Pushgateway' do
push = Prometheus::Client::Push.new('test-job', nil, 'http://pu.sh:1234')

expect(push.gateway).to eql('http://pu.sh:1234')
end

it 'raises an ArgumentError if a given gateway URL can not be parsed' do
expect do
Prometheus::Client::Push.new('test-job', nil, 'inva.lid:1233')
end.to raise_error ArgumentError
end

it 'uses the default metrics path if no instance value given' do
push = Prometheus::Client::Push.new('test-job')

expect(push.path).to eql('/metrics/jobs/test-job')
end

it 'uses the full metrics path if an instance value is given' do
push = Prometheus::Client::Push.new('bar-job', 'foo')

expect(push.path).to eql('/metrics/jobs/bar-job/instances/foo')
end
end

describe '#push' do
it 'pushes a given registry to the configured Pushgateway' do
http = double(:http)
http.should_receive(:send_request).with(
'PUT',
'/metrics/jobs/foo/instances/bar',
Prometheus::Client::Formats::Text.marshal(registry),
'Content-Type' => Prometheus::Client::Formats::Text::CONTENT_TYPE,
)
Net::HTTP.should_receive(:new).with('push.er', 9091).and_return(http)

described_class.new('foo', 'bar', 'http://push.er:9091').push(registry)
end
end
end

0 comments on commit 28d4953

Please sign in to comment.