ddtrace 0.21.2 → 0.22.0

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