atatus 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +72 -22
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +2 -2
  6. data/lib/atatus.rb +76 -16
  7. data/lib/atatus/agent.rb +78 -29
  8. data/lib/atatus/central_config.rb +72 -27
  9. data/lib/atatus/central_config/cache_control.rb +18 -1
  10. data/lib/atatus/child_durations.rb +64 -0
  11. data/lib/atatus/collector/base.rb +61 -29
  12. data/lib/atatus/collector/builder.rb +46 -2
  13. data/lib/atatus/collector/hist.rb +54 -0
  14. data/lib/atatus/collector/transport.rb +41 -11
  15. data/lib/atatus/config.rb +129 -28
  16. data/lib/atatus/config/bytes.rb +17 -0
  17. data/lib/atatus/config/duration.rb +17 -0
  18. data/lib/atatus/config/options.rb +29 -9
  19. data/lib/atatus/config/regexp_list.rb +17 -0
  20. data/lib/atatus/config/wildcard_pattern_list.rb +64 -0
  21. data/lib/atatus/context.rb +32 -1
  22. data/lib/atatus/context/request.rb +17 -0
  23. data/lib/atatus/context/request/socket.rb +18 -1
  24. data/lib/atatus/context/request/url.rb +17 -0
  25. data/lib/atatus/context/response.rb +27 -2
  26. data/lib/atatus/context/user.rb +17 -0
  27. data/lib/atatus/context_builder.rb +19 -4
  28. data/lib/atatus/deprecations.rb +17 -0
  29. data/lib/atatus/error.rb +27 -0
  30. data/lib/atatus/error/exception.rb +24 -0
  31. data/lib/atatus/error/log.rb +17 -0
  32. data/lib/atatus/error_builder.rb +17 -2
  33. data/lib/atatus/grape.rb +62 -0
  34. data/lib/atatus/graphql.rb +91 -0
  35. data/lib/atatus/grpc.rb +99 -0
  36. data/lib/atatus/instrumenter.rb +135 -30
  37. data/lib/atatus/internal_error.rb +17 -0
  38. data/lib/atatus/logging.rb +17 -2
  39. data/lib/atatus/metadata.rb +17 -0
  40. data/lib/atatus/metadata/process_info.rb +17 -0
  41. data/lib/atatus/metadata/service_info.rb +21 -6
  42. data/lib/atatus/metadata/system_info.rb +22 -3
  43. data/lib/atatus/metadata/system_info/container_info.rb +49 -10
  44. data/lib/atatus/metadata/system_info/hw_info.rb +1 -1
  45. data/lib/atatus/metrics.rb +69 -27
  46. data/lib/atatus/metrics/breakdown_set.rb +31 -0
  47. data/lib/atatus/metrics/{cpu_mem.rb → cpu_mem_set.rb} +110 -63
  48. data/lib/atatus/metrics/metric.rb +140 -0
  49. data/lib/atatus/metrics/set.rb +123 -0
  50. data/lib/atatus/metrics/span_scoped_set.rb +56 -0
  51. data/lib/atatus/metrics/transaction_set.rb +26 -0
  52. data/lib/atatus/metrics/vm_set.rb +58 -0
  53. data/lib/atatus/metricset.rb +48 -4
  54. data/lib/atatus/middleware.rb +28 -8
  55. data/lib/atatus/naively_hashable.rb +17 -0
  56. data/lib/atatus/normalizers.rb +23 -9
  57. data/lib/atatus/normalizers/grape.rb +22 -0
  58. data/lib/atatus/normalizers/grape/endpoint_run.rb +65 -0
  59. data/lib/atatus/normalizers/rails.rb +27 -0
  60. data/lib/atatus/normalizers/rails/action_controller.rb +44 -0
  61. data/lib/atatus/normalizers/rails/action_mailer.rb +43 -0
  62. data/lib/atatus/normalizers/{action_view.rb → rails/action_view.rb} +17 -0
  63. data/lib/atatus/normalizers/rails/active_record.rb +80 -0
  64. data/lib/atatus/opentracing.rb +75 -42
  65. data/lib/atatus/rails.rb +29 -13
  66. data/lib/atatus/railtie.rb +19 -6
  67. data/lib/atatus/resque.rb +29 -0
  68. data/lib/atatus/sinatra.rb +53 -0
  69. data/lib/atatus/span.rb +44 -15
  70. data/lib/atatus/span/context.rb +43 -28
  71. data/lib/atatus/span/context/db.rb +43 -0
  72. data/lib/atatus/span/context/destination.rb +77 -0
  73. data/lib/atatus/span/context/http.rb +43 -0
  74. data/lib/atatus/span_helpers.rb +18 -1
  75. data/lib/atatus/spies.rb +33 -15
  76. data/lib/atatus/spies/action_dispatch.rb +27 -6
  77. data/lib/atatus/spies/delayed_job.rb +26 -5
  78. data/lib/atatus/spies/dynamo_db.rb +62 -0
  79. data/lib/atatus/spies/elasticsearch.rb +53 -7
  80. data/lib/atatus/spies/faraday.rb +54 -20
  81. data/lib/atatus/spies/http.rb +36 -6
  82. data/lib/atatus/spies/json.rb +18 -0
  83. data/lib/atatus/spies/mongo.rb +41 -10
  84. data/lib/atatus/spies/net_http.rb +52 -11
  85. data/lib/atatus/spies/rake.rb +42 -23
  86. data/lib/atatus/spies/redis.rb +17 -0
  87. data/lib/atatus/spies/resque.rb +57 -0
  88. data/lib/atatus/spies/sequel.rb +54 -17
  89. data/lib/atatus/spies/shoryuken.rb +69 -0
  90. data/lib/atatus/spies/sidekiq.rb +46 -25
  91. data/lib/atatus/spies/sinatra.rb +20 -4
  92. data/lib/atatus/spies/sneakers.rb +74 -0
  93. data/lib/atatus/spies/sucker_punch.rb +58 -0
  94. data/lib/atatus/spies/tilt.rb +20 -1
  95. data/lib/atatus/sql.rb +36 -0
  96. data/lib/atatus/sql/signature.rb +169 -0
  97. data/lib/atatus/sql/tokenizer.rb +264 -0
  98. data/lib/atatus/sql/tokens.rb +63 -0
  99. data/lib/atatus/sql_summarizer.rb +24 -6
  100. data/lib/atatus/stacktrace.rb +17 -0
  101. data/lib/atatus/stacktrace/frame.rb +17 -3
  102. data/lib/atatus/stacktrace_builder.rb +23 -3
  103. data/lib/atatus/subscriber.rb +23 -4
  104. data/lib/atatus/trace_context.rb +84 -51
  105. data/lib/atatus/trace_context/traceparent.rb +111 -0
  106. data/lib/atatus/trace_context/tracestate.rb +148 -0
  107. data/lib/atatus/transaction.rb +74 -18
  108. data/lib/atatus/transport/base.rb +44 -27
  109. data/lib/atatus/transport/connection.rb +28 -72
  110. data/lib/atatus/transport/connection/http.rb +58 -35
  111. data/lib/atatus/transport/connection/proxy_pipe.rb +24 -5
  112. data/lib/atatus/transport/filters.rb +18 -1
  113. data/lib/atatus/transport/filters/hash_sanitizer.rb +77 -0
  114. data/lib/atatus/transport/filters/secrets_filter.rb +30 -55
  115. data/lib/atatus/transport/headers.rb +83 -0
  116. data/lib/atatus/transport/serializers.rb +17 -5
  117. data/lib/atatus/transport/serializers/context_serializer.rb +30 -3
  118. data/lib/atatus/transport/serializers/error_serializer.rb +17 -2
  119. data/lib/atatus/transport/serializers/metadata_serializer.rb +44 -22
  120. data/lib/atatus/transport/serializers/metricset_serializer.rb +34 -6
  121. data/lib/atatus/transport/serializers/span_serializer.rb +47 -12
  122. data/lib/atatus/transport/serializers/transaction_serializer.rb +18 -2
  123. data/lib/atatus/transport/user_agent.rb +48 -0
  124. data/lib/atatus/transport/worker.rb +31 -7
  125. data/lib/atatus/util.rb +18 -1
  126. data/lib/atatus/util/inflector.rb +17 -0
  127. data/lib/atatus/util/lru_cache.rb +17 -0
  128. data/lib/atatus/util/throttle.rb +17 -0
  129. data/lib/atatus/version.rb +19 -1
  130. metadata +46 -26
  131. data/Rakefile +0 -19
  132. data/bench/.gitignore +0 -2
  133. data/bench/app.rb +0 -53
  134. data/bench/benchmark.rb +0 -36
  135. data/bench/report.rb +0 -55
  136. data/bench/rubyprof.rb +0 -39
  137. data/bench/stackprof.rb +0 -23
  138. data/bin/build_docs +0 -5
  139. data/bin/console +0 -15
  140. data/bin/setup +0 -8
  141. data/bin/with_framework +0 -7
  142. data/lib/atatus/metrics/vm.rb +0 -60
  143. data/lib/atatus/normalizers/action_controller.rb +0 -27
  144. data/lib/atatus/normalizers/action_mailer.rb +0 -26
  145. data/lib/atatus/normalizers/active_record.rb +0 -45
  146. data/lib/atatus/util/prefixed_logger.rb +0 -18
  147. data/vendor/.gitkeep +0 -0
