atatus 1.6.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile +49 -13
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +3 -3
  6. data/lib/atatus/agent.rb +18 -7
  7. data/lib/atatus/central_config.rb +19 -8
  8. data/lib/atatus/collector/base.rb +113 -1
  9. data/lib/atatus/collector/builder.rb +8 -0
  10. data/lib/atatus/collector/layer.rb +1 -1
  11. data/lib/atatus/collector/transport.rb +23 -2
  12. data/lib/atatus/{sql_summarizer.rb → config/log_level_map.rb} +22 -28
  13. data/lib/atatus/config/options.rb +2 -1
  14. data/lib/atatus/config/regexp_list.rb +1 -1
  15. data/lib/atatus/config/round_float.rb +31 -0
  16. data/lib/atatus/config/server_info.rb +50 -0
  17. data/lib/atatus/config/wildcard_pattern_list.rb +3 -1
  18. data/lib/atatus/config.rb +94 -70
  19. data/lib/atatus/context/company.rb +38 -0
  20. data/lib/atatus/context/request/socket.rb +1 -2
  21. data/lib/atatus/context/response.rb +1 -3
  22. data/lib/atatus/context.rb +4 -8
  23. data/lib/atatus/context_builder.rb +3 -3
  24. data/lib/atatus/error.rb +2 -1
  25. data/lib/atatus/error_builder.rb +1 -1
  26. data/lib/atatus/fields.rb +98 -0
  27. data/lib/atatus/graphql.rb +2 -0
  28. data/lib/atatus/grpc.rb +5 -7
  29. data/lib/atatus/instrumenter.rb +38 -24
  30. data/lib/atatus/metadata/cloud_info.rb +156 -0
  31. data/lib/atatus/metadata/service_info.rb +3 -3
  32. data/lib/atatus/metadata/system_info/container_info.rb +20 -8
  33. data/lib/atatus/metadata/system_info.rb +20 -5
  34. data/lib/atatus/metadata.rb +3 -1
  35. data/lib/atatus/metrics/cpu_mem_set.rb +10 -38
  36. data/lib/atatus/metrics/jvm_set.rb +88 -0
  37. data/lib/atatus/metrics/metric.rb +2 -0
  38. data/lib/atatus/metrics.rb +32 -16
  39. data/lib/atatus/middleware.rb +8 -3
  40. data/lib/atatus/naively_hashable.rb +1 -0
  41. data/lib/atatus/normalizers/rails/active_record.rb +25 -7
  42. data/lib/atatus/normalizers.rb +2 -2
  43. data/lib/atatus/opentracing.rb +5 -3
  44. data/lib/atatus/rails.rb +1 -1
  45. data/lib/atatus/span/context/db.rb +1 -1
  46. data/lib/atatus/span/context/destination.rb +58 -32
  47. data/lib/atatus/span/context/http.rb +2 -0
  48. data/lib/atatus/span/context/links.rb +32 -0
  49. data/lib/atatus/{sql.rb → span/context/message.rb} +16 -12
  50. data/lib/atatus/span/context/service.rb +55 -0
  51. data/lib/atatus/span/context.rb +28 -3
  52. data/lib/atatus/span.rb +35 -5
  53. data/lib/atatus/span_helpers.rb +12 -23
  54. data/lib/atatus/spies/action_dispatch.rb +10 -13
  55. data/lib/atatus/spies/azure_storage_table.rb +148 -0
  56. data/lib/atatus/spies/delayed_job.rb +19 -13
  57. data/lib/atatus/spies/dynamo_db.rb +56 -15
  58. data/lib/atatus/spies/elasticsearch.rb +54 -39
  59. data/lib/atatus/spies/faraday.rb +92 -58
  60. data/lib/atatus/spies/http.rb +33 -37
  61. data/lib/atatus/spies/json.rb +5 -9
  62. data/lib/atatus/spies/mongo.rb +26 -19
  63. data/lib/atatus/spies/net_http.rb +53 -51
  64. data/lib/atatus/spies/racecar.rb +77 -0
  65. data/lib/atatus/spies/rake.rb +27 -27
  66. data/lib/atatus/spies/redis.rb +11 -12
  67. data/lib/atatus/spies/resque.rb +18 -23
  68. data/lib/atatus/spies/s3.rb +132 -0
  69. data/lib/atatus/spies/sequel.rb +50 -40
  70. data/lib/atatus/spies/shoryuken.rb +4 -6
  71. data/lib/atatus/spies/sidekiq.rb +23 -31
  72. data/lib/atatus/spies/sinatra.rb +20 -28
  73. data/lib/atatus/spies/sneakers.rb +2 -0
  74. data/lib/atatus/spies/sns.rb +126 -0
  75. data/lib/atatus/spies/sqs.rb +231 -0
  76. data/lib/atatus/spies/sucker_punch.rb +20 -22
  77. data/lib/atatus/spies/tilt.rb +10 -13
  78. data/lib/atatus/spies.rb +20 -0
  79. data/lib/atatus/sql/signature.rb +4 -2
  80. data/lib/atatus/sql/tokenizer.rb +23 -7
  81. data/lib/atatus/stacktrace/frame.rb +1 -0
  82. data/lib/atatus/stacktrace_builder.rb +12 -16
  83. data/lib/atatus/subscriber.rb +1 -0
  84. data/lib/atatus/trace_context/traceparent.rb +5 -8
  85. data/lib/atatus/trace_context/tracestate.rb +16 -14
  86. data/lib/atatus/trace_context.rb +6 -16
  87. data/lib/atatus/transaction.rb +25 -4
  88. data/lib/atatus/transport/base.rb +1 -3
  89. data/lib/atatus/transport/connection/http.rb +11 -3
  90. data/lib/atatus/transport/connection/proxy_pipe.rb +1 -2
  91. data/lib/atatus/transport/connection.rb +3 -2
  92. data/lib/atatus/transport/filters/hash_sanitizer.rb +16 -34
  93. data/lib/atatus/transport/filters/secrets_filter.rb +35 -12
  94. data/lib/atatus/transport/serializers/context_serializer.rb +1 -2
  95. data/lib/atatus/transport/serializers/metadata_serializer.rb +54 -8
  96. data/lib/atatus/transport/serializers/metricset_serializer.rb +2 -2
  97. data/lib/atatus/transport/serializers/span_serializer.rb +55 -9
  98. data/lib/atatus/transport/serializers/transaction_serializer.rb +1 -0
  99. data/lib/atatus/transport/serializers.rb +9 -6
  100. data/lib/atatus/transport/user_agent.rb +16 -9
  101. data/lib/atatus/transport/worker.rb +2 -1
  102. data/lib/atatus/util/deep_dup.rb +65 -0
  103. data/lib/atatus/util/precision_validator.rb +46 -0
  104. data/lib/atatus/util.rb +2 -0
  105. data/lib/atatus/version.rb +1 -1
  106. data/lib/atatus.rb +48 -5
  107. metadata +41 -11
