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
@@ -0,0 +1,63 @@
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
+ module Sql
22
+ # @api private
23
+ module Tokens
24
+ OTHER = :OTHER
25
+ COMMENT = :COMMENT
26
+ IDENT = :IDENT
27
+ NUMBER = :NUMBER
28
+ STRING = :STRING
29
+
30
+ PERIOD = :PERIOD
31
+ COMMA = :COMMA
32
+ LPAREN = :LPAREN
33
+ RPAREN = :RPAREN
34
+
35
+ AS = :AS
36
+ CALL = :CALL
37
+ DELETE = :DELETE
38
+ FROM = :FROM
39
+ INSERT = :INSERT
40
+ INTO = :INTO
41
+ OR = :OR
42
+ REPLACE = :REPLACE
43
+ SELECT = :SELECT
44
+ SET = :SET
45
+ TABLE = :TABLE
46
+ TRUNCATE = :TRUNCATE
47
+ UPDATE = :UPDATE
48
+
49
+ KEYWORDS = {
50
+ 2 => [AS, OR],
51
+ 3 => [SET],
52
+ 4 => [CALL, FROM, INTO],
53
+ 5 => [TABLE],
54
+ 6 => [DELETE, INSERT, SELECT, UPDATE],
55
+ 7 => [REPLACE],
56
+ 8 => [TRUNCATE]
57
+ }.freeze
58
+
59
+ KEYWORD_MIN_LENGTH = KEYWORDS.keys.first
60
+ KEYWORD_MAX_LENGTH = KEYWORDS.keys.last
61
+ end
62
+ end
63
+ 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
  require 'atatus/util/lru_cache'
@@ -9,12 +26,12 @@ module Atatus
9
26
  TABLE_REGEX = %{["'`]?([A-Za-z0-9_]+)["'`]?}
10
27
 
11
28
  REGEXES = {
12
- /^BEGIN/i => 'BEGIN',
13
- /^COMMIT/i => 'COMMIT',
14
- /^SELECT .* FROM #{TABLE_REGEX}/i => 'SELECT FROM ',
15
- /^INSERT INTO #{TABLE_REGEX}/i => 'INSERT INTO ',
16
- /^UPDATE #{TABLE_REGEX}/i => 'UPDATE ',
17
- /^DELETE FROM #{TABLE_REGEX}/i => 'DELETE FROM '
29
+ /^BEGIN/iu => 'BEGIN',
30
+ /^COMMIT/iu => 'COMMIT',
31
+ /^SELECT .* FROM #{TABLE_REGEX}/iu => 'SELECT FROM ',
32
+ /^INSERT INTO #{TABLE_REGEX}/iu => 'INSERT INTO ',
33
+ /^UPDATE #{TABLE_REGEX}/iu => 'UPDATE ',
34
+ /^DELETE FROM #{TABLE_REGEX}/iu => 'DELETE FROM '
18
35
  }.freeze
19
36
 
20
37
  FORMAT = '%s%s'
@@ -24,6 +41,7 @@ module Atatus
24
41
  end
25
42
 
26
43
  def summarize(sql)
44
+ sql = sql.encode('utf-8', invalid: :replace, undef: :replace)
27
45
  self.class.cache[sql] ||=
28
46
  REGEXES.find do |regex, sig|
29
47
  if (match = sql[0...1000].match(regex))
@@ -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/naively_hashable'
@@ -21,8 +38,6 @@ module Atatus
21
38
  :module,
22
39
  :colno
23
40
  )
24
-
25
- # rubocop:disable Metrics/AbcSize
26
41
  def build_context(context_line_count)
27
42
  return unless abs_path && context_line_count > 0
28
43
 
@@ -38,7 +53,6 @@ module Atatus
38
53
  self.pre_context = file_lines.first(padding)
39
54
  self.post_context = file_lines.last(padding)
40
55
  end
41
- # rubocop:enable Metrics/AbcSize
42
56
 
43
57
  private
44
58
 
@@ -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/frame'
@@ -12,7 +29,12 @@ module Atatus
12
29
  RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}.freeze
13
30
  JRUBY_ORG_REGEX = %r{org/jruby}.freeze
14
31
 
15
- GEMS_PATH = defined?(Bundler) ? Bundler.bundle_path.to_s : Gem.dir
32
+ GEMS_PATH =
33
+ if defined?(Bundler) && Bundler.default_bundle_dir
34
+ Bundler.bundle_path.to_s
35
+ else
36
+ Gem.dir
37
+ end
16
38
 
17
39
  def initialize(config)
18
40
  @config = config
@@ -31,7 +53,6 @@ module Atatus
31
53
 
32
54
  private
33
55
 
34
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
35
56
  def build_frame(cache, keys)
36
57
  line, type = keys
37
58
  abs_path, lineno, function, _module_name = parse_line(line)
@@ -49,7 +70,6 @@ module Atatus
49
70
 
50
71
  cache[[line, type]] = frame
51
72
  end
