grape-datadog 1.2.0 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b14dbd829e8c193af488bfcde4a5bbd625a5ecbf
4
- data.tar.gz: c3055618f84b9970c4fc308bb363db61225691da
3
+ metadata.gz: a406efffcb252cbd474b6397fdc301c522e93aaf
4
+ data.tar.gz: 2632cc04b6453f1302393af0a38b57fc45762d54
5
5
  SHA512:
6
- metadata.gz: bd8d20ca60f27da9cc0ee74d60410dbc4e79033ce7471a8be31e6ff71f79d9f0ddacfdb4d74a946bd02c109b2a4f555c7cc6c25cdb40b9fefe28acd4a2ab7fb0
7
- data.tar.gz: 3be84c6ea24b6c259cd872a9b2fa3b1c3ee2996acd789f89971deff5b2fba0a55f8968b5dc776eee67935fde047edd296acd741b4214540e4e36a7a25a526804
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 stats reporter for [Grape][0], based on code from [this Librato
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
- Include it in your Grape API like this:
16
+ Configure it in an initializer:
18
17
 
19
- class TestAPI < Grape::API
20
- use Grape::Datadog::Middleware
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][4].
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
@@ -1,3 +1,87 @@
1
- %w|version middleware|.each do |name|
2
- require "grape/datadog/#{name}"
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
@@ -1,5 +1,5 @@
1
1
  module Grape
2
- module Datadog
3
- VERSION = "1.2.0"
2
+ class Datadog
3
+ VERSION = "1.3.2"
4
4
  end
5
5
  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
@@ -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
- $statsd = MockStatsd.new(nil, nil, {}, 10000)
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.0
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-01 00:00:00.000000000 Z
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