atatus 1.3.0 → 1.4.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 (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