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 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