52
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
53
73
 
54
74
  def parse_line(line)
55
75
  ruby_match = line.match(RUBY_FORMAT)
@@ -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 'active_support/notifications'
@@ -28,8 +45,6 @@ module Atatus
28
45
  # AS::Notifications API
29
46
 
30
47
  Notification = Struct.new(:id, :span)
31
-
32
- # rubocop:disable Metrics/MethodLength
33
48
  def start(name, id, payload)
34
49
  return unless (transaction = @agent.current_transaction)
35
50
 
@@ -52,9 +67,9 @@ module Atatus
52
67
 
53
68
  transaction.notifications << Notification.new(id, span)
54
69
  end
55
- # rubocop:enable Metrics/MethodLength
56
70
 
57
- def finish(_name, id, _payload)
71
+ # rubocop:disable Metrics/CyclomaticComplexity
72
+ def finish(name, id, payload)
58
73
  # debug "AS::Notification#finish:#{name}:#{id}"
59
74
  return unless (transaction = @agent.current_transaction)
60
75
 
@@ -62,11 +77,15 @@ module Atatus
62
77
  next unless notification.id == id
63
78
 
64
79
  if (span = notification.span)
80
+ if @agent.config.span_frames_min_duration?
81
+ span.original_backtrace ||= @normalizers.backtrace(name, payload)
82
+ end
65
83
  @agent.end_span if span == @agent.current_span
66
84
  end
67
85
  return
68
86
  end
69
87
  end
88
+ # rubocop:enable Metrics/CyclomaticComplexity
70
89
 
71
90
  private
72
91
 
@@ -1,85 +1,118 @@
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
 
20
+ require 'atatus/trace_context/tracestate'
21
+ require 'atatus/trace_context/traceparent'
22
+
3
23
  module Atatus
4
24
  # @api private
5
25
  class TraceContext
6
- class InvalidTraceparentHeader < StandardError; end
26
+ extend Forwardable
7
27
 
8
- VERSION = '00'
9
- HEX_REGEX = /[^[:xdigit:]]/.freeze
10
-
11
- TRACE_ID_LENGTH = 16
12
- ID_LENGTH = 8
28
+ class InvalidTraceparentHeader < StandardError; end
13
29
 
14
30
  def initialize(
15
- version: VERSION,
16
- trace_id: nil,
17
- span_id: nil,
18
- id: nil,
19
- recorded: true
31
+ traceparent: nil,
32
+ tracestate: nil,
33
+ **legacy_traceparent_attrs
20
34
  )
21
- @version = version
22
- @trace_id = trace_id || hex(TRACE_ID_LENGTH)
23
- # TODO: rename to parent_id with next major version bump
24
- @parent_id = span_id
25
- @id = id || hex(ID_LENGTH)
26
- @recorded = recorded
35
+ @traceparent = traceparent || Traceparent.new(**legacy_traceparent_attrs)
36
+ @tracestate = tracestate || Tracestate.new
27
37
  end
28
38
 
29
- attr_accessor :version, :id, :trace_id, :parent_id, :recorded
39
+ attr_accessor :traceparent, :tracestate
40
+
41
+ def_delegators :traceparent,
42
+ :version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded?
43
+
44
+ class << self
45
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
46
+ def parse(legacy_header = nil, env: nil, metadata: nil)
47
+ unless legacy_header || env || metadata
48
+ raise ArgumentError, 'TraceContext expects env:, metadata: ' \
49
+ 'or single argument header string'
50
+ end
51
+
52
+ if legacy_header
53
+ legacy_parse_from_header(legacy_header)
54
+ elsif env
55
+ trace_context_from_env(env)
56
+ elsif metadata
57
+ trace_context_from_metadata(metadata)
58
+ end
59
+ end
60
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
61
+
62
+ private
30
63
 
31
- alias :recorded? :recorded
64
+ def trace_context_from_env(env)
65
+ return unless (
66
+ header =
67
+ env['HTTP_ATATUS_TRACEPARENT'] || env['HTTP_TRACEPARENT']
68
+ )
32
69
 
33
- # rubocop:disable Metrics/AbcSize
34
- def self.parse(header)
35
- raise InvalidTraceparentHeader unless header.length == 55
36
- raise InvalidTraceparentHeader unless header[0..1] == VERSION
70
+ parent = TraceContext::Traceparent.parse(header)
37
71
 
38
- new.tap do |t|
39
- t.version, t.trace_id, t.parent_id, t.flags =
40
- header.split('-').tap do |values|
41
- values[-1] = Util.hex_to_bits(values[-1])
72
+ state =
73
+ if (header = env['HTTP_TRACESTATE'])
74
+ TraceContext::Tracestate.parse(header)
42
75
  end
43
76
 
44
- raise InvalidTraceparentHeader if HEX_REGEX =~ t.trace_id
45
- raise InvalidTraceparentHeader if HEX_REGEX =~ t.parent_id
77
+ new(traceparent: parent, tracestate: state)
46
78
  end
