ddtrace 0.21.2 → 0.22.0

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: 61acb994baa0c01a709084beac147a5c6052e92d
4
- data.tar.gz: bd4846e16ff33ddd1e6b8a88b080ed79c0958190
3
+ metadata.gz: ca30d1de4944896da5082d6e5479437c7ec76ae8
4
+ data.tar.gz: 1a9d2c6922699e01f6364039d5ff39530c7fb6fd
5
5
  SHA512:
6
- metadata.gz: a55674e8f2d82acd24ee34458af85d3555948854439cb84ab85d3f4c496235e28d14809aa10925fecc9af91e484d7b996f6d6de684351c22d4ef4f1be2e3ed5d
7
- data.tar.gz: 90a143ebed788d5c90902866d126a40d2f5ba8f640eb98b66a2efa79e1eee1571c974d62aefd38192024c7836324de972a89b0bf90136125ef0e8c0ab6f6e358
6
+ metadata.gz: '01378c87cbd74c47525e7b5eed4fa161305b3f7c1a0da54e6c105074806b5c32af84b27d69e7251ba39cc5d7040f4335f3eb878e30bd3a2f6cf08d317f75c4e8'
7
+ data.tar.gz: e08463b5ad5f4a67af403558bcb5a51fe9a7a4451804037ac5db6203280cb263dde1e02d78c9fc4973e12a07aa44f6334b6b79f337bc34c7fc5d8eb0118b0182
data/.env CHANGED
@@ -1,4 +1,5 @@
1
1
  DD_AGENT_HOST=localhost
2
+ DD_METRIC_AGENT_PORT=8125
2
3
  DD_TRACE_AGENT_PORT=8126
3
4
  TEST_DDAGENT_API_KEY=invalid_key_but_this_is_fine
4
5
  TEST_ELASTICSEARCH_HOST=127.0.0.1
data/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@
4
4
 
5
5
  ## [Unreleased (beta)]
6
6
 
