atatus 1.0.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.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +57 -0
  5. data/LICENSE +65 -0
  6. data/LICENSE-THIRD-PARTY +205 -0
  7. data/README.md +13 -0
  8. data/Rakefile +19 -0
  9. data/atatus.gemspec +36 -0
  10. data/atatus.yml +2 -0
  11. data/bench/.gitignore +2 -0
  12. data/bench/app.rb +53 -0
  13. data/bench/benchmark.rb +36 -0
  14. data/bench/report.rb +55 -0
  15. data/bench/rubyprof.rb +39 -0
  16. data/bench/stackprof.rb +23 -0
  17. data/bin/build_docs +5 -0
  18. data/bin/console +15 -0
  19. data/bin/setup +8 -0
  20. data/bin/with_framework +7 -0
  21. data/lib/atatus.rb +325 -0
  22. data/lib/atatus/agent.rb +260 -0
  23. data/lib/atatus/central_config.rb +141 -0
  24. data/lib/atatus/central_config/cache_control.rb +34 -0
  25. data/lib/atatus/collector/base.rb +329 -0
  26. data/lib/atatus/collector/builder.rb +317 -0
  27. data/lib/atatus/collector/transport.rb +72 -0
  28. data/lib/atatus/config.rb +248 -0
  29. data/lib/atatus/config/bytes.rb +25 -0
  30. data/lib/atatus/config/duration.rb +23 -0
  31. data/lib/atatus/config/options.rb +134 -0
  32. data/lib/atatus/config/regexp_list.rb +13 -0
  33. data/lib/atatus/context.rb +33 -0
  34. data/lib/atatus/context/request.rb +11 -0
  35. data/lib/atatus/context/request/socket.rb +19 -0
  36. data/lib/atatus/context/request/url.rb +42 -0
  37. data/lib/atatus/context/response.rb +22 -0
  38. data/lib/atatus/context/user.rb +42 -0
  39. data/lib/atatus/context_builder.rb +97 -0
  40. data/lib/atatus/deprecations.rb +22 -0
  41. data/lib/atatus/error.rb +22 -0
  42. data/lib/atatus/error/exception.rb +46 -0
  43. data/lib/atatus/error/log.rb +24 -0
  44. data/lib/atatus/error_builder.rb +76 -0
  45. data/lib/atatus/instrumenter.rb +224 -0
  46. data/lib/atatus/internal_error.rb +6 -0
  47. data/lib/atatus/logging.rb +55 -0
  48. data/lib/atatus/metadata.rb +19 -0
  49. data/lib/atatus/metadata/process_info.rb +18 -0
  50. data/lib/atatus/metadata/service_info.rb +61 -0
  51. data/lib/atatus/metadata/system_info.rb +35 -0
  52. data/lib/atatus/metadata/system_info/container_info.rb +121 -0
  53. data/lib/atatus/metadata/system_info/hw_info.rb +118 -0
  54. data/lib/atatus/metadata/system_info/os_info.rb +31 -0
  55. data/lib/atatus/metrics.rb +98 -0
  56. data/lib/atatus/metrics/cpu_mem.rb +240 -0
  57. data/lib/atatus/metrics/vm.rb +60 -0
  58. data/lib/atatus/metricset.rb +19 -0
  59. data/lib/atatus/middleware.rb +76 -0
  60. data/lib/atatus/naively_hashable.rb +21 -0
  61. data/lib/atatus/normalizers.rb +68 -0
  62. data/lib/atatus/normalizers/action_controller.rb +27 -0
  63. data/lib/atatus/normalizers/action_mailer.rb +26 -0
  64. data/lib/atatus/normalizers/action_view.rb +77 -0
  65. data/lib/atatus/normalizers/active_record.rb +45 -0
  66. data/lib/atatus/opentracing.rb +346 -0
  67. data/lib/atatus/rails.rb +61 -0
  68. data/lib/atatus/railtie.rb +30 -0
  69. data/lib/atatus/span.rb +125 -0
  70. data/lib/atatus/span/context.rb +40 -0
  71. data/lib/atatus/span_helpers.rb +44 -0
  72. data/lib/atatus/spies.rb +86 -0
  73. data/lib/atatus/spies/action_dispatch.rb +28 -0
  74. data/lib/atatus/spies/delayed_job.rb +68 -0
  75. data/lib/atatus/spies/elasticsearch.rb +36 -0
  76. data/lib/atatus/spies/faraday.rb +70 -0
  77. data/lib/atatus/spies/http.rb +44 -0
  78. data/lib/atatus/spies/json.rb +22 -0
  79. data/lib/atatus/spies/mongo.rb +87 -0
  80. data/lib/atatus/spies/net_http.rb +70 -0
  81. data/lib/atatus/spies/rake.rb +45 -0
  82. data/lib/atatus/spies/redis.rb +27 -0
  83. data/lib/atatus/spies/sequel.rb +47 -0
  84. data/lib/atatus/spies/sidekiq.rb +89 -0
  85. data/lib/atatus/spies/sinatra.rb +41 -0
  86. data/lib/atatus/spies/tilt.rb +27 -0
  87. data/lib/atatus/sql_summarizer.rb +35 -0
  88. data/lib/atatus/stacktrace.rb +16 -0
  89. data/lib/atatus/stacktrace/frame.rb +52 -0
  90. data/lib/atatus/stacktrace_builder.rb +104 -0
  91. data/lib/atatus/subscriber.rb +77 -0
  92. data/lib/atatus/trace_context.rb +85 -0
  93. data/lib/atatus/transaction.rb +100 -0
  94. data/lib/atatus/transport/base.rb +174 -0
  95. data/lib/atatus/transport/connection.rb +156 -0
  96. data/lib/atatus/transport/connection/http.rb +116 -0
  97. data/lib/atatus/transport/connection/proxy_pipe.rb +75 -0
  98. data/lib/atatus/transport/filters.rb +43 -0
  99. data/lib/atatus/transport/filters/secrets_filter.rb +74 -0
  100. data/lib/atatus/transport/serializers.rb +93 -0
  101. data/lib/atatus/transport/serializers/context_serializer.rb +85 -0
  102. data/lib/atatus/transport/serializers/error_serializer.rb +77 -0
  103. data/lib/atatus/transport/serializers/metadata_serializer.rb +70 -0
  104. data/lib/atatus/transport/serializers/metricset_serializer.rb +28 -0
  105. data/lib/atatus/transport/serializers/span_serializer.rb +80 -0
  106. data/lib/atatus/transport/serializers/transaction_serializer.rb +37 -0
  107. data/lib/atatus/transport/worker.rb +73 -0
  108. data/lib/atatus/util.rb +42 -0
  109. data/lib/atatus/util/inflector.rb +93 -0
  110. data/lib/atatus/util/lru_cache.rb +48 -0
  111. data/lib/atatus/util/prefixed_logger.rb +18 -0
  112. data/lib/atatus/util/throttle.rb +35 -0
  113. data/lib/atatus/version.rb +5 -0
  114. data/vendor/.gitkeep +0 -0
  115. metadata +190 -0
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class ElasticsearchSpy
8
+ NAME_FORMAT = '%s %s'
9
+ TYPE = 'db.elasticsearch'
10
+
11
+ # rubocop:disable Metrics/MethodLength
12
+ def install
13
+ ::Elasticsearch::Transport::Client.class_eval do
14
+ alias perform_request_without_apm perform_request
15
+
16
+ def perform_request(method, path, *args, &block)
17
+ name = format(NAME_FORMAT, method, path)
18
+ statement = args[0].is_a?(String) ? args[0] : args[0].to_json
19
+ context = Span::Context.new(db: { statement: statement })
20
+
21
+ Atatus.with_span name, TYPE, context: context do
22
+ perform_request_without_apm(method, path, *args, &block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ # rubocop:enable Metrics/MethodLength
28
+ end
29
+
30
+ register(
31
+ 'Elasticsearch::Transport::Client',
32
+ 'elasticsearch-transport',
33
+ ElasticsearchSpy.new
34
+ )
35
+ end
36
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class FaradaySpy
8
+ TYPE = 'ext'
9
+ SUBTYPE = 'faraday'
10
+
11
+ def self.without_net_http
12
+ return yield unless defined?(NetHTTPSpy)
13
+
14
+ Atatus::Spies::NetHTTPSpy.disable_in do
15
+ yield
16
+ end
17
+ end
18
+
19
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
20
+ # rubocop:disable Metrics/BlockLength, Metrics/PerceivedComplexity
21
+ # rubocop:disable Metrics/CyclomaticComplexity
22
+ def install
23
+ ::Faraday::Connection.class_eval do
24
+ alias run_request_without_apm run_request
25
+
26
+ def run_request(method, url, body, headers, &block)
27
+ unless (transaction = Atatus.current_transaction)
28
+ return run_request_without_apm(method, url, body, headers, &block)
29
+ end
30
+
31
+ host = if url_prefix.is_a?(URI) && url_prefix.host
32
+ url_prefix.host
33
+ elsif url.nil?
34
+ tmp_request = build_request(method) do |req|
35
+ yield(req) if block_given?
36
+ end
37
+ URI(tmp_request.path).host
38
+ else
39
+ URI(url).host
40
+ end
41
+
42
+ name = "#{method.upcase} #{host}"
43
+
44
+ Atatus.with_span(
45
+ name,
46
+ TYPE,
47
+ subtype: SUBTYPE,
48
+ action: method.to_s
49
+ ) do |span|
50
+ Atatus::Spies::FaradaySpy.without_net_http do
51
+ trace_context = span&.trace_context || transaction.trace_context
52
+
53
+ run_request_without_apm(method, url, body, headers) do |req|
54
+ req['Atatus-Apm-Traceparent'] = trace_context.to_header
55
+
56
+ yield req if block_given?
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ # rubocop:enable Metrics/CyclomaticComplexity
64
+ # rubocop:enable Metrics/BlockLength, Metrics/PerceivedComplexity
65
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
66
+ end
67
+
68
+ register 'Faraday', 'faraday', FaradaySpy.new
69
+ end
70
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class HTTPSpy
8
+ TYPE = 'ext'
9
+ SUBTYPE = 'http_rb'
10
+
11
+ # rubocop:disable Metrics/MethodLength
12
+ def install
13
+ ::HTTP::Client.class_eval do
14
+ alias perform_without_apm perform
15
+
16
+ def perform(req, options)
17
+ unless (transaction = Atatus.current_transaction)
18
+ return perform_without_apm(req, options)
19
+ end
20
+
21
+ method = req.verb.to_s.upcase
22
+ host = req.uri.host
23
+
24
+ name = "#{method} #{host}"
25
+
26
+ Atatus.with_span(
27
+ name,
28
+ TYPE,
29
+ subtype: SUBTYPE,
30
+ action: method
31
+ ) do |span|
32
+ trace_context = span&.trace_context || transaction.trace_context
33
+ req['Atatus-Apm-Traceparent'] = trace_context.to_header
34
+ perform_without_apm(req, options)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ # rubocop:enable Metrics/MethodLength
40
+ end
41
+
42
+ register 'HTTP', 'http', HTTPSpy.new
43
+ end
44
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'atatus/span_helpers'
4
+
5
+ module Atatus
6
+ # @api private
7
+ module Spies
8
+ # @api private
9
+ class JSONSpy
10
+ def install
11
+ ::JSON.class_eval do
12
+ include SpanHelpers
13
+ span_class_method :parse, 'JSON#parse', 'json.parse'
14
+ span_class_method :parse!, 'JSON#parse!', 'json.parse'
15
+ span_class_method :generate, 'JSON#generate', 'json.generate'
16
+ end
17
+ end
18
+ end
19
+
20
+ register 'JSON', 'json', JSONSpy.new
21
+ end
22
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class MongoSpy
8
+ def install
9
+ ::Mongo::Monitoring::Global.subscribe(
10
+ ::Mongo::Monitoring::COMMAND,
11
+ Subscriber.new
12
+ )
13
+ end
14
+
15
+ # @api private
16
+ class Subscriber
17
+ TYPE = 'db'
18
+ SUBTYPE = 'mongodb'
19
+ ACTION = 'query'
20
+
21
+ def initialize
22
+ @events = {}
23
+ end
24
+
25
+ def started(event)
26
+ push_event(event)
27
+ end
28
+
29
+ def failed(event)
30
+ pop_event(event)
31
+ end
32
+
33
+ def succeeded(event)
34
+ pop_event(event)
35
+ end
36
+
37
+ private
38
+
39
+ # rubocop:disable Metrics/MethodLength
40
+ def push_event(event)
41
+ return unless Atatus.current_transaction
42
+ # Some MongoDB commands are not on collections but rather are db
43
+ # admin commands. For these commands, the value at the `command_name`
44
+ # key is the integer 1.
45
+ unless event.command[event.command_name] == 1
46
+ collection = event.command[event.command_name]
47
+ end
48
+ name = [event.database_name,
49
+ collection,
50
+ event.command_name].compact.join('.')
51
+
52
+ span =
53
+ Atatus.start_span(
54
+ name,
55
+ TYPE,
56
+ subtype: SUBTYPE,
57
+ action: ACTION,
58
+ context: build_context(event)
59
+ )
60
+
61
+ @events[event.operation_id] = span
62
+ end
63
+ # rubocop:enable Metrics/MethodLength
64
+
65
+ def pop_event(event)
66
+ return unless (curr = Atatus.current_span)
67
+ span = @events.delete(event.operation_id)
68
+
69
+ curr == span && Atatus.end_span
70
+ end
71
+
72
+ def build_context(event)
73
+ Span::Context.new(
74
+ db: {
75
+ instance: event.database_name,
76
+ statement: event.command.to_s,
77
+ type: 'mongodb',
78
+ user: nil
79
+ }
80
+ )
81
+ end
82
+ end
83
+ end
84
+
85
+ register 'Mongo', 'mongo', MongoSpy.new
86
+ end
87
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class NetHTTPSpy
8
+ KEY = :__atatus_net_http_disabled
9
+ TYPE = 'ext'
10
+ SUBTYPE = 'net_http'
11
+
12
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
13
+ class << self
14
+ def disabled=(disabled)
15
+ Thread.current[KEY] = disabled
16
+ end
17
+
18
+ def disabled?
19
+ Thread.current[KEY] ||= false
20
+ end
21
+
22
+ def disable_in
23
+ self.disabled = true
24
+
25
+ begin
26
+ yield
27
+ ensure
28
+ self.disabled = false
29
+ end
30
+ end
31
+ end
32
+
33
+ def install
34
+ Net::HTTP.class_eval do
35
+ alias request_without_apm request
36
+
37
+ def request(req, body = nil, &block)
38
+ unless (transaction = Atatus.current_transaction)
39
+ return request_without_apm(req, body, &block)
40
+ end
41
+ if Atatus::Spies::NetHTTPSpy.disabled?
42
+ return request_without_apm(req, body, &block)
43
+ end
44
+
45
+ host, = req['host'] && req['host'].split(':')
46
+ method = req.method
47
+
48
+ host ||= address
49
+
50
+ name = "#{method} #{host}"
51
+
52
+ Atatus.with_span(
53
+ name,
54
+ TYPE,
55
+ subtype: SUBTYPE,
56
+ action: method.to_s
57
+ ) do |span|
58
+ trace_context = span&.trace_context || transaction.trace_context
59
+ req['Atatus-Apm-Traceparent'] = trace_context.to_header
60
+ request_without_apm(req, body, &block)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
66
+ end
67
+
68
+ register 'Net::HTTP', 'net/http', NetHTTPSpy.new
69
+ end
70
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class RakeSpy
8
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
9
+ def install
10
+ ::Rake::Task.class_eval do
11
+ alias execute_without_apm execute
12
+
13
+ def execute(*args)
14
+ agent = Atatus.start
15
+
16
+ unless agent && agent.config.instrumented_rake_tasks.include?(name)
17
+ return execute_without_apm(*args)
18
+ end
19
+
20
+ transaction =
21
+ Atatus.start_transaction("Rake::Task[#{name}]", 'Rake')
22
+
23
+ begin
24
+ result = execute_without_apm(*args)
25
+
26
+ transaction.result = 'success' if transaction
27
+ rescue StandardError => e
28
+ transaction.result = 'error' if transaction
29
+ Atatus.report(e)
30
+
31
+ raise
32
+ ensure
33
+ Atatus.end_transaction
34
+ Atatus.stop
35
+ end
36
+
37
+ result
38
+ end
39
+ end
40
+ end
41
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
42
+ end
43
+ register 'Rake::Task', 'rake', RakeSpy.new
44
+ end
45
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Atatus
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class RedisSpy
8
+ def install
9
+ ::Redis::Client.class_eval do
10
+ alias call_without_apm call
11
+
12
+ def call(command, &block)
13
+ name = command[0].upcase
14
+
15
+ return call_without_apm(command, &block) if command[0] == :auth
16
+
17
+ Atatus.with_span(name.to_s, 'db.redis') do
18
+ call_without_apm(command, &block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ register 'Redis', 'redis', RedisSpy.new
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'atatus/sql_summarizer'
4
+
5
+ module Atatus
6
+ # @api private
7
+ module Spies
8
+ # @api private
9
+ class SequelSpy
10
+ TYPE = 'db.sequel.sql'
11
+
12
+ def self.summarizer
13
+ @summarizer ||= SqlSummarizer.new
14
+ end
15
+
16
+ def self.build_context(sql, opts)
17
+ Span::Context.new(
18
+ db: { statement: sql, type: 'sql', user: opts[:user] }
19
+ )
20
+ end
21
+
22
+ # rubocop:disable Metrics/MethodLength
23
+ def install
24
+ require 'sequel/database/logging'
25
+
26
+ ::Sequel::Database.class_eval do
27
+ alias log_connection_yield_without_apm log_connection_yield
28
+
29
+ def log_connection_yield(sql, *args, &block)
30
+ unless Atatus.current_transaction
31
+ return log_connection_yield_without_apm(sql, *args, &block)
32
+ end
33
+
34
+ summarizer = Atatus::Spies::SequelSpy.summarizer
35
+ name = summarizer.summarize sql
36
+ context = Atatus::Spies::SequelSpy.build_context(sql, opts)
37
+
38
+ Atatus.with_span(name, TYPE, context: context, &block)
39
+ end
40
+ end
41
+ end
42
+ # rubocop:enable Metrics/MethodLength
43
+ end
44
+
45
+ register 'Sequel', 'sequel', SequelSpy.new
46
+ end
47
+ end