47
- end
48
- # rubocop:enable Metrics/AbcSize
49
79
 
50
- def flags=(flags)
51
- @flags = flags
80
+ def trace_context_from_metadata(metadata)
81
+ return unless (header = metadata['atatus-traceparent'] ||
82
+ metadata['traceparent'])
52
83
 
53
- self.recorded = flags[7] == '1'
54
- end
84
+ parent = TraceContext::Traceparent.parse(header)
55
85
 
56
- def flags
57
- format('0000000%d', recorded? ? 1 : 0)
58
- end
86
+ state =
87
+ if (header = metadata['tracestate'])
88
+ TraceContext::Tracestate.parse(header)
89
+ end
59
90
 
60
- def hex_flags
61
- format('%02x', flags.to_i(2))
62
- end
91
+ new(traceparent: parent, tracestate: state)
92
+ end
63
93
 
64
- def ensure_parent_id
65
- @parent_id ||= hex(ID_LENGTH)
94
+ def legacy_parse_from_header(header)
95
+ parent = Traceparent.parse(header)
96
+ new(traceparent: parent)
97
+ end
66
98
  end
67
99
 
68
100
  def child
69
101
  dup.tap do |tc|
70
- tc.parent_id = tc.id
71
- tc.id = hex(ID_LENGTH)
102
+ tc.traceparent = tc.traceparent.child
72
103
  end
73
104
  end
74
105
 
75
- def to_header
76
- format('%s-%s-%s-%s', version, trace_id, id, hex_flags)
77
- end
106
+ def apply_headers
107
+ yield 'Traceparent', traceparent.to_header
108
+
109
+ if tracestate
110
+ yield 'Tracestate', tracestate.to_header
111
+ end
78
112
 
79
- private
113
+ return unless Atatus.agent.config.use_atatus_traceparent_header
80
114
 
81
- def hex(len)
82
- SecureRandom.hex(len)
115
+ yield 'Atatus-Traceparent', traceparent.to_header
83
116
  end
84
117
  end
85
118
  end
@@ -0,0 +1,111 @@
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
+ class TraceContext
22
+ # @api private
23
+ class Traceparent
24
+ VERSION = '00'
25
+ HEX_REGEX = /[^[:xdigit:]]/.freeze
26
+
27
+ TRACE_ID_LENGTH = 16
28
+ ID_LENGTH = 8
29
+
30
+ # rubocop:disable Metrics/ParameterLists
31
+ def initialize(
32
+ version: VERSION,
33
+ trace_id: nil,
34
+ span_id: nil,
35
+ id: nil,
36
+ recorded: true
37
+ )
38
+ @version = version
39
+ @trace_id = trace_id || hex(TRACE_ID_LENGTH)
40
+ # TODO: rename span_id kw arg to parent_id with next major version bump
41
+ @parent_id = span_id
42
+ @id = id || hex(ID_LENGTH)
43
+ @recorded = recorded
44
+ end
45
+ # rubocop:enable Metrics/ParameterLists
46
+
47
+ attr_accessor :version, :id, :trace_id, :parent_id, :recorded
48
+
49
+ alias :recorded? :recorded
50
+
51
+ def self.parse(header)
52
+ raise_invalid(header) unless header.length == 55
53
+ raise_invalid(header) unless header[0..1] == VERSION
54
+
55
+ new.tap do |t|
56
+ t.version, t.trace_id, t.parent_id, t.flags =
57
+ header.split('-').tap do |values|
58
+ values[-1] = Util.hex_to_bits(values[-1])
59
+ end
60
+
61
+ raise_invalid(header) if HEX_REGEX =~ t.trace_id
62
+ raise_invalid(header) if HEX_REGEX =~ t.parent_id
63
+ end
64
+ end
65
+
66
+ class << self
67
+ private
68
+
69
+ def raise_invalid(header)
70
+ raise InvalidTraceparentHeader,
71
+ "Couldn't parse invalid traceparent header: #{header.inspect}"
72
+ end
73
+ end
74
+
75
+ def flags=(flags)
76
+ @flags = flags
77
+
78
+ self.recorded = flags[7] == '1'
79
+ end
80
+
81
+ def flags
82
+ format('0000000%d', recorded? ? 1 : 0)
83
+ end
84
+
85
+ def hex_flags
86
+ format('%02x', flags.to_i(2))
87
+ end
88
+
89
+ def ensure_parent_id
90
+ @parent_id ||= hex(ID_LENGTH)
91
+ end
92
+
93
+ def child
94
+ dup.tap do |copy|
95
+ copy.parent_id = id
96
+ copy.id = hex(ID_LENGTH)
97
+ end
98
+ end
99
+
100
+ def to_header
101
+ format('%s-%s-%s-%s', version, trace_id, id, hex_flags)
102
+ end
103
+
104
+ private
105
+
106
+ def hex(len)
107
+ SecureRandom.hex(len)
108
+ end
109
+ end
110
+ end
111
+ end