@@ -1,3 +1,20 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  module Atatus
@@ -1,3 +1,20 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  require 'atatus/stacktrace'
@@ -18,5 +35,15 @@ module Atatus
18
35
  attr_accessor :id, :culprit, :exception, :log, :transaction_id,
19
36
  :transaction, :context, :parent_id, :trace_id
20
37
  attr_reader :timestamp
38
+
39
+ def inspect
40
+ "<Atatus::Error id:#{id}" \
41
+ " culprit:#{culprit}" \
42
+ " timestamp:#{timestamp}" \
43
+ " transaction_id:#{transaction_id}" \
44
+ " trace_id:#{trace_id}" \
45
+ " exception:#{exception.inspect}" \
46
+ '>'
47
+ end
21
48
  end
22
49
  end
@@ -1,3 +1,20 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  module Atatus
@@ -34,6 +51,13 @@ module Atatus
34
51
  :cause
35
52
  )
36
53
 
54
+ def inspect
55
+ '<Atatus::Error::Exception' \
56
+ " type:#{type}" \
57
+ " message:#{message}" \
58
+ '>'
59
+ end
60
+
37
61
  class << self
38
62
  private
39
63
 
@@ -1,3 +1,20 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  module Atatus
@@ -1,3 +1,20 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  module Atatus
@@ -53,7 +70,6 @@ module Atatus
53
70
  error.culprit = stacktrace.frames.first&.function