@@ -29,10 +29,9 @@ module Atatus
29
29
 
30
30
  def initialize(
31
31
  traceparent: nil,
32
- tracestate: nil,
33
- **legacy_traceparent_attrs
32
+ tracestate: nil
34
33
  )
35
- @traceparent = traceparent || Traceparent.new(**legacy_traceparent_attrs)
34
+ @traceparent = traceparent || Traceparent.new
36
35
  @tracestate = tracestate || Tracestate.new
37
36
  end
38
37
 
@@ -42,22 +41,18 @@ module Atatus
42
41
  :version, :trace_id, :id, :parent_id, :ensure_parent_id, :recorded?
43
42
 
44
43
  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
44
+ def parse(env: nil, metadata: nil)
45
+ unless env || metadata
48
46
  raise ArgumentError, 'TraceContext expects env:, metadata: ' \
49
47
  'or single argument header string'
50
48
  end
51
49
 
52
- if legacy_header
53
- legacy_parse_from_header(legacy_header)
54
- elsif env
50
+ if env
55
51
  trace_context_from_env(env)
56
52
  elsif metadata
57
53
  trace_context_from_metadata(metadata)
58
54
  end
59
55
  end
60
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
61
56
 
62
57
  private
63
58
 
@@ -90,11 +85,6 @@ module Atatus
90
85
 