7
+ ## [0.22.0] - 2019-04-15
8
+
9
+ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.22.0
10
+
11
+ Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.21.2...v0.22.0
12
+
13
+ In this release we are adding initial support for the **beta** [Runtime metrics collection](https://docs.datadoghq.com/tracing/advanced/runtime_metrics/?tab=ruby) feature.
14
+
15
+ ### Changed
16
+
17
+ - Add warning log if an integration is incompatible (#722) (@ericmustin)
18
+
19
+ ### Added
20
+
21
+ - Initial beta support for Runtime metrics collection (#677)
22
+
7
23
  ## [0.21.2] - 2019-04-10
8
24
 
9
25
  Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.21.2
@@ -751,8 +767,9 @@ Release notes: https://github.com/DataDog/dd-trace-rb/releases/tag/v0.3.1
751
767
 
752
768
  Git diff: https://github.com/DataDog/dd-trace-rb/compare/v0.3.0...v0.3.1
753
769
 
754
- [Unreleased (stable)]: https://github.com/DataDog/dd-trace-rb/compare/v0.21.2...master
755
- [Unreleased (beta)]: https://github.com/DataDog/dd-trace-rb/compare/v0.21.2...0.22-dev
770
+ [Unreleased (stable)]: https://github.com/DataDog/dd-trace-rb/compare/v0.22.0...master
771
+ [Unreleased (beta)]: https://github.com/DataDog/dd-trace-rb/compare/v0.22.0...0.23-dev
772
+ [0.22.0]: https://github.com/DataDog/dd-trace-rb/compare/v0.21.2...v0.22.0
756
773
  [0.21.2]: https://github.com/DataDog/dd-trace-rb/compare/v0.21.1...v0.21.2
757
774
  [0.21.1]: https://github.com/DataDog/dd-trace-rb/compare/v0.21.0...v0.21.1
758
775
  [0.21.0]: https://github.com/DataDog/dd-trace-rb/compare/v0.20.0...v0.21.0
data/Rakefile CHANGED
@@ -15,39 +15,47 @@ namespace :spec do
15
15
  :rails, :railsredis, :railssidekiq, :railsactivejob,
16
16
  :elasticsearch, :http, :redis, :sidekiq, :sinatra]
17
17
 
18
- RSpec::Core::RakeTask.new(:main) do |t|
18
+ RSpec::Core::RakeTask.new(:main) do |t, args|
19
19
  t.pattern = 'spec/**/*_spec.rb'
20
20
  t.exclude_pattern = 'spec/**/{contrib,benchmark,redis,opentracer}/**/*_spec.rb'
21
+ t.rspec_opts = args.to_a.join(' ')
21
22
  end
22
23
 
23
- RSpec::Core::RakeTask.new(:opentracer) do |t|
24
+ RSpec::Core::RakeTask.new(:opentracer) do |t, args|
24
25
  t.pattern = 'spec/ddtrace/opentracer/**/*_spec.rb'
26
+ t.rspec_opts = args.to_a.join(' ')
25
27
  end
26
28
 
27
- RSpec::Core::RakeTask.new(:rails) do |t|
29
+ RSpec::Core::RakeTask.new(:rails) do |t, args|
28
30
  t.pattern = 'spec/ddtrace/contrib/rails/**/*_spec.rb'
29
31
  t.exclude_pattern = 'spec/ddtrace/contrib/rails/**/*{sidekiq,active_job,disable_env}*_spec.rb'
32
+ t.rspec_opts = args.to_a.join(' ')
30
33
  end
31
34
 
32
- RSpec::Core::RakeTask.new(:railsredis) do |t|
35
+ RSpec::Core::RakeTask.new(:railsredis) do |t, args|
33
36
  t.pattern = 'spec/ddtrace/contrib/rails/**/*redis*_spec.rb'
37
+ t.rspec_opts = args.to_a.join(' ')
34
38
  end
35
39
 
36
- RSpec::Core::RakeTask.new(:railssidekiq) do |t|
40
+ RSpec::Core::RakeTask.new(:railssidekiq) do |t, args|
37
41
  t.pattern = 'spec/ddtrace/contrib/rails/**/*sidekiq*_spec.rb'
42
+ t.rspec_opts = args.to_a.join(' ')
38
43
  end
39
44
 
40
- RSpec::Core::RakeTask.new(:railsactivejob) do |t|
45
+ RSpec::Core::RakeTask.new(:railsactivejob) do |t, args|
41
46
  t.pattern = 'spec/ddtrace/contrib/rails/**/*active_job*_spec.rb'
47
+ t.rspec_opts = args.to_a.join(' ')
42
48
  end
43
49
 
44
- RSpec::Core::RakeTask.new(:railsdisableenv) do |t|
50
+ RSpec::Core::RakeTask.new(:railsdisableenv) do |t, args|
45
51
  t.pattern = 'spec/ddtrace/contrib/rails/**/*disable_env*_spec.rb'
52
+ t.rspec_opts = args.to_a.join(' ')
46
53
  end
47
54
 
48
- RSpec::Core::RakeTask.new(:contrib) do |t|
55
+ RSpec::Core::RakeTask.new(:contrib) do |t, args|
49
56
  # rubocop:disable Metrics/LineLength
50
57
  t.pattern = 'spec/**/contrib/{analytics,configurable,integration,patchable,patcher,registerable,registry,configuration/*}_spec.rb'
58
+ t.rspec_opts = args.to_a.join(' ')
51
59
  end
52
60
 
53
61
  [
@@ -79,8 +87,9 @@ namespace :spec do
79
87
  :sucker_punch,
80
88
  :shoryuken
81
89
  ].each do |contrib|
82
- RSpec::Core::RakeTask.new(contrib) do |t|
90
+ RSpec::Core::RakeTask.new(contrib) do |t, args|
83
91
  t.pattern = "spec/ddtrace/contrib/#{contrib}/**/*_spec.rb"
92
+ t.rspec_opts = args.to_a.join(' ')
84
93
  end
85
94
  end
86
95
  end
data/ddtrace.gemspec CHANGED
@@ -33,10 +33,13 @@ Gem::Specification.new do |spec|
33
33
  spec.require_paths = ['lib']
34
34
 
35
35
  spec.add_dependency 'msgpack'
36
+
37
+ # Optional extensions
36
38
  # TODO: Move this to Appraisals?
37
- spec.add_dependency 'opentracing', '>= 0.4.1'
39
+ spec.add_development_dependency 'dogstatsd-ruby', '>= 3.3.0'
40
+ spec.add_development_dependency 'opentracing', '>= 0.4.1'
38
41
 
39
- spec.add_development_dependency 'climate_control', '~> 0.2.0'
42
+ # Development dependencies
40
43
  spec.add_development_dependency 'rake', '>= 10.5'
41
44
  spec.add_development_dependency 'rubocop', '= 0.49.1' if RUBY_VERSION >= '2.1.0'
42
45
  spec.add_development_dependency 'rspec', '~> 3.0'
@@ -48,6 +51,7 @@ Gem::Specification.new do |spec|
48
51
  spec.add_development_dependency 'builder'
49
52
  spec.add_development_dependency 'ruby-prof'
50
53
  spec.add_development_dependency 'sqlite3', '~> 1.3.6'
54
+ spec.add_development_dependency 'climate_control', '~> 0.2.0'
51
55
 
52
56
  # locking transitive dependency of webmock
53
57
  spec.add_development_dependency 'addressable', '~> 2.4.0'
data/docker-compose.yml CHANGED
@@ -175,8 +175,10 @@ services:
175
175
  - DD_BIND_HOST=0.0.0.0
176
176
  - DD_API_KEY=invalid_key_but_this_is_fine
177
177
  expose:
178
+ - "8125/udp"
178
179
  - "8126"
179
180
  ports:
181
+ - "${DD_METRIC_AGENT_PORT}:8125/udp"
180
182
  - "${DD_TRACE_AGENT_PORT}:8126"
181
183
  elasticsearch:
182
184
  # Note: ES 5.0 dies with error:
@@ -63,6 +63,8 @@ For descriptions of terminology used in APM, take a look at the [official docume
63
63
  - [Filtering](#filtering)
64
64
  - [Processing](#processing)
65
65
  - [Trace correlation](#trace-correlation)
66
+ - [Metrics](#metrics)
67
+ - [For application runtime](#for-application-runtime)
66
68
  - [OpenTracing](#opentracing)
67
69
 
68
70
  ## Compatibility
@@ -1725,6 +1727,56 @@ Datadog.tracer.trace('my.operation') { logger.warn('This is a traced operation.'
1725
1727
  # [2019-01-16 18:38:41 +0000][my_app][WARN][dd.trace_id=8545847825299552251 dd.span_id=3711755234730770098] This is a traced operation.
1726
1728
  ```
1727
1729
 
1730
+ ### Metrics
1731
+
1732
+ The tracer and its integrations can produce some additional metrics that can provide useful insight into the performance of your application. These metrics are collected with `dogstatsd-ruby`, and can be sent to the same Datadog agent to which you send your traces.
1733
+
1734
+ To configure your application for metrics collection:
1735
+
1736
+ 1. [Configure your Datadog agent for StatsD](https://docs.datadoghq.com/developers/dogstatsd/#setup)
1737
+ 2. Add `gem 'dogstatsd-ruby'` to your Gemfile
1738
+
1739
+ #### For application runtime
1740
+
1741
+ If runtime metrics are configured, the trace library will automatically collect and send metrics about the health of your application.
1742
+
1743
+ To configure runtime metrics, add the following configuration:
1744
+
1745
+ ```ruby
1746
+ # config/initializers/datadog.rb
1747
+ require 'datadog/statsd'
1748
+ require 'ddtrace'
1749
+
1750
+ Datadog.configure do |c|
1751
+ # To enable runtime metrics collection, set `true`. Defaults to `false`
1752
+ # You can also set DD_RUNTIME_METRICS_ENABLED=true to configure this.
1753
+ c.runtime_metrics_enabled = true
1754
+
1755
+ # Optionally, you can configure the Statsd instance used for sending runtime metrics.
1756
+ # Statsd is automatically configured with default settings if `dogstatsd-ruby` is available.
1757
+ # You can configure with host and port of Datadog agent; defaults to 'localhost:8125'.
1758
+ c.runtime_metrics statsd: Datadog::Statsd.new
1759
+ end
1760
+ ```
1761
+
1762
+ See the [Dogstatsd documentation](https://www.rubydoc.info/github/DataDog/dogstatsd-ruby/master/frames) for more details about configuring `Datadog::Statsd`.
1763
+
1764
+ The stats sent will include:
1765
+
1766
+ | Name | Type | Description |
1767
+ | -------------------------- | ------- | -------------------------------------------------------- |
1768
+ | `runtime.ruby.class_count` | `gauge` | Number of classes in memory space. |
1769
+ | `runtime.ruby.thread_count` | `gauge` | Number of threads. |
1770
+ | `runtime.ruby.gc.*`. | `gauge` | Garbage collection statistics (one per value in GC.stat) |
1771
+
1772
+ In addition, all metrics will include the following tags:
1773
+
1774
+ | Name | Description |
1775
+ | ------------ | ------------------------------------------------------- |
1776
+ | `language` | Programming language traced. (e.g. `ruby`) |
1777
+ | `runtime-id` | Unique identifier of runtime environment (i.e. process) |
1778
+ | `service` | List of services this metric is associated with. |
1779
+
1728
1780
  ### OpenTracing
1729
1781
 
1730
1782
  For setting up Datadog with OpenTracing, see out [Quickstart for OpenTracing](#quickstart-for-opentracing) section for details.
data/lib/ddtrace.rb CHANGED
@@ -9,6 +9,7 @@ require 'ddtrace/pipeline'
9
9
  require 'ddtrace/configuration'
10
10
  require 'ddtrace/patcher'
11
11
  require 'ddtrace/augmentation'
12
+ require 'ddtrace/metrics'
12
13
 
13
14
  # \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
14
15
  module Datadog
@@ -22,5 +22,9 @@ module Datadog
22
22
  def tracer
23
23
  configuration.tracer
24
24
  end
25
+
26
+ def runtime_metrics
27
+ tracer.writer.runtime_metrics
28
+ end
25
29
  end
26
30
  end
@@ -1,7 +1,11 @@
1
1
  require 'ddtrace/ext/analytics'
2
- require 'ddtrace/environment'
2
+ require 'ddtrace/ext/runtime'
3
3
  require 'ddtrace/configuration/options'
4
4
 
5
+ require 'ddtrace/environment'
6
+ require 'ddtrace/tracer'
7
+ require 'ddtrace/metrics'
8
+
5
9
  module Datadog
6
10
  module Configuration
7
11
  # Global configuration settings for the trace library.
@@ -13,6 +17,10 @@ module Datadog
13
17
  default: -> { env_to_bool(Ext::Analytics::ENV_TRACE_ANALYTICS_ENABLED, nil) },
14
18
  lazy: true
15
19
 
20
+ option :runtime_metrics_enabled,
21
+ default: -> { env_to_bool(Ext::Runtime::Metrics::ENV_ENABLED, false) },
22
+ lazy: true
23
+
16
24
  option :tracer, default: Tracer.new
17
25
 
18
26
  def initialize(options = {})
@@ -28,7 +36,15 @@ module Datadog
28
36
  yield(self) if block_given?
29
37
  end
30
38
 
39
+ def runtime_metrics(options = nil)
40
+ runtime_metrics = get_option(:tracer).writer.runtime_metrics
41
+ return runtime_metrics if options.nil?
42
+
43
+ runtime_metrics.configure(options)
44
+ end
45
+
31
46
  # Backwards compatibility for configuring tracer e.g. `c.tracer debug: true`
47
+ remove_method :tracer
32
48
  def tracer(options = nil)
33
49
  tracer = options && options.key?(:instance) ? set_option(:tracer, options[:instance]) : get_option(:tracer)
34
50
 
@@ -200,14 +200,14 @@ module Datadog
200
200
  end
201
201
 
202
202
  def attach_sampling_priority
203
- @trace.first.set_metric(
203
+ @current_root_span.set_metric(
204
204
  Ext::DistributedTracing::SAMPLING_PRIORITY_KEY,
205
205
  @sampling_priority
206
206
  )
207
207
  end
208
208
 
209
209
  def attach_origin
210
- @trace.first.set_tag(
210
+ @current_root_span.set_tag(
211
211
  Ext::DistributedTracing::ORIGIN_KEY,
212
212
  @origin
213
213
  )
@@ -29,7 +29,11 @@ module Datadog
29
29
  end
30
30
 
31
31
  def patch
32
- return if !self.class.compatible? || patcher.nil?
32
+ if !self.class.compatible? || patcher.nil?
33
+ Datadog::Tracer.log.warn("Unable to patch #{self.class.name}")
34
+ return
35
+ end
36
+
33
37
  patcher.patch
34
38
  end
35
39
  end
@@ -124,6 +124,7 @@ module Datadog
124
124
  end
125
125
  end
126
126
 
127
+ # rubocop:disable Metrics/AbcSize
127
128
  def set_request_tags!(request_span, env, status, headers, response, original_env)
128
129
  # http://www.rubydoc.info/github/rack/rack/file/SPEC
129
130
  # The source of truth in Rack is the PATH_INFO key that holds the
@@ -142,6 +143,9 @@ module Datadog
142
143
 
143
144
  request_span.resource ||= resource_name_for(env, status)
144
145
 
146
+ # Associate with runtime metrics
147
+ Datadog.runtime_metrics.associate_with_span(request_span)
148
+
145
149
  # Set analytics sample rate
146
150
  if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
147
151
  Contrib::Analytics.set_sample_rate(request_span, configuration[:analytics_sample_rate])
@@ -53,6 +53,9 @@ module Datadog
53
53
  # Set analytics sample rate
54
54
  Utils.set_analytics_sample_rate(span)
55
55
 
56
+ # Associate with runtime metrics
57
+ Datadog.runtime_metrics.associate_with_span(span)
58
+
56
59
  span.set_tag(Ext::TAG_ROUTE_ACTION, payload.fetch(:action))
57
60
  span.set_tag(Ext::TAG_ROUTE_CONTROLLER, payload.fetch(:controller))
58
61
 
@@ -0,0 +1,11 @@
1
+ module Datadog
2
+ module Ext
3
+ module Metrics
4
+ TAG_LANG = 'language'.freeze
5
+ TAG_LANG_INTERPRETER = 'language-interpreter'.freeze
6
+ TAG_LANG_VERSION = 'language-version'.freeze
7
+ TAG_RUNTIME_ID = 'runtime-id'.freeze
8
+ TAG_TRACER_VERSION = 'tracer-version'.freeze
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ require 'ddtrace/version'
2
+
3
+ module Datadog
4
+ module Ext
5
+ module Runtime
6
+ # Identity
7
+ LANG = 'ruby'.freeze
8
+ LANG_INTERPRETER = begin
9
+ if Gem::Version.new(RUBY_VERSION) > Gem::Version.new('1.9')
10
+ (RUBY_ENGINE + '-' + RUBY_PLATFORM)
11
+ else
12
+ ('ruby-' + RUBY_PLATFORM)
13
+ end
14
+ end.freeze
15
+ LANG_VERSION = RUBY_VERSION
16
+ TRACER_VERSION = Datadog::VERSION::STRING
17
+
18
+ TAG_LANG = 'language'.freeze
19
+ TAG_RUNTIME_ID = 'runtime-id'.freeze
20
+
21
+ # Metrics
22
+ module Metrics
23
+ ENV_ENABLED = 'DD_RUNTIME_METRICS_ENABLED'.freeze
24
+
25
+ METRIC_CLASS_COUNT = 'runtime.ruby.class_count'.freeze
26
+ METRIC_GC_PREFIX = 'runtime.ruby.gc'.freeze
27
+ METRIC_THREAD_COUNT = 'runtime.ruby.thread_count'.freeze
28
+
29
+ TAG_SERVICE = 'service'.freeze
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,131 @@
1
+ require 'ddtrace/ext/metrics'
2
+
3
+ require 'set'
4
+ require 'ddtrace/utils/time'
5
+ require 'ddtrace/runtime/identity'
6
+
7
+ module Datadog
8
+ # Acts as client for sending metrics (via Statsd)
9
+ # Wraps a Statsd client with default tags and additional configuration.
10
+ class Metrics
11
+ DEFAULT_AGENT_HOST = '127.0.0.1'.freeze
12
+ DEFAULT_METRIC_AGENT_PORT = '8125'.freeze
13
+
14
+ attr_reader :statsd
15
+
16
+ def initialize(options = {})
17
+ @statsd = options.fetch(:statsd) { default_statsd_client if supported? }
18
+ @enabled = options.fetch(:enabled, true)
19
+ end
20
+
21
+ def supported?
22
+ Gem.loaded_specs['dogstatsd-ruby'] \
23
+ && Gem.loaded_specs['dogstatsd-ruby'].version >= Gem::Version.new('3.3.0')
24
+ end
25
+
26
+ def enabled?
27
+ @enabled
28
+ end
29
+
30
+ def enabled=(enabled)
31
+ @enabled = (enabled == true)
32
+ end
33
+
34
+ def default_statsd_client
35
+ require 'datadog/statsd' unless defined?(::Datadog::Statsd)
36
+
37
+ # Create a StatsD client that points to the agent.
38
+ Datadog::Statsd.new(
39
+ ENV.fetch('DD_AGENT_HOST', DEFAULT_AGENT_HOST),
40
+ ENV.fetch('DD_METRIC_AGENT_PORT', DEFAULT_METRIC_AGENT_PORT)
41
+ )
42
+ end
43
+
44
+ def configure(options = {})
45
+ @statsd = options[:statsd] if options.key?(:statsd)
46
+ @enabled = options[:enabled] if options.key?(:enabled)
47
+ end
48
+
49
+ def send_stats?
50
+ enabled? && !statsd.nil?
51
+ end
52
+
53
+ def distribution(stat, value, options = nil)
54
+ return unless send_stats? && statsd.respond_to?(:distribution)
55
+ statsd.distribution(stat, value, metric_options(options))
56
+ rescue StandardError => e
57
+ Datadog::Tracer.log.error("Failed to send distribution stat. Cause: #{e.message} Source: #{e.backtrace.first}")
58
+ end
59
+
60
+ def increment(stat, options = nil)
61
+ return unless send_stats? && statsd.respond_to?(:increment)
62
+ statsd.increment(stat, metric_options(options))
63
+ rescue StandardError => e
64
+ Datadog::Tracer.log.error("Failed to send increment stat. Cause: #{e.message} Source: #{e.backtrace.first}")
65
+ end
66
+
67
+ def gauge(stat, value, options = nil)
68
+ return unless send_stats? && statsd.respond_to?(:gauge)
69
+ statsd.gauge(stat, value, metric_options(options))
70
+ rescue StandardError => e
71
+ Datadog::Tracer.log.error("Failed to send gauge stat. Cause: #{e.message} Source: #{e.backtrace.first}")
72
+ end
73
+
74
+ def time(stat, options = nil)
75
+ return yield unless send_stats?
76
+
77
+ # Calculate time, send it as a distribution.
78
+ start = Utils::Time.get_time
79
+ return yield
80
+ ensure
81
+ begin
82
+ if send_stats? && !start.nil?
83
+ finished = Utils::Time.get_time
84
+ distribution(stat, ((finished - start) * 1000), options)
85
+ end
86
+ rescue StandardError => e
87
+ Datadog::Tracer.log.error("Failed to send time stat. Cause: #{e.message} Source: #{e.backtrace.first}")
88
+ end
89
+ end
90
+
91
+ # For defining and adding default options to metrics
92
+ module Options
93
+ DEFAULT = {
94
+ tags: DEFAULT_TAGS = [
95
+ "#{Ext::Metrics::TAG_LANG}:#{Runtime::Identity.lang}".freeze
96
+ # "#{Ext::Metrics::TAG_LANG_INTERPRETER}:#{Runtime::Identity.lang_interpreter}".freeze,
97
+ # "#{Ext::Metrics::TAG_LANG_VERSION}:#{Runtime::Identity.lang_version}".freeze,
98
+ # "#{Ext::Metrics::TAG_TRACER_VERSION}:#{Runtime::Identity.tracer_version}".freeze
99
+ ].freeze
100
+ }.freeze
101
+
102
+ def metric_options(options = nil)
103
+ return default_metric_options if options.nil?
104
+
105
+ default_metric_options.merge(options) do |key, old_value, new_value|
106
+ case key
107
+ when :tags
108
+ old_value.dup.concat(new_value).uniq
109
+ else
110
+ new_value
111
+ end
112
+ end
113
+ end
114
+
115
+ def default_metric_options
116
+ # Return dupes, so that the constant isn't modified,
117
+ # and defaults are unfrozen for mutation in Statsd.
118
+ DEFAULT.dup.tap do |options|
119
+ options[:tags] = options[:tags].dup
120
+
121
+ # Add runtime ID dynamically because it might change during fork.
122
+ options[:tags] << "#{Ext::Metrics::TAG_RUNTIME_ID}:#{Runtime::Identity.id}".freeze
123
+ end
124
+ end
125
+ end
126
+
127
+ # Make available on for both class and instance.
128
+ include Options
129
+ extend Options
130
+ end
131
+ end
@@ -0,0 +1,17 @@
1
+ module Datadog
2
+ module Runtime
3
+ # Retrieves number of classes from runtime
4
+ module ClassCount
5
+ module_function
6
+
7
+ def value
8
+ ObjectSpace.count_objects[:T_CLASS]
9
+ end
10
+
11
+ def available?
12
+ ObjectSpace.respond_to?(:count_objects) \
13
+ && ObjectSpace.count_objects.key?(:T_CLASS)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Datadog
2
+ module Runtime
3
+ # Retrieves garbage collection statistics
4
+ module GC
5
+ module_function
6
+
7
+ def stat
8
+ ::GC.stat
9
+ end
10
+
11
+ def available?
12
+ defined?(::GC) && ::GC.respond_to?(:stat)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,41 @@
1
+ require 'securerandom'
2
+ require 'ddtrace/ext/runtime'
3
+
4
+ module Datadog
5
+ module Runtime
6
+ # For runtime identity
7
+ module Identity
8
+ module_function
9
+
10
+ # Retrieves number of classes from runtime
11
+ def id
12
+ @pid ||= Process.pid
13
+ @id ||= SecureRandom.uuid
14
+
15
+ # Check if runtime has changed, e.g. forked.
16
+ if Process.pid != @pid
17
+ @pid = Process.pid
18
+ @id = SecureRandom.uuid
19
+ end
20
+
21
+ @id
22
+ end
23
+
24
+ def lang
25
+ Ext::Runtime::LANG
26
+ end
27
+
28
+ def lang_interpreter
29
+ Ext::Runtime::LANG_INTERPRETER
30
+ end
31
+
32
+ def lang_version
33
+ Ext::Runtime::LANG_VERSION
34
+ end
35
+
36
+ def tracer_version
37
+ Ext::Runtime::TRACER_VERSION
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,94 @@
1
+ require 'ddtrace/ext/runtime'
2
+
3
+ require 'ddtrace/metrics'
4
+ require 'ddtrace/runtime/class_count'
5
+ require 'ddtrace/runtime/gc'
6
+ require 'ddtrace/runtime/identity'
7
+ require 'ddtrace/runtime/thread_count'
8
+
9
+ module Datadog
10
+ module Runtime
11
+ # For generating runtime metrics
12
+ class Metrics < Datadog::Metrics
13
+ def initialize(options = {})
14
+ super
15
+
16
+ # Initialize service list
17
+ @services = Set.new
18
+ @service_tags = nil
19
+ end
20
+
21
+ def associate_with_span(span)
22
+ return if span.nil?
23
+
24
+ # Register service as associated with metrics
25
+ register_service(span.service) unless span.service.nil?
26
+
27
+ # Tag span with language and runtime ID for association with metrics
28
+ span.set_tag(Ext::Runtime::TAG_LANG, Runtime::Identity.lang)
29
+ span.set_tag(Ext::Runtime::TAG_RUNTIME_ID, Runtime::Identity.id)
30
+ end
31
+
32
+ # Associate service with runtime metrics
33
+ def register_service(service)
34
+ return if service.nil?
35
+
36
+ service = service.to_s
37
+
38
+ unless @services.include?(service)
39
+ # Add service to list and update services tag
40
+ services << service
41
+
42
+ # Recompile the service tags
43
+ compile_service_tags!
44
+ end
45
+ end
46
+
47
+ # Flush all runtime metrics to Statsd client
48
+ def flush
49
+ return unless enabled?
50
+
51
+ try_flush { gauge(Ext::Runtime::Metrics::METRIC_CLASS_COUNT, ClassCount.value) if ClassCount.available? }
52
+ try_flush { gauge(Ext::Runtime::Metrics::METRIC_THREAD_COUNT, ThreadCount.value) if ThreadCount.available? }
53
+ try_flush { gc_metrics.each { |metric, value| gauge(metric, value) } if GC.available? }
54
+ end
55
+
56
+ def gc_metrics
57
+ Hash[
58
+ GC.stat.map do |k, v|
59
+ ["#{Ext::Runtime::Metrics::METRIC_GC_PREFIX}.#{k}", v]
60
+ end
61
+ ]
62
+ end
63
+
64
+ def try_flush
65
+ yield
66
+ rescue StandardError => e
67
+ Datadog::Tracer.log.error("Error while sending runtime metric. Cause: #{e.message}")
68
+ end
69
+
70
+ def default_metric_options
71
+ # Return dupes, so that the constant isn't modified,
72
+ # and defaults are unfrozen for mutation in Statsd.
73
+ super.tap do |options|
74
+ options[:tags] = options[:tags].dup
75
+
76
+ # Add services dynamically because they might change during runtime.
77
+ options[:tags].concat(service_tags) unless service_tags.nil?
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader \
84
+ :service_tags,
85
+ :services
86
+
87
+ def compile_service_tags!
88
+ @service_tags = services.to_a.collect do |service|
89
+ "#{Ext::Runtime::Metrics::TAG_SERVICE}:#{service}".freeze
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,16 @@
1
+ module Datadog
2
+ module Runtime
3
+ # Retrieves number of threads from runtime
4
+ module ThreadCount
5
+ module_function
6
+
7
+ def value
8
+ Thread.list.count
9
+ end
10
+
11
+ def available?
12
+ Thread.respond_to?(:list)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ module Datadog
2
+ module Utils
3
+ # Common database-related utility functions.
4
+ module Time
5
+ PROCESS_TIME_SUPPORTED = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.1.0')
6
+
7
+ module_function
8
+
9
+ def get_time
10
+ PROCESS_TIME_SUPPORTED ? Process.clock_gettime(Process::CLOCK_MONOTONIC) : ::Time.now.to_f
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,8 +1,8 @@
1
1
  module Datadog
2
2
  module VERSION
3
3
  MAJOR = 0
4
- MINOR = 21
5
- PATCH = 2
4
+ MINOR = 22
5
+ PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
@@ -1,6 +1,7 @@
1
1
  require 'time'
2
2
 
3
3
  require 'ddtrace/buffer'
4
+ require 'ddtrace/runtime/metrics'
4
5
 
5
6
  module Datadog
6
7
  module Workers
@@ -14,19 +15,31 @@ module Datadog
14
15
  BACK_OFF_MAX = 5
15
16
  SHUTDOWN_TIMEOUT = 1
16
17
 
17
- attr_reader :trace_buffer, :service_buffer
18
+ attr_reader \
19
+ :service_buffer,
20
+ :trace_buffer
18
21
 
19
- def initialize(transport, buff_size, trace_task, service_task, interval)
20
- @trace_task = trace_task
21
- @service_task = service_task
22
+ def initialize(options = {})
23
+ @transport = options[:transport]
24
+
25
+ # Callbacks
26
+ @trace_task = options[:on_trace]
27
+ @service_task = options[:on_service]
28
+ @runtime_metrics_task = options[:on_runtime_metrics]
29
+
30
+ # Intervals
31
+ interval = options.fetch(:interval, 1)
22
32
  @flush_interval = interval
23
33
  @back_off = interval
24
- @trace_buffer = TraceBuffer.new(buff_size)
25
- @service_buffer = TraceBuffer.new(buff_size)
26
- @transport = transport
34
+
35
+ # Buffers
36
+ buffer_size = options.fetch(:buffer_size, 100)
37
+ @trace_buffer = TraceBuffer.new(buffer_size)
38
+ @service_buffer = TraceBuffer.new(buffer_size)
39
+
40
+ # Threading
27
41
  @shutdown = ConditionVariable.new
28
42
  @mutex = Mutex.new
29
-
30
43
  @worker = nil
31
44
  @run = false
32
45
  end
@@ -36,9 +49,9 @@ module Datadog
36
49
  return true if @trace_buffer.empty?
37
50
 
38
51
  begin
39
- traces = @trace_buffer.pop()
52
+ traces = @trace_buffer.pop
40
53
  traces = Pipeline.process!(traces)
41
- @trace_task.call(traces, @transport)
54
+ @trace_task.call(traces, @transport) unless @trace_task.nil?
42
55
  rescue StandardError => e
43
56
  # ensures that the thread will not die because of an exception.
44
57
  # TODO[manu]: findout the reason and reschedule the send if it's not
@@ -53,7 +66,7 @@ module Datadog
53
66
 
54
67
  begin
55
68
  services = @service_buffer.pop()
56
- @service_task.call(services[0], @transport)
69
+ @service_task.call(services[0], @transport) unless @service_task.nil?
57
70
  rescue StandardError => e
58
71
  # ensures that the thread will not die because of an exception.
59
72
  # TODO[manu]: findout the reason and reschedule the send if it's not
@@ -62,6 +75,12 @@ module Datadog
62
75
  end
63
76
  end
64
77
 
78
+ def callback_runtime_metrics
79
+ @runtime_metrics_task.call unless @runtime_metrics_task.nil?
80
+ rescue StandardError => e
81
+ Datadog::Tracer.log.error("Error during runtime metrics flush. Cause: #{e}")
82
+ end
83
+
65
84
  # Start the timer execution.
66
85
  def start
67
86
  @mutex.synchronize do
@@ -113,6 +132,7 @@ module Datadog
113
132
  @back_off = flush_data ? @flush_interval : [@back_off * BACK_OFF_RATIO, BACK_OFF_MAX].min
114
133
 
115
134
  callback_services
135
+ callback_runtime_metrics
116
136
 
117
137
  @mutex.synchronize do
118
138
  return if !@run && @trace_buffer.empty? && @service_buffer.empty?
@@ -3,15 +3,20 @@ require 'ddtrace/encoding'
3
3
  require 'ddtrace/workers'
4
4
 
5
5
  module Datadog
6
- # Traces and services writer that periodically sends data to the trace-agent
6
+ # Processor that sends traces and metadata to the agent
7
7
  class Writer
8
- attr_reader :transport, :worker, :priority_sampler
8
+ attr_reader \
9
+ :priority_sampler,
10
+ :runtime_metrics,
11
+ :transport,
12
+ :worker
9
13
 
10
14
  def initialize(options = {})
11
15
  # writer and transport parameters
12
16
  @buff_size = options.fetch(:buffer_size, 100)
13
17
  @flush_interval = options.fetch(:flush_interval, 1)
14
18
  transport_options = options.fetch(:transport_options, {})
19
+
15
20
  # priority sampling
16
21
  if options[:priority_sampler]
17
22
  @priority_sampler = options[:priority_sampler]
@@ -24,6 +29,11 @@ module Datadog
24
29
  HTTPTransport.new(transport_options)
25
30
  end
26
31
 
32
+ # Runtime metrics
33
+ @runtime_metrics = options.fetch(:runtime_metrics) do
34
+ Runtime::Metrics.new
35
+ end
36
+
27
37
  @services = {}
28
38
 
29
39
  # handles the thread creation after an eventual fork
@@ -43,11 +53,15 @@ module Datadog
43
53
  @pid = Process.pid
44
54
  @trace_handler = ->(items, transport) { send_spans(items, transport) }
45
55
  @service_handler = ->(items, transport) { send_services(items, transport) }
46
- @worker = Datadog::Workers::AsyncTransport.new(@transport,
47
- @buff_size,
48
- @trace_handler,
49
- @service_handler,
50
- @flush_interval)
56
+ @runtime_metrics_handler = -> { send_runtime_metrics }
57
+ @worker = Datadog::Workers::AsyncTransport.new(
58
+ transport: @transport,
59
+ buffer_size: @buff_size,
60
+ on_trace: @trace_handler,
61
+ on_service: @service_handler,
62
+ on_runtime_metrics: @runtime_metrics_handler,
63
+ interval: @flush_interval
64
+ )
51
65
 
52
66
  @worker.start()
53
67
  end
@@ -80,6 +94,12 @@ module Datadog
80
94
  status
81
95
  end
82
96
 
97
+ def send_runtime_metrics
98
+ return unless Datadog.configuration.runtime_metrics_enabled
99
+
100
+ runtime_metrics.flush
101
+ end
102
+
83
103
  # enqueue the trace for submission to the API
84
104
  def write(trace, services)
85
105
  # In multiprocess environments, the main process initializes the +Writer+ instance and if
@@ -98,6 +118,9 @@ module Datadog
98
118
  end
99
119
  end
100
120
 
121
+ # Associate root span with runtime metrics
122
+ runtime_metrics.associate_with_span(trace.first) unless trace.empty?
123
+
101
124
  @worker.enqueue_trace(trace)
102
125
  @worker.enqueue_service(services)
103
126
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddtrace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.2
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-10 00:00:00.000000000 Z
11
+ date: 2019-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack
@@ -25,33 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: opentracing
28
+ name: dogstatsd-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.4.1
34
- type: :runtime
33
+ version: 3.3.0
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.4.1
40
+ version: 3.3.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: climate_control
42
+ name: opentracing
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.2.0
47
+ version: 0.4.1
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.2.0
54
+ version: 0.4.1
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - "~>"
207
207
  - !ruby/object:Gem::Version
208
208
  version: 1.3.6
209
+ - !ruby/object:Gem::Dependency
210
+ name: climate_control
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: 0.2.0
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: 0.2.0
209
223
  - !ruby/object:Gem::Dependency
210
224
  name: addressable
211
225
  requirement: !ruby/object:Gem::Requirement
@@ -510,10 +524,13 @@ files:
510
524
  - lib/ddtrace/ext/distributed.rb
511
525
  - lib/ddtrace/ext/errors.rb
512
526
  - lib/ddtrace/ext/http.rb
527
+ - lib/ddtrace/ext/metrics.rb
513
528
  - lib/ddtrace/ext/net.rb
514
529
  - lib/ddtrace/ext/priority.rb
530
+ - lib/ddtrace/ext/runtime.rb
515
531
  - lib/ddtrace/ext/sql.rb
516
532
  - lib/ddtrace/logger.rb
533
+ - lib/ddtrace/metrics.rb
517
534
  - lib/ddtrace/monkey.rb
518
535
  - lib/ddtrace/opentracer.rb
519
536
  - lib/ddtrace/opentracer/binary_propagator.rb
@@ -542,6 +559,11 @@ files:
542
559
  - lib/ddtrace/provider.rb
543
560
  - lib/ddtrace/quantization/hash.rb
544
561
  - lib/ddtrace/quantization/http.rb
562
+ - lib/ddtrace/runtime/class_count.rb
563
+ - lib/ddtrace/runtime/gc.rb
564
+ - lib/ddtrace/runtime/identity.rb
565
+ - lib/ddtrace/runtime/metrics.rb
566
+ - lib/ddtrace/runtime/thread_count.rb
545
567
  - lib/ddtrace/sampler.rb
546
568
  - lib/ddtrace/span.rb
547
569
  - lib/ddtrace/sync_writer.rb
@@ -549,6 +571,7 @@ files:
549
571
  - lib/ddtrace/transport.rb
550
572
  - lib/ddtrace/utils.rb
551
573
  - lib/ddtrace/utils/database.rb
574
+ - lib/ddtrace/utils/time.rb
552
575
  - lib/ddtrace/vendor/active_record/connection_specification.rb
553
576
  - lib/ddtrace/version.rb
554
577
  - lib/ddtrace/workers.rb
@@ -575,7 +598,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
575
598
  version: '0'
576
599
  requirements: []
577
600
  rubyforge_project:
578
- rubygems_version: 2.6.14
601
+ rubygems_version: 2.5.2.3
579
602
  signing_key:
580
603
  specification_version: 4
581
604
  summary: Datadog tracing code for your Ruby applications