54
71
  end
55
72
 
56
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
57
73
  def add_current_transaction_fields(error, transaction)
58
74
  return unless transaction
59
75
 
@@ -71,6 +87,5 @@ module Atatus
71
87
  Util.reverse_merge!(error.context.labels, transaction.context.labels)
72
88
  Util.reverse_merge!(error.context.custom, transaction.context.custom)
73
89
  end
74
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
75
90
  end
76
91
  end
@@ -0,0 +1,62 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ require 'atatus/subscriber'
21
+ require 'atatus/normalizers/grape'
22
+
23
+ module Atatus
24
+ # Module for starting the Atatus agent and hooking into Grape.
25
+ module Grape
26
+ extend self
27
+ # Start the Atatus agent and hook into Grape.
28
+ #
29
+ # @param app [Grape::API] A Grape app.
30
+ # @param config [Config, Hash] An instance of Config or a Hash config.
31
+ # @return [true, nil] true if the agent was started, nil otherwise.
32
+ def start(app, config = {})
33
+ config = Config.new(config) unless config.is_a?(Config)
34
+ configure_app(app, config)
35
+
36
+ Atatus.start(config).tap do |agent|
37
+ attach_subscriber(agent)
38
+ end
39
+
40
+ Atatus.running?
41
+ rescue StandardError => e
42
+ config.logger.error format('Failed to start: %s', e.message)
43
+ config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
44
+ end
45
+
46
+ private
47
+
48
+ def configure_app(app, config)
49
+ config.service_name ||= app.name
50
+ config.framework_name ||= 'Grape'
51
+ config.framework_version ||= ::Grape::VERSION
52
+ config.logger ||= app.logger
53
+ config.__root_path ||= Dir.pwd
54
+ end
55
+
56
+ def attach_subscriber(agent)
57
+ return unless agent
58
+
59
+ agent.instrumenter.subscriber = Atatus::Subscriber.new(agent)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,91 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Atatus
21
+ # @api private
22
+ module GraphQL
23
+ TYPE = 'app'
24
+ SUBTYPE = 'graphql'
25
+
26
+ PREFIX = 'GraphQL: '
27
+ UNNAMED = '[unnamed]'
28
+ MULTIPLE_QUERIES = '[multiple-queries]'
29
+ CONCATENATOR = '+'
30
+ MAX_NUMBER_OF_QUERIES_FOR_NAME = 5
31
+
32
+ KEYS_TO_NAME = {
33
+ 'lex' => 'graphql.lex',
34
+ 'parse' => 'graphql.parse',
35
+ 'validate' => 'graphql.validate',
36
+ 'analyze_multiplex' => 'graphql.analyze_multiplex',
37
+ 'analyze_query' => 'graphql.analyze_query',
38
+ 'execute_multiplex' => 'graphql.execute_multiplex',
39
+ 'execute_query' => 'graphql.execute_query',
40
+ 'execute_query_lazy' => 'graphql.execute_query_lazy',
41
+ 'authorized_lazy' => 'graphql.authorized_lazy',
42
+ 'resolve_type' => 'graphql.resolve_type',
43
+ 'resolve_type_lazy' => 'graphql.resolve_type_lazy'
44
+
45
+ # These are available but deliberately left out as they are mostly noise.
46
+ # "execute_field" => "graphql.execute_field",
47
+ # "execute_field_lazy" => "graphql.execute_field_lazy",
48
+ # "authorized" => "graphql.authorized",
49
+ }.freeze
50
+
51
+ def self.trace(key, data)
52
+ return yield unless KEYS_TO_NAME.key?(key)
53
+ return yield unless (transaction = Atatus.current_transaction)
54
+
55
+ if key == 'execute_query'
56
+ transaction.name =
57
+ "#{PREFIX}#{query_name(data[:query])}"
58
+ end
59
+
60
+ results =
61
+ Atatus.with_span(
62
+ KEYS_TO_NAME.fetch(key, key),
63
+ TYPE, subtype: SUBTYPE, action: key
64
+ ) { yield }
65
+
66
+ if key == 'execute_multiplex'
67
+ transaction.name = "#{PREFIX}#{concat_names(results)}"
68
+ end
69
+
70
+ results
71
+ end
72
+
73
+ class << self
74
+ private
75
+
76
+ def query_name(query)
77
+ query.operation_name || UNNAMED
78
+ end
79
+
80
+ def concat_names(results)
81
+ if results.length > MAX_NUMBER_OF_QUERIES_FOR_NAME
82
+ return MULTIPLE_QUERIES
83
+ end
84
+
85
+ results.map do |result|
86
+ query_name(result.query)
87
+ end.join(CONCATENATOR)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,99 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module Atatus
21
+ # @api private
22
+ class GRPC
23
+ # @api private
24
+ class ClientInterceptor < ::GRPC::ClientInterceptor
25
+ TYPE = 'external'
26
+ SUBTYPE = 'grpc'
27
+
28
+ # rubocop:disable Lint/UnusedMethodArgument
29
+ def request_response(request:, call:, method:, metadata:)
30
+ return yield unless (transaction = Atatus.current_transaction)
31
+ if (trace_context = transaction.trace_context)
32
+ trace_context.apply_headers { |k, v| metadata[k.downcase] = v }
33
+ end
34
+
35
+ Atatus.with_span(
36
+ method, TYPE,
37
+ subtype: SUBTYPE,
38
+ context: span_context(call)
39
+ ) do
40
+ yield
41
+ end
42
+ end
43
+ # rubocop:enable Lint/UnusedMethodArgument
44
+
45
+ private
46
+
47
+ def span_context(call)
48
+ peer = call.instance_variable_get(:@wrapped)&.peer
49
+ return unless peer
50
+
51
+ split_peer = URI.split(peer)
52
+ destination = Atatus::Span::Context::Destination.new(
53
+ type: TYPE,
54
+ name: SUBTYPE,
55
+ resource: peer,
56
+ address: split_peer[0],
57
+ port: split_peer[6]
58
+ )
59
+ Atatus::Span::Context.new(destination: destination)
60
+ end
61
+ end
62
+
63
+ # @api private
64
+ class ServerInterceptor < ::GRPC::ClientInterceptor
65
+ TYPE = 'request'
66
+
67
+ # rubocop:disable Lint/UnusedMethodArgument
68
+ def request_response(request:, call:, method:)
69
+ transaction = start_transaction(call)
70
+ yield
71
+ transaction.done 'success'
72
+ rescue ::Exception => e
73
+ Atatus.report(e, handled: false)
74
+ transaction.done 'error' if transaction
75
+ raise
76
+ ensure
77
+ Atatus.end_transaction
78
+ end
79
+ # rubocop:enable Lint/UnusedMethodArgument
80
+
81
+ private
82
+
83
+ def start_transaction(call)
84
+ Atatus.start_transaction(
85
+ 'grpc',
86
+ 'request',
87
+ trace_context: trace_context(call)
88
+ )
89
+ end
90
+
91
+ def trace_context(call)
92
+ TraceContext.parse(metadata: call.metadata)
93
+ rescue TraceContext::InvalidTraceparentHeader
94
+ warn "Couldn't parse invalid trace context header: #{call.metadata}"
95
+ nil
96
+ end
97
+ end
98
+ end
99
+ end
@@ -1,12 +1,29 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  require 'atatus/trace_context'
21
+ require 'atatus/child_durations'
4
22
  require 'atatus/span'