91
86
  new(traceparent: parent, tracestate: state)
92
87
  end
93
-
94
- def legacy_parse_from_header(header)
95
- parent = Traceparent.parse(header)
96
- new(traceparent: parent)
97
- end
98
88
  end
99
89
 
100
90
  def child
@@ -106,7 +96,7 @@ module Atatus
106
96
  def apply_headers
107
97
  yield 'Traceparent', traceparent.to_header
108
98
 
109
- if tracestate
99
+ if tracestate && !tracestate.to_header.empty?
110
100
  yield 'Tracestate', tracestate.to_header
111
101
  end
112
102
 
@@ -20,6 +20,17 @@
20
20
  module Atatus
21
21
  # @api private
22
22
  class Transaction
23
+ # @api private
24
+ class Outcome
25
+ FAILURE = "failure"
26
+ SUCCESS = "success"
27
+ UNKNOWN = "unknown"
28
+
29
+ def self.from_http_status(code)
30
+ code.to_i >= 500 ? FAILURE : SUCCESS
31
+ end
32
+ end
33
+
23
34
  extend Forwardable
24
35
  include ChildDurations::Methods
25
36
 
@@ -33,10 +44,10 @@ module Atatus
33
44
  def initialize(
34
45
  name = nil,
35
46
  type = nil,
47
+ config:,
36
48
  sampled: true,
37
49
  sample_rate: 1,
38
50
  context: nil,
39
- config:,
40
51
  trace_context: nil
41
52
  )
42
53
  @name = name
@@ -63,7 +74,9 @@ module Atatus
63
74
  unless (@trace_context = trace_context)
64
75
  @trace_context = TraceContext.new(
65
76
  traceparent: TraceContext::Traceparent.new(recorded: sampled),
66
- tracestate: TraceContext::Tracestate.new(sample_rate: sampled ? sample_rate : 0)
77
+ tracestate: TraceContext::Tracestate.new(
78
+ sample_rate: sampled ? sample_rate : 0
79
+ )
67
80
  )
68
81
  end
69
82
 
@@ -74,7 +87,7 @@ module Atatus
74
87
  end
75
88
  # rubocop:enable Metrics/ParameterLists
76
89
 
77
- attr_accessor :name, :type, :result, :spans, :ruby_time
90
+ attr_accessor :name, :type, :result, :outcome, :spans, :ruby_time
78
91
 
79
92
  attr_reader(
80
93
  :breakdown_metrics,
@@ -90,7 +103,7 @@ module Atatus
90
103
  :started_spans,
91
104
  :timestamp,
92
105
  :trace_context,
93
- :transaction_max_spans
106
+ :transaction_max_spans
94
107
  )
95
108
 
96
109
  alias :collect_metrics? :collect_metrics
@@ -148,6 +161,14 @@ module Atatus
148
161
  context.user = Context::User.infer(@config, user)
149
162
  end
150
163
 
164
+ def set_company(company)
165
+ context.company = Context::Company.infer(@config, company)
166
+ end
167
+
168
+ def set_response_body(response_body)
169
+ context.response_body = response_body
170
+ end
171
+
151
172
  def inspect
152
173
  "<Atatus::Transaction id:#{id}" \
153
174
  " name:#{name.inspect} type:#{type.inspect}>"
@@ -35,7 +35,6 @@ module Atatus
35
35
  include Logging
36
36
 
37
37
  WATCHER_EXECUTION_INTERVAL = 5
38
- WATCHER_TIMEOUT_INTERVAL = 4
39
38
  WORKER_JOIN_TIMEOUT = 5
40
39
 
41
40
  def initialize(config)
@@ -112,8 +111,7 @@ module Atatus
112
111
 
113
112
  def create_watcher
114
113
  @watcher = Concurrent::TimerTask.execute(
115
- execution_interval: WATCHER_EXECUTION_INTERVAL,
116
- timeout_interval: WATCHER_TIMEOUT_INTERVAL
114
+ execution_interval: WATCHER_EXECUTION_INTERVAL
117
115
  ) { ensure_worker_count }
