atatus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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