5
23
  require 'atatus/transaction'
6
24
  require 'atatus/span_helpers'
7
25
 
8
26
  module Atatus
9
- # rubocop:disable Metrics/ClassLength
10
27
  # @api private
11
28
  class Instrumenter
12
29
  TRANSACTION_KEY = :__atatus_instrumenter_transaction_key
@@ -39,18 +56,22 @@ module Atatus
39
56
  end
40
57
  end
41
58
 
42
- def initialize(config, stacktrace_builder:, &enqueue)
59
+ def initialize(config, metrics:, stacktrace_builder:, &enqueue)
43
60
  @config = config
44
61
  @stacktrace_builder = stacktrace_builder
45
62
  @enqueue = enqueue
63
+ @metrics = metrics
46
64
 
47
65
  @current = Current.new
48
66
  end
49
67
 
50
- attr_reader :config, :stacktrace_builder, :enqueue
68
+ attr_reader :stacktrace_builder, :enqueue
51
69
 
52
70
  def start
53
71
  debug 'Starting instrumenter'
72
+ # We call register! on @subscriber in case the
73
+ # instrumenter was stopped and started again
74
+ @subscriber&.register!
54
75
  end
55
76
 
56
77
  def stop
@@ -59,7 +80,12 @@ module Atatus
59
80
  self.current_transaction = nil