118
116
  end
119
117
 
@@ -74,8 +74,16 @@ module Atatus
74
74
  debug '%s: Closing request with reason %s', thread_str, reason
75
75
  @closed.make_true
76
76
 
77
- @wr&.close(reason)
78
- return if @request.nil? || @request&.join(5)
77
+ @wr&.close
78
+
79
+ if @request&.join(5)
80
+ @rd&.close
81
+ return
82
+ end
83
+
84
+ @rd&.close
85
+
86
+ return if @request.nil?
79
87
 
80
88
  error(
81
89
  '%s: APM Server not responding in time, terminating request',
@@ -85,7 +93,7 @@ module Atatus
85
93
  end
86
94
 
87
95
  def closed?
88
- @closed.true?
96
+ @rd.closed? && @closed.true?
89
97
  end
90
98
 
91
99
  def inspect
@@ -62,8 +62,7 @@ module Atatus
62
62
  @io = Zlib::GzipWriter.new(io)
63
63
  end
64
64
 
65
- def close(reason = nil)
66
- debug("Closing writer with reason #{reason}")
65
+ def close
67
66
  io.close
68
67
  end
69
68
 
@@ -42,11 +42,12 @@ module Atatus
42
42
  Metadata.new(config)
43
43
  )
44
44
  )
45
- @url = config.server_url + '/intake/v2/events'
45
+ @url = "#{config.server_url}/intake/v2/events"
46
46
  @mutex = Mutex.new
47
47
  end
48
48
 
49
49
  attr_reader :http
50
+
50
51
  def write(str)
51
52
  return false if @config.disable_send
52
53
 
@@ -104,7 +105,7 @@ module Atatus
104
105
  @close_task&.cancel
105
106
  @close_task =
106
107
  Concurrent::ScheduledTask.execute(@config.api_request_time) do
107
- flush(:timeout)
108
+ flush(:scheduled_flush)
108
109
  end
109
110
  end
110
111
  end
@@ -17,59 +17,41 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
+ require 'atatus/util/deep_dup'
21
+
20
22
  module Atatus
21
23
  module Transport
22
24
  module Filters
25
+ # @api private
23
26
  class HashSanitizer
24
27
  FILTERED = '[FILTERED]'
25
28
 
26
- KEY_FILTERS = [
27
- /passw(or)?d/i,
28
- /auth/i,
29
- /^pw$/,
30
- /secret/i,
31
- /token/i,
32
- /api[-._]?key/i,
33
- /session[-._]?id/i,
34
- /(set[-_])?cookie/i
35
- ].freeze
36
-
37
- VALUE_FILTERS = [
38
- # (probably) credit card number
39
- /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
40
- ].freeze
29
+ def initialize(key_patterns:)
30
+ @key_patterns = key_patterns
31
+ end
41
32
 
42
- attr_accessor :key_filters
33
+ attr_accessor :key_patterns
43
34
 
44
- def initialize
45
- @key_filters = KEY_FILTERS
35
+ def strip_from(obj)
36
+ strip_from!(Util::DeepDup.dup(obj))
46
37
  end
47
38
 
48
- def strip_from!(obj, key_filters = KEY_FILTERS)
49
- return unless obj&.is_a?(Hash)
50
-
51
- obj.each do |k, v|
52
- if filter_key?(k)
53
- next obj[k] = FILTERED
54
- end
39
+ def strip_from!(obj)
40
+ return unless obj.is_a?(Hash)
55
41
 
42
+ obj.each_pair do |k, v|
56
43
  case v
57
44
  when Hash
58
45
  strip_from!(v)
59
- when String
60
- if filter_value?(v)
61
- obj[k] = FILTERED
62
- end
46
+ else
47
+ next unless filter_key?(k)
48
+ obj[k] = FILTERED
63
49
  end
64
50
  end
65
51
  end
66
52
 
67
53
  def filter_key?(key)
