grape-datadog 1.2.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -15
- data/lib/grape/datadog.rb +86 -2
- data/lib/grape/datadog/version.rb +2 -2
- data/spec/grape/datadog_spec.rb +51 -0
- data/spec/spec_helper.rb +29 -4
- metadata +4 -5
- data/lib/grape/datadog/middleware.rb +0 -62
- data/spec/grape/datadog/middleware_spec.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a406efffcb252cbd474b6397fdc301c522e93aaf
|
4
|
+
data.tar.gz: 2632cc04b6453f1302393af0a38b57fc45762d54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 603d3ae2b7146a31ae80880de9802bec51e364037ed56cb287be1c73c7e2ccefa7a0d4b95b72a927edc8c7d806aa06fe1f8e67948512ca20948a35b3183fc81d
|
7
|
+
data.tar.gz: 60de14bf54c7c1c264424f25de2d7ccde7ff7b34faa978ab07c4b4d940733aa93490057436a97e9a723f4408799bedb314756adfb78aae27a6923d39ab4dfe0c
|
data/README.md
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
grape-datadog
|
2
2
|
=============
|
3
3
|
|
4
|
-
Datadog
|
5
|
-
gem][1], which is based on [this NewRelic gem][2], using [dogstatsd-ruby][3]
|
4
|
+
Datadog intrumentation for [Grape](https://github.com/intridea/grape), integrated via [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html).
|
6
5
|
|
7
6
|
## Installation
|
8
7
|
|
@@ -14,17 +13,14 @@ Or install:
|
|
14
13
|
|
15
14
|
$ gem install grape-datadog
|
16
15
|
|
17
|
-
|
16
|
+
Configure it in an initializer:
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
get 'hello' do
|
23
|
-
"Hello World"
|
24
|
-
end
|
18
|
+
Grape::Datadog.install! do |c|
|
19
|
+
c.hostname = "my-host"
|
20
|
+
c.tags = ["my:tag"]
|
25
21
|
end
|
26
22
|
|
27
|
-
For full configuration options, please see the [Documentation][
|
23
|
+
For full configuration options, please see the [Documentation][http://www.rubydoc.info/gems/grape-datadog].
|
28
24
|
|
29
25
|
## Contributing
|
30
26
|
|
@@ -34,8 +30,3 @@ For full configuration options, please see the [Documentation][4].
|
|
34
30
|
4. Push to the branch (`git push origin my-new-feature`)
|
35
31
|
5. Make a pull request
|
36
32
|
|
37
|
-
[0]: https://github.com/intridea/grape
|
38
|
-
[1]: https://github.com/seanmoon/grape-librato
|
39
|
-
[2]: https://github.com/flyerhzm/newrelic-grape
|
40
|
-
[3]: https://github.com/DataDog/dogstatsd-ruby
|
41
|
-
[4]: http://www.rubydoc.info/gems/grape-datadog
|
data/lib/grape/datadog.rb
CHANGED
@@ -1,3 +1,87 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'grape/datadog/version'
|
2
|
+
require 'grape'
|
3
|
+
require 'statsd'
|
4
|
+
require 'socket'
|
5
|
+
require 'active_support/notifications'
|
6
|
+
|
7
|
+
module Grape
|
8
|
+
class Datadog
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
# Configure and install datadog instrumentation. Example:
|
12
|
+
#
|
13
|
+
# Grape::Datadog.install! do |c|
|
14
|
+
# c.hostname = "my-host"
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Settings:
|
18
|
+
# * <tt>hostname</tt> - the hostname used for instrumentation, defaults to system hostname, respects +INSTRUMENTATION_HOSTNAME+ env variable
|
19
|
+
# * <tt>metric_name</tt> - the metric name (prefix) to use, defaults to "grape.request"
|
20
|
+
# * <tt>tags</tt> - array of custom tags, these can be plain strings or lambda blocks accepting a rack env instance
|
21
|
+
# * <tt>statsd_host</tt> - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
|
22
|
+
# * <tt>statsd_port</tt> - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
|
23
|
+
# * <tt>statsd</tt> - custom statsd instance
|
24
|
+
def self.install!(&block)
|
25
|
+
block.call instance if block
|
26
|
+
instance.send(:subscribe!)
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :hostname, :metric_name, :statsd_host, :statsd_port, :tags, :statsd
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@hostname = ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname
|
33
|
+
@metric_name = "grape.request"
|
34
|
+
@statsd_host = ENV['STATSD_HOST'] || "localhost"
|
35
|
+
@statsd_port = (ENV['STATSD_PORT'] || 8125).to_i
|
36
|
+
@tags = []
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def subscribe!
|
42
|
+
if frozen?
|
43
|
+
warn "#{self.class.name} is already installed (called from #{caller[1]})"
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
if tags.none? {|t| t =~ /^host\:/ }
|
48
|
+
tags.push("host:#{hostname}")
|
49
|
+
end
|
50
|
+
|
51
|
+
if ENV['RACK_ENV'] && tags.none? {|t| t =~ /^env\:/ }
|
52
|
+
tags.push("env:#{ENV['RACK_ENV']}")
|
53
|
+
end
|
54
|
+
|
55
|
+
ActiveSupport::Notifications.subscribe 'endpoint_run.grape' do |_, start, finish, _, payload|
|
56
|
+
record payload[:endpoint], ((finish-start)*1000).round
|
57
|
+
end
|
58
|
+
|
59
|
+
@statsd ||= ::Statsd.new(statsd_host, statsd_port)
|
60
|
+
freeze
|
61
|
+
end
|
62
|
+
|
63
|
+
def record(endpoint, ms)
|
64
|
+
route = endpoint.route
|
65
|
+
version = route.route_version
|
66
|
+
method = route.route_method
|
67
|
+
|
68
|
+
path = route.route_path
|
69
|
+
path.sub!("(.:format)", "")
|
70
|
+
path.sub!(":version/", "") if version
|
71
|
+
path.gsub!(/\(?:(\w+)\)?/) {|m| "_#{m[1..-1]}_" }
|
72
|
+
|
73
|
+
tags = self.tags.map do |tag|
|
74
|
+
case tag when String then tag when Proc then tag.call(endpoint) end
|
75
|
+
end
|
76
|
+
tags.push "method:#{method}"
|
77
|
+
tags.push "path:#{path}"
|
78
|
+
tags.push "version:#{version}" if version
|
79
|
+
tags.push "status:#{endpoint.status}"
|
80
|
+
tags.compact!
|
81
|
+
|
82
|
+
statsd.increment metric_name, :tags => tags
|
83
|
+
statsd.timing "#{metric_name}.time", ms, :tags => tags
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
3
87
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Datadog do
|
4
|
+
|
5
|
+
def app; TestAPI; end
|
6
|
+
|
7
|
+
before { subject.statsd.buffer.clear }
|
8
|
+
subject { described_class.instance }
|
9
|
+
|
10
|
+
it 'should be configurable' do
|
11
|
+
expect(subject.tags.size).to eq(4)
|
12
|
+
expect(subject.tags).to include("host:test.host")
|
13
|
+
expect(subject.tags).to include("env:test")
|
14
|
+
expect(subject.tags).to include("custom:tag")
|
15
|
+
expect(subject.statsd).to be_instance_of(MockStatsd)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should send an increment and timing event for each request' do
|
19
|
+
get '/echo/1/1234'
|
20
|
+
expect(last_response.status).to eq(200)
|
21
|
+
expect(last_response.body).to eq('1 1234')
|
22
|
+
|
23
|
+
expect(subject.statsd.buffer).to eq([
|
24
|
+
"grape.request:1|c|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/echo/_key1_/_key2_,status:200",
|
25
|
+
"grape.request.time:333|ms|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/echo/_key1_/_key2_,status:200",
|
26
|
+
])
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should support namespaces and versioning' do
|
30
|
+
get '/api/v1/sub/versioned'
|
31
|
+
expect(last_response.status).to eq(200)
|
32
|
+
expect(last_response.body).to eq('OK')
|
33
|
+
|
34
|
+
expect(subject.statsd.buffer).to eq([
|
35
|
+
"grape.request:1|c|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/api/sub/versioned,version:v1,status:200",
|
36
|
+
"grape.request.time:333|ms|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/api/sub/versioned,version:v1,status:200",
|
37
|
+
])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should support deep nesting' do
|
41
|
+
get '/sub/secure/resource'
|
42
|
+
expect(last_response.status).to eq(403)
|
43
|
+
expect(last_response.body).to eq('forbidden')
|
44
|
+
|
45
|
+
expect(subject.statsd.buffer).to eq([
|
46
|
+
"grape.request:1|c|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/sub/secure/resource,status:403",
|
47
|
+
"grape.request.time:333|ms|#custom:tag,format:txt,host:test.host,env:test,method:GET,path:/sub/secure/resource,status:403",
|
48
|
+
])
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,19 +1,44 @@
|
|
1
|
+
ENV['RACK_ENV'] ||= 'test'
|
2
|
+
|
1
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
5
|
|
4
6
|
require 'rack/test'
|
7
|
+
require 'grape-datadog'
|
5
8
|
|
6
9
|
RSpec.configure do |config|
|
7
10
|
config.include Rack::Test::Methods
|
8
11
|
end
|
9
12
|
|
10
|
-
require 'grape'
|
11
|
-
require 'grape-datadog'
|
12
|
-
|
13
13
|
class MockStatsd < Statsd
|
14
14
|
def timing(stat, ms, opts={}); super(stat, 333, opts); end
|
15
15
|
def flush_buffer; end
|
16
16
|
alias :send_stat :send_to_buffer
|
17
17
|
end
|
18
18
|
|
19
|
-
|
19
|
+
class VersionedTestAPI < Grape::API
|
20
|
+
version 'v1'
|
21
|
+
prefix 'api'
|
22
|
+
|
23
|
+
get('versioned') { "OK" }
|
24
|
+
end
|
25
|
+
|
26
|
+
class TestAPI < Grape::API
|
27
|
+
get 'echo/:key1/:key2' do
|
28
|
+
"#{params['key1']} #{params['key2']}"
|
29
|
+
end
|
30
|
+
|
31
|
+
namespace :sub do
|
32
|
+
mount VersionedTestAPI
|
33
|
+
|
34
|
+
namespace :secure do
|
35
|
+
get("/resource") { error!("forbidden", 403) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Grape::Datadog.install! do |c|
|
41
|
+
c.hostname = "test.host"
|
42
|
+
c.statsd = MockStatsd.new(nil, nil, {}, 10000)
|
43
|
+
c.tags = ["custom:tag", lambda{|e| "format:#{e.env['api.format']}" }]
|
44
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grape-datadog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Chernyshev
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-10-
|
12
|
+
date: 2015-10-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: grape
|
@@ -110,10 +110,9 @@ files:
|
|
110
110
|
- grape-datadog.gemspec
|
111
111
|
- lib/grape-datadog.rb
|
112
112
|
- lib/grape/datadog.rb
|
113
|
-
- lib/grape/datadog/middleware.rb
|
114
113
|
- lib/grape/datadog/version.rb
|
115
|
-
- spec/grape/datadog/middleware_spec.rb
|
116
114
|
- spec/grape/datadog/version_spec.rb
|
115
|
+
- spec/grape/datadog_spec.rb
|
117
116
|
- spec/spec_helper.rb
|
118
117
|
homepage: https://github.com/bsm/grape-datadog
|
119
118
|
licenses: []
|
@@ -139,6 +138,6 @@ signing_key:
|
|
139
138
|
specification_version: 4
|
140
139
|
summary: Datadog metrics for grape
|
141
140
|
test_files:
|
142
|
-
- spec/grape/datadog/middleware_spec.rb
|
143
141
|
- spec/grape/datadog/version_spec.rb
|
142
|
+
- spec/grape/datadog_spec.rb
|
144
143
|
- spec/spec_helper.rb
|
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'grape'
|
2
|
-
require 'statsd'
|
3
|
-
require 'socket'
|
4
|
-
|
5
|
-
module Grape
|
6
|
-
module Datadog
|
7
|
-
|
8
|
-
class Middleware < ::Grape::Middleware::Base
|
9
|
-
|
10
|
-
# Create a new +Datadog+ middleware instance.
|
11
|
-
#
|
12
|
-
# ==== Options
|
13
|
-
# * <tt>:hostname</tt> - the hostname used for instrumentation, defaults to system hostname, respects +INSTRUMENTATION_HOSTNAME+ env variable
|
14
|
-
# * <tt>:metric_name</tt> - the metric name (prefix) to use, defaults to "grape.request"
|
15
|
-
# * <tt>:tags</tt> - array of custom tags, these can be plain strings or lambda blocks accepting a rack env instance
|
16
|
-
# * <tt>:statsd_host</tt> - the statsD host, defaults to "localhost", respects +STATSD_HOST+ env variable
|
17
|
-
# * <tt>:statsd_port</tt> - the statsD port, defaults to 8125, respects +STATSD_PORT+ env variable
|
18
|
-
# * <tt>:prefer_global</tt> - if set, tries to find global `$statsd` instance, otherwise connects to +statsd_host+:+statsd_port. Default: true
|
19
|
-
def initialize(app, opts = {})
|
20
|
-
hostname = opts[:hostname] || ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname
|
21
|
-
statsd_host = opts[:statsd_host] || ENV['STATSD_HOST'] || "localhost"
|
22
|
-
statsd_port = (opts[:statsd_port] || ENV['STATSD_PORT'] || 8125).to_i
|
23
|
-
|
24
|
-
@app = app
|
25
|
-
@metric = opts[:metric_name] || "grape.request"
|
26
|
-
@statsd = opts[:prefer_global] == false || !defined?($statsd) ? ::Statsd.new(statsd_host, statsd_port) : $statsd
|
27
|
-
@tags = opts[:tags] || []
|
28
|
-
@tags += ["host:#{hostname}"]
|
29
|
-
end
|
30
|
-
|
31
|
-
def call(env)
|
32
|
-
tags = prepare_tags(env)
|
33
|
-
@statsd.time "#{@metric}.time", :tags => tags do
|
34
|
-
resp = @app.call(env)
|
35
|
-
tags.push "status:#{resp.status}"
|
36
|
-
@statsd.increment @metric, :tags => tags
|
37
|
-
resp
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def prepare_tags(env)
|
44
|
-
endpoint = env['api.endpoint']
|
45
|
-
path = File.join endpoint.namespace, endpoint.options[:path].join('/').gsub(/:(\w+)/) {|m| "_#{m[1..-1]}_" }
|
46
|
-
versions = endpoint.namespace_inheritable(:version)
|
47
|
-
version = versions.first if versions
|
48
|
-
|
49
|
-
@tags.map do |tag|
|
50
|
-
case tag when String then tag when Proc then tag.call(env) end
|
51
|
-
end.compact + [
|
52
|
-
"method:#{env[Rack::REQUEST_METHOD]}",
|
53
|
-
"path:#{path}",
|
54
|
-
(version ? "version:#{version}" : nil),
|
55
|
-
].compact
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Grape::Datadog::Middleware do
|
4
|
-
|
5
|
-
class VersionedTestAPI < Grape::API
|
6
|
-
version 'v1'
|
7
|
-
prefix 'api'
|
8
|
-
|
9
|
-
get('versioned') { "OK" }
|
10
|
-
end
|
11
|
-
|
12
|
-
class TestAPI < Grape::API
|
13
|
-
use Grape::Datadog::Middleware,
|
14
|
-
hostname: "test.host",
|
15
|
-
tags: ["custom:tag", lambda {|env| "scheme:#{Rack::Request.new(env).scheme}" }]
|
16
|
-
|
17
|
-
get 'echo/:key1/:key2' do
|
18
|
-
"#{params['key1']} #{params['key2']}"
|
19
|
-
end
|
20
|
-
|
21
|
-
namespace :sub do
|
22
|
-
mount VersionedTestAPI
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def app; TestAPI; end
|
27
|
-
|
28
|
-
before { $statsd.buffer.clear }
|
29
|
-
|
30
|
-
it 'should be configurable' do
|
31
|
-
subject = described_class.new(nil, hostname: "test.host")
|
32
|
-
expect(subject.instance_variable_get(:@tags)).to eq(["host:test.host"])
|
33
|
-
expect(subject.instance_variable_get(:@statsd)).to be_instance_of(MockStatsd)
|
34
|
-
|
35
|
-
subject = described_class.new(nil, prefer_global: false)
|
36
|
-
expect(subject.instance_variable_get(:@statsd)).to be_instance_of(Statsd)
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'should send an increment and timing event for each request' do
|
40
|
-
get '/echo/1/1234'
|
41
|
-
expect(last_response.status).to eq(200)
|
42
|
-
expect(last_response.body).to eq('1 1234')
|
43
|
-
|
44
|
-
expect($statsd.buffer).to eq([
|
45
|
-
"grape.request:1|c|#custom:tag,scheme:http,host:test.host,method:GET,path:/echo/_key1_/_key2_,status:200",
|
46
|
-
"grape.request.time:333|ms|#custom:tag,scheme:http,host:test.host,method:GET,path:/echo/_key1_/_key2_,status:200",
|
47
|
-
])
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'should support namespaces and versioning' do
|
51
|
-
get '/api/v1/sub/versioned'
|
52
|
-
expect(last_response.status).to eq(200)
|
53
|
-
expect(last_response.body).to eq('OK')
|
54
|
-
|
55
|
-
expect($statsd.buffer).to eq([
|
56
|
-
"grape.request:1|c|#custom:tag,scheme:http,host:test.host,method:GET,path:/sub/versioned,version:v1,status:200",
|
57
|
-
"grape.request.time:333|ms|#custom:tag,scheme:http,host:test.host,method:GET,path:/sub/versioned,version:v1,status:200",
|
58
|
-
])
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|