60
81
  current_spans.pop until current_spans.empty?
61
82
 
62
- @subscriber.unregister! if @subscriber
83
+ @subscriber&.unregister!
84
+ end
85
+
86
+ def handle_forking!
87
+ stop
88
+ start
63
89
  end
64
90
 
65
91
  def subscriber=(subscriber)
@@ -78,10 +104,10 @@ module Atatus
78
104
  @current.transaction = transaction
79
105
  end
80
106
 
81
- # rubocop:disable Metrics/MethodLength
82
107
  def start_transaction(
83
108
  name = nil,
84
109
  type = nil,
110
+ config:,
85
111
  context: nil,
86
112
  trace_context: nil
87
113
  )
@@ -89,10 +115,17 @@ module Atatus
89
115
 
90
116
  if (transaction = current_transaction)
91
117
  raise ExistingTransactionError,
92
- "Transactions may not be nested.\nAlready inside #{transaction}"
118
+ "Transactions may not be nested.\n" \
119
+ "Already inside #{transaction.inspect}"
93
120
  end
94
121
 
95
- sampled = trace_context ? trace_context.recorded? : random_sample?
122
+ if trace_context
123
+ samled = trace_context.recorded?
124
+ sample_rate = trace_context.tracestate.sample_rate
125
+ else
126
+ sampled = random_sample?(config)
127
+ sample_rate = config.transaction_sample_rate
128
+ end
96
129
 