68
- @key_filters.any? { |regex| regex.match(key) }
69
- end
70
-
71
- def filter_value?(value)
72
- VALUE_FILTERS.any? { |regex| regex.match(value) }
54
+ @key_patterns.any? { |regex| regex.match(key) }
73
55
  end
74
56
  end
75
57
  end
@@ -26,21 +26,44 @@ module Atatus
26
26
  class SecretsFilter
27
27
  def initialize(config)
28
28
  @config = config
29
- @sanitizer = HashSanitizer.new
30
- @sanitizer.key_filters += config.custom_key_filters +
31
- config.sanitize_field_names
29
+ @sanitizer =
30
+ HashSanitizer.new(
31
+ key_patterns: config.custom_key_filters +
32
+ config.sanitize_field_names
33
+ )
32
34
  end
33
35
 
34
36
  def call(payload)
35
- @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :headers)
36
- @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :env)
37
- @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :cookies)
38
- @sanitizer.strip_from! payload.dig(:transaction, :context, :response, :headers)
39
- @sanitizer.strip_from! payload.dig(:error, :context, :request, :headers)
40
- @sanitizer.strip_from! payload.dig(:error, :context, :request, :cookies)
41
- @sanitizer.strip_from! payload.dig(:error, :context, :response, :headers)
42
- @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :body)
43
-
37
+ @sanitizer.strip_from!(
38
+ payload.dig(:transaction, :context, :request, :body)
39
+ )
40
+ @sanitizer.strip_from!(
41
+ payload.dig(:transaction, :context, :request, :cookies)
42
+ )
43
+ @sanitizer.strip_from!(
44
+ payload.dig(:transaction, :context, :request, :env)
45
+ )
46
+ @sanitizer.strip_from!(
47
+ payload.dig(:transaction, :context, :request, :headers)
48
+ )
49
+ @sanitizer.strip_from!(
50
+ payload.dig(:transaction, :context, :response, :headers)
51
+ )
52
+ @sanitizer.strip_from!(
53
+ payload.dig(:error, :context, :request, :body)
54
+ )
55
+ @sanitizer.strip_from!(
56
+ payload.dig(:error, :context, :request, :cookies)
57
+ )
58
+ @sanitizer.strip_from!(
59
+ payload.dig(:error, :context, :request, :env)
60
+ )
61
+ @sanitizer.strip_from!(
62
+ payload.dig(:error, :context, :request, :headers)
63
+ )
64
+ @sanitizer.strip_from!(
65
+ payload.dig(:error, :context, :response, :headers)
66
+ )
44
67
  payload
45
68
  end
46
69
  end
@@ -77,8 +77,7 @@ module Atatus
77
77
  return unless socket
78
78
 
79
79
  {
80
- remote_addr: socket.remote_addr,
81
- encrypted: socket.encrypted
80
+ remote_addr: socket.remote_addr
82
81
  }
83
82
  end
84
83
 
@@ -23,14 +23,19 @@ module Atatus
23
23
  # @api private
24
24
  class MetadataSerializer < Serializer
25
25
  def build(metadata)
26
- {
27
- metadata: {
26
+ base =
27
+ {
28
28
  service: build_service(metadata.service),
29
29
  process: build_process(metadata.process),
30
30
  system: build_system(metadata.system),
31
31
  labels: build_labels(metadata.labels)
32
32
  }
33
- }
33
+
34
+ if metadata.cloud.provider
35
+ base[:cloud] = build_cloud(metadata.cloud)
36
+ end
37
+
38
+ { metadata: base }
34
39
  end
35
40
 
36
41
  private
@@ -59,8 +64,8 @@ module Atatus
59
64
  }
60
65
  }
61
66
 
62
- if node_name = service.node_name
63
- base[:node] = { name: keyword_field(node_name) }
67
+ if (node_name = service.node_name)
68
+ base[:node] = { configured_name: keyword_field(node_name) }
64
69
  end
65
70
 
66
71
  base
@@ -75,17 +80,58 @@ module Atatus
75
80
  end
76
81
 
77
82
  def build_system(system)