97
130
  transaction =
98
131
  Transaction.new(
@@ -101,14 +134,14 @@ module Atatus
101
134
  context: context,
102
135
  trace_context: trace_context,
103
136
  sampled: sampled,
104
- labels: config.default_labels
137
+ sample_rate: sample_rate,
138
+ config: config
105
139
  )
106
140
 
107
141
  transaction.start
108
142
 
109
143
  self.current_transaction = transaction
110
144
  end
111
- # rubocop:enable Metrics/MethodLength
112
145
 
113
146
  def end_transaction(result = nil)
114
147
  return nil unless (transaction = current_transaction)
@@ -119,6 +152,8 @@ module Atatus
119
152
 
120
153
  enqueue.call transaction
121
154
 
155
+ update_transaction_metrics(transaction)
156
+
122
157
  transaction
123
158
  end
124
159
 
@@ -132,8 +167,8 @@ module Atatus
132
167
  current_spans.last
133
168
  end
134
169
 
135
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
136
- # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
170
+ # rubocop:disable Metrics/CyclomaticComplexity
171
+ # rubocop:disable Metrics/PerceivedComplexity
137
172
  # rubocop:disable Metrics/ParameterLists
138
173
  def start_span(
139
174
  name,
@@ -142,32 +177,40 @@ module Atatus
142
177
  action: nil,
143
178
  backtrace: nil,
144
179
  context: nil,
145
- trace_context: nil
180
+ trace_context: nil,
181
+ parent: nil,
182
+ sync: nil
146
183
  )
147
- return unless (transaction = current_transaction)
148
- return unless transaction.sampled?
149
-
150
- transaction.inc_started_spans!
151
184
 
152
- if transaction.max_spans_reached?(config)
153
- transaction.inc_dropped_spans!
154
- return
155
- end
185
+ transaction =
186
+ case parent
187
+ when Span
188
+ parent.transaction
189
+ when Transaction
190
+ parent
191
+ else
192
+ current_transaction
193
+ end
194
+ return unless transaction
195
+ return unless transaction.sampled?
196
+ return unless transaction.inc_started_spans!
156
197
 
157
- parent = current_span || transaction
198
+ parent ||= (current_span || current_transaction)
158
199
 
159
200
  span = Span.new(
160
201
  name: name,
161
202
  subtype: subtype,
162
203
  action: action,
163
- transaction_id: transaction.id,
164
- trace_context: trace_context || parent.trace_context.child,
204
+ transaction: transaction,
205
+ parent: parent,
206
+ trace_context: trace_context,
165
207
  type: type,
166
208
  context: context,
167
- stacktrace_builder: stacktrace_builder
209
+ stacktrace_builder: stacktrace_builder,
210
+ sync: sync
168
211
  )
169
212
 
170
- if backtrace && config.span_frames_min_duration?
213
+ if backtrace && transaction.span_frames_min_duration
171
214
  span.original_backtrace = backtrace
172
215
  end
173
216
 
@@ -176,8 +219,8 @@ module Atatus
176
219
  span.start
177
220
  end
178
221
  # rubocop:enable Metrics/ParameterLists
179
- # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
180
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
222
+ # rubocop:enable Metrics/CyclomaticComplexity
223
+ # rubocop:enable Metrics/PerceivedComplexity
181
224
 
182
225
  def end_span
183
226
  return unless (span = current_spans.pop)
@@ -186,6 +229,8 @@ module Atatus
186
229
 
187
230
  enqueue.call span
188
231
 
232
+ update_span_metrics(span)
233
+
189
234
  span
190
235
  end
191
236
 
@@ -205,7 +250,7 @@ module Atatus
205
250
 
206
251
  def set_user(user)
207
252
  return unless current_transaction
208
- current_transaction.context.user = Context::User.infer(config, user)
253
+ current_transaction.set_user(user)
209
254
  end
210
255
 
211
256
  def inspect
@@ -216,9 +261,69 @@ module Atatus
216
261
 
217
262
  private
218
263
 
219
- def random_sample?
264
+ def random_sample?(config)
220
265
  rand <= config.transaction_sample_rate
221
266
  end
267
+
268
+ def update_transaction_metrics(transaction)
269
+ return unless transaction.collect_metrics?
270
+
271
+ tags = {
272
+ 'transaction.name': transaction.name,
273
+ 'transaction.type': transaction.type
274
+ }
275
+
276
+ @metrics.get(:transaction).timer(
277
+ :'transaction.duration.sum.us',
278
+ tags: tags, reset_on_collect: true
279
+ ).update(transaction.duration)
280
+
281
+ @metrics.get(:transaction).counter(
282
+ :'transaction.duration.count',
283
+ tags: tags, reset_on_collect: true
284
+ ).inc!
285
+
286
+ return unless transaction.sampled?
287
+ return unless transaction.breakdown_metrics
288
+
289
+ @metrics.get(:breakdown).counter(
290
+ :'transaction.breakdown.count',
291
+ tags: tags, reset_on_collect: true
292
+ ).inc!
293
+
294
+ span_tags = tags.merge('span.type': 'app')
295
+
296
+ @metrics.get(:breakdown).timer(
297
+ :'span.self_time.sum.us',
298
+ tags: span_tags, reset_on_collect: true
299
+ ).update(transaction.self_time)
300
+
301
+ @metrics.get(:breakdown).counter(
302
+ :'span.self_time.count',
303
+ tags: span_tags, reset_on_collect: true
304
+ ).inc!
305
+ end
306
+
307
+ def update_span_metrics(span)
308
+ return unless span.transaction.collect_metrics?
309
+
310
+ tags = {
311
+ 'span.type': span.type,
312
+ 'transaction.name': span.transaction.name,
313
+ 'transaction.type': span.transaction.type
314
+ }
315
+
316
+ tags[:'span.subtype'] = span.subtype if span.subtype
317
+
318
+ @metrics.get(:breakdown).timer(
319
+ :'span.self_time.sum.us',
320
+ tags: tags, reset_on_collect: true
321
+ ).update(span.self_time)
322
+
323
+ @metrics.get(:breakdown).counter(
324
+ :'span.self_time.count',
325
+ tags: tags, reset_on_collect: true
326
+ ).inc!
327
+ end
222
328
  end
223
- # rubocop:enable Metrics/ClassLength
224
329
  end