78
- {
79
- hostname: keyword_field(system.hostname),
83
+ base = {
84
+ configured_hostname: keyword_field(system.configured_hostname),
80
85
  architecture: keyword_field(system.architecture),
81
86
  platform: keyword_field(system.platform),
82
- kubernetes: keyword_object(system.kubernetes)
87
+ kubernetes: keyword_object(system.kubernetes),
88
+ container: keyword_object(system.container)
83
89
  }
90
+ if system.detected_hostname
91
+ base[:detected_hostname] = keyword_field(system.detected_hostname)
92
+ end
93
+
94
+ base
95
+ end
96
+
97
+ def build_cloud(cloud)
98
+ strip_nulls!(
99
+ provider: cloud.provider,
100
+ account: {
101
+ id: keyword_field(cloud.account_id),
102
+ name: keyword_field(cloud.account_name)
103
+ },
104
+ availability_zone: keyword_field(cloud.availability_zone),
105
+ instance: {
106
+ id: keyword_field(cloud.instance_id),
107
+ name: keyword_field(cloud.instance_name)
108
+ },
109
+ machine: { type: keyword_field(cloud.machine_type) },
110
+ project: {
111
+ id: keyword_field(cloud.project_id),
112
+ name: keyword_field(cloud.project_name)
113
+ },
114
+ region: keyword_field(cloud.region)
115
+ )
84
116
  end
85
117
 
86
118
  def build_labels(labels)
87
119
  keyword_object(labels)
88
120
  end
121
+
122
+ # A bug in APM Server 7.9 disallows null values in `cloud`
123
+ def strip_nulls!(hash)
124
+ hash.each_key do |key|
125
+ case value = hash[key]
126
+ when Hash
127
+ strip_nulls!(value)
128
+ hash.delete(key) if value.empty?
129
+ when nil then hash.delete(key)
130
+ end
131
+ end
132
+
133
+ hash
134
+ end
89
135
  end
90
136
  end
91
137
  end
@@ -46,8 +46,8 @@ module Atatus
46
46
  private
47
47
 
48
48
  def build_samples(samples)
49
- samples.each_with_object({}) do |(key, value), hsh|
50
- hsh[key] = { value: value }
49
+ samples.transform_values do |value|
50
+ { value: value }
51
51
  end
52
52
  end
53
53
  end
@@ -43,14 +43,14 @@ module Atatus
43
43
  stacktrace: span.stacktrace.to_a,
44
44
  timestamp: span.timestamp,
45
45
  trace_id: span.trace_id,
46
- sample_rate: span.sample_rate
46
+ sample_rate: span.sample_rate,
47
+ outcome: keyword_field(span.outcome)
47
48
  }
48
49
  }
49
50
  end
50
51
 
51
52
  # @api private
52
53
  class ContextSerializer < Serializer
53
- # rubocop:disable Metrics/CyclomaticComplexity
54
54
  def build(context)
55
55
  return unless context
56
56
 
@@ -65,9 +65,20 @@ module Atatus
65
65
  base[:destination] = build_destination(context.destination)
66
66
  end
67
67
 
68
+ if context.message
69
+ base[:message] = build_message(context.message)
70
+ end
71
+
72
+ if context.service
73
+ base[:service] = build_service(context.service)
74
+ end
75
+
76
+ if context.links && !context.links.empty?
77
+ base[:links] = build_links(context.links)
78
+ end
79
+
68
80
  base
69
81
  end
70
- # rubocop:enable Metrics/CyclomaticComplexity
71
82
 
72
83
  private
73
84
 
@@ -90,15 +101,50 @@ module Atatus
90
101
  end
91
102
 
92
103
  def build_destination(destination)
93
- {
94
- service: {
95
- name: keyword_field(destination.name),
96
- resource: keyword_field(destination.resource),
97
- type: keyword_field(destination.type)
98
- },
104
+ return unless destination
105
+
106
+ base = {
99
107
  address: keyword_field(destination.address),
100
108
  port: destination.port
101
109
  }
110
+
111
+ if (service = destination.service) && !service.empty?
112
+ base[:service] = service.to_h
113
+ end
114
+
115
+ if (cloud = destination.cloud) && !cloud.empty?
116
+ base[:cloud] = cloud.to_h
117
+ end
118
+
119
+ base
120
+ end
121
+
122
+ def build_message(message)
123
+ {
124
+ queue: {
125
+ name: keyword_field(message.queue_name)
126
+ },
127
+ age: {
128
+ ms: message.age_ms.to_i
129
+ }
130
+ }
131
+ end
132
+
133
+ def build_service(service)
134
+ {
135
+ target: {
136
+ name: keyword_field(service.target&.name),
137
+ type: keyword_field(service.target&.type)
138
+ }
139
+ }
140
+ end
141
+
142
+ def build_links(links)
143
+ {
144
+ links: links.map do |link|
145
+ {"trace_id" => link.trace_id, "span_id" => link.span_id}
146
+ end
147
+ }
102
148
  end
103
149
  end
104
150
 
@@ -35,6 +35,7 @@ module Atatus
35
35
  name: keyword_field(transaction.name),
36
36
  type: keyword_field(transaction.type),
37
37
  result: keyword_field(transaction.result.to_s),
38
+ outcome: keyword_field(transaction.outcome),
38
39
  duration: ms(transaction.duration),
39
40
  timestamp: transaction.timestamp,
40
41
  sampled: transaction.sampled?,
@@ -45,18 +45,20 @@ module Atatus
45
45
  def keyword_object(hash)
46
46
  return unless hash
47
47
 
48
- hash.tap do |h|
49
- h.each { |k, v| hash[k] = keyword_field(v) }
48
+ hash.each do |k, v|
49
+ hash[k] =
50
+ case v
51
+ when Hash then keyword_object(v)
52
+ else keyword_field(v)
53
+ end
50
54
  end
51
55
  end
52
56
 
53
57
  def mixed_object(hash)
54
58
  return unless hash
55
59
 
56
- hash.tap do |h|
57
- h.each do |k, v|
58
- hash[k] = v.is_a?(String) ? keyword_field(v) : v
59
- end
60
+ hash.each do |k, v|
61
+ hash[k] = v.is_a?(String) ? keyword_field(v) : v
60
62
  end
61
63
  end
62
64
  end
@@ -72,6 +74,7 @@ module Atatus
72
74
  end
73
75
 
74
76
  attr_reader :transaction, :span, :error, :metadata, :metricset
77
+
75
78
  def serialize(resource)
76
79
  case resource
77
80
  when Transaction
@@ -21,7 +21,8 @@ module Atatus
21
21
  module Transport
22
22
  # @api private
23
23
  class UserAgent
24
- def initialize(config)
24
+ def initialize(config, version: VERSION)
25
+ @version = version
25
26
  @built = build(config)
26
27
  end
27
28
 
@@ -32,16 +33,22 @@ module Atatus
32
33
  private
33
34
 
34
35
  def build(config)
35
- metadata = Metadata.new(config)
36
+ service = Metadata::ServiceInfo.new(config)
36
37
 
37
38
  [
38
- "atatus-ruby/#{VERSION}",
39
- HTTP::Request::USER_AGENT,
40
- [
41
- metadata.service.runtime.name,
42
- metadata.service.runtime.version
43
- ].join('/')
44
- ].join(' ')
39
+ "atatus-ruby/#{@version}",
40
+ formatted_service_info(service)
41
+ ].compact.join(' ')
42
+ end
43
+
44
+ def formatted_service_info(service)
45
+ if service.name
46
+ "(#{[
47
+ service.name,
48
+ service.version
49
+ ].compact.join(' ')
50
+ })"
51
+ end
45
52
  end
46
53
  end
47
54
  end
@@ -53,11 +53,12 @@ module Atatus
53
53
  end
54
54
 
55
55
  attr_reader :queue, :filters, :name, :connection, :serializers
56
+
56
57
  def work_forever
57
58
  while (msg = queue.pop)
58
59
  case msg
59
60
  when StopMessage
60
- debug 'Stopping worker -- %s', self
61
+ debug 'Stopping worker [%s]', self
61
62
  connection.flush(:halt)
62
63
  break
63
64
  else