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
  require 'atatus/central_config/cache_control'
@@ -23,21 +40,29 @@ module Atatus
23
40
  def initialize(config)
24
41
  @config = config
25
42
  @modified_options = {}
26
- @service_info = {
27
- 'service.name': config.service_name,
28
- 'service.environment': config.environment
29
- }.to_json
43
+ # @http = Transport::Connection::Http.new(config)
44
+ @etag = 1
30
45
  end
31
46
 
32
47
  attr_reader :config
33
48
  attr_reader :scheduled_task, :promise # for specs
34
49
 
35
50
  def start
51
+ return
36
52
  return unless config.central_config?
37
53
 
54
+ debug 'Starting CentralConfig'
55
+
38
56
  fetch_and_apply_config
39
57
  end
40
58
 
59
+ def stop
60
+ return
61
+ debug 'Stopping CentralConfig'
62
+
63
+ @scheduled_task&.cancel
64
+ end
65
+
41
66
  def fetch_and_apply_config
42
67
  @promise =
43
68
  Concurrent::Promise
@@ -46,11 +71,6 @@ module Atatus
46
71
  .rescue(&method(:handle_error))
47
72
  end
48
73
 
49
- def stop
50
- @scheduled_task&.cancel
51
- end
52
-
53
- # rubocop:disable Metrics/MethodLength
54
74
  def fetch_config
55
75
  resp = perform_request
56
76
 
@@ -65,7 +85,6 @@ module Atatus
65
85
  raise ServerError, resp
66
86
  end
67
87
  end
68
- # rubocop:enable Metrics/MethodLength
69
88
 
70
89
  def assign(update)
71
90
  # For each updated option, store the original value,
@@ -81,20 +100,35 @@ module Atatus
81
100
  update[key] = @modified_options.delete(key)
82
101
  end
83
102
 
84
- config.assign(update)
103
+ @config.replace_options(update)
104
+ end
105
+
106
+ def handle_forking!
107
+ stop
108
+ start
85
109
  end
86
110
 
87
111
  private
88
112
 
89
- # rubocop:disable Metrics/MethodLength
113
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
90
114
  def handle_success(resp)
115
+ if (etag = resp.headers['Etag'])
116
+ @etag = etag
117
+ end
118
+
91
119
  if resp.status == 304
92
120
  info 'Received 304 Not Modified'
93
121
  else
94
- update = JSON.parse(resp.body.to_s)
95
- assign(update)
122
+ if resp.body && !resp.body.empty?
123
+ update = JSON.parse(resp.body.to_s)
124
+ assign(update)
125
+ end
96
126
 
97
- info 'Updated config from Kibana'
127
+ if update && update.any?
128
+ info 'Updated config from Kibana'
129
+ debug 'Modified: %s', update.inspect
130
+ debug 'Modified original options: %s', @modified_options.inspect
131
+ end
98
132
  end
99
133
 
100
134
  schedule_next_fetch(resp)
@@ -104,31 +138,42 @@ module Atatus
104
138
  error 'Failed to apply remote config, %s', e.inspect
105
139
  debug { e.backtrace.join('\n') }
106
140
  end
107
- # rubocop:enable Metrics/MethodLength
141
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
108
142
 
109
143
  def handle_error(error)
110
- error(
144
+ # For tests, WebMock failures don't have real responses
145
+ response = error.response if error.respond_to?(:response)
146
+
147
+ debug(
111
148
  'Failed fetching config: %s, trying again in %d seconds',
112
- error.response.body, DEFAULT_MAX_AGE
149
+ response&.body, DEFAULT_MAX_AGE
113
150
  )
114
151
 
115
152
  assign({})
116
153
 
117
- schedule_next_fetch(error.response)
154
+ schedule_next_fetch(response)
118
155
  end
119
156
 
120
157
  def perform_request
121
- Http.post(
122
- config.server_url + '/agent/v1/config/',
123
- body: @service_info,
124
- headers: { etag: 1, content_type: 'application/json' }
125
- )
158
+ # @http.get(server_url, headers: headers)
159
+ end
160
+
161
+ def server_url
162
+ @server_url ||=
163
+ config.server_url +
164
+ '/config/v1/agents' \
165
+ "?service.name=#{config.service_name}"
166
+ end
167
+
168
+ def headers
169
+ { 'Etag': @etag }
126
170
  end
127
171
 
128
- def schedule_next_fetch(resp)
172
+ def schedule_next_fetch(resp = nil)
173
+ headers = resp&.headers
129
174
  seconds =
130
- if (cache_header = resp.headers['Cache-Control'])
131
- CacheControl.new(cache_header).max_age
175
+ if headers && headers['Cache-Control']
176
+ CacheControl.new(headers['Cache-Control']).max_age
132
177
  else
133
178
  DEFAULT_MAX_AGE
134
179
  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
@@ -26,7 +43,7 @@ module Atatus
26
43
  def parse!(value)
27
44
  value.split(',').each do |token|
28
45
  k, v = token.split('=').map(&:strip)
29
- instance_variable_set(:"@#{k.gsub('-', '_')}", v ? v.to_i : true)
46
+ instance_variable_set(:"@#{k.tr('-', '_')}", v ? v.to_i : true)
30
47
  end
31
48
  end
32
49
  end
@@ -0,0 +1,64 @@
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 ChildDurations
23
+ # @api private
24
+ module Methods
25
+ def child_durations
26
+ @child_durations ||= Durations.new
27
+ end
28
+
29
+ def child_started
30
+ child_durations.start
31
+ end
32
+
33
+ def child_stopped
34
+ child_durations.stop
35
+ end
36
+ end
37
+
38
+ # @api private
39
+ class Durations
40
+ def initialize
41
+ @nesting_level = 0
42
+ @start = nil
43
+ @duration = 0
44
+ @mutex = Mutex.new
45
+ end
46
+
47
+ attr_reader :duration
48
+
49
+ def start
50
+ @mutex.synchronize do
51
+ @nesting_level += 1
52
+ @start = Util.micros if @nesting_level == 1
53
+ end
54
+ end
55
+
56
+ def stop
57
+ @mutex.synchronize do
58
+ @nesting_level -= 1
59
+ @duration = (Util.micros - @start) if @nesting_level == 0
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,7 +1,9 @@
1
1
  require 'json'
2
2
  require 'atatus'
3
3
  require 'atatus/config'
4
+ require 'atatus/child_durations'
4
5
  require 'atatus/transaction'
6
+ require 'atatus/collector/hist'
5
7
  require 'atatus/collector/layer'
6
8
  require 'atatus/collector/transport'
7
9
 
@@ -29,6 +31,7 @@ module Atatus
29
31
 
30
32
  @txns_lock = Mutex.new
31
33
  @txns_agg = {}
34
+ @txn_hist_agg = {}
32
35
  @traces_agg = []
33
36
  @error_metrics_agg = {}
34
37
  @error_requests_agg = []
@@ -39,7 +42,7 @@ module Atatus
39
42
  @metrics_lock = Mutex.new
40
43
  @metrics_agg = []
41
44
 
42
- @transport = Atatus::BaseTransport.new(config)
45
+ @transport = Atatus::BaseTransport.new(config)
43
46
  @collect_counter = 0
44
47
  @running = false
45
48
  end
@@ -69,6 +72,11 @@ module Atatus
69
72
  error "Backtrace:\n" + e.backtrace.join("\n")
70
73
  end
71
74
 
75
+ def handle_forking!
76
+ stop
77
+ start
78
+ end
79
+
72
80
  def add_error(error)
73
81
  ensure_worker_running
74
82
 
@@ -84,7 +92,7 @@ module Atatus
84
92
 
85
93
  def add_metrics(metricset)
86
94
  ensure_worker_running
87
-
95
+
88
96
  metric_added = false
89
97
  metric = {}
90
98
 
@@ -99,13 +107,17 @@ module Atatus
99
107
  metric_added = true
100
108
  end
101
109
 
102
- if %i[ruby.gc.count ruby.threads ruby.heaps.slots.live ruby.heap.slots.free ruby.heap.allocations.total].all? {|s| metricset.samples.key? s}
110
+ if %i[ruby.gc.count ruby.threads ruby.heap.slots.live ruby.heap.slots.free ruby.heap.allocations.total].all? {|s| metricset.samples.key? s}
103
111
  then
104
112
  metric[:'ruby.gc.count'] = metricset.samples[:'ruby.gc.count']
105
113
  metric[:'ruby.threads'] = metricset.samples[:'ruby.threads']
106
114
  metric[:'ruby.heap.slots.live'] = metricset.samples[:'ruby.heap.slots.live']
107
115
  metric[:'ruby.heap.slots.free'] = metricset.samples[:'ruby.heap.slots.free']
108
116
  metric[:'ruby.heap.allocations.total'] = metricset.samples[:'ruby.heap.allocations.total']
117
+ if %i[ruby.gc.time].all? {|s| metricset.samples.key? s}
118
+ then
119
+ metric[:'ruby.gc.time'] = metricset.samples[:'ruby.gc.time']
120
+ end
109
121
  metric_added = true
110
122
  end
111
123
 
@@ -120,10 +132,10 @@ module Atatus
120
132
  ensure_worker_running
121
133
 
122
134
  if
123
- span.transaction_id.nil? ||
124
- span.name.nil? ||
125
- span.type.nil? ||
126
- span.subtype.nil? ||
135
+ span.transaction_id.nil? ||
136
+ span.name.nil? ||
137
+ span.type.nil? ||
138
+ span.subtype.nil? ||
127
139
  span.duration.nil?
128
140
  then
129
141
  return
@@ -135,10 +147,10 @@ module Atatus
135
147
  def add_txn(txn)
136
148
  ensure_worker_running
137
149
 
138
- if
139
- txn.name.nil? ||
140
- txn.id.nil? ||
141
- txn.timestamp.nil? ||
150
+ if
151
+ txn.name.nil? ||
152
+ txn.id.nil? ||
153
+ txn.timestamp.nil? ||
142
154
  txn.duration.nil?
143
155
  then
144
156
  return
@@ -148,14 +160,15 @@ module Atatus
148
160
  return if txn.duration <= 0
149
161
 
150
162
  @txns_lock.synchronize do
151
- if !@txns_agg.key?(txn.name)
152
- txn_type = @config.framework_name || "Ruby"
153
- background = false
154
- if !txn.type.nil?
155
- if txn.type == "Sidekiq"
156
- background = true
157
- end
163
+ txn_type = @config.framework_name || "Ruby"
164
+ background = false
165
+ if !txn.type.nil?
166
+ if txn.type == "Sidekiq"
167
+ background = true
158
168
  end
169
+ end
170
+
171
+ if !@txns_agg.key?(txn.name)
159
172
  @txns_agg[txn.name] = Txn.new(txn_type, "Ruby", txn.duration, background: background)
160
173
  @txns_agg[txn.name].id = txn.id
161
174
  @txns_agg[txn.name].pid = txn.id
@@ -163,17 +176,25 @@ module Atatus
163
176
  @txns_agg[txn.name].aggregate! txn.duration
164
177
  end
165
178
 
179
+ if background == false and txn.duration <= 150*1000*1000.0
180
+ if !@txn_hist_agg.key?(txn.name)
181
+ @txn_hist_agg[txn.name] = TxnHist.new(txn_type, "Ruby", Util.ms(txn.duration))
182
+ else
183
+ @txn_hist_agg[txn.name].aggregate! Util.ms(txn.duration)
184
+ end
185
+ end
186
+
166
187
  spans_present = false
167
188
  ruby_time = 0
168
189
  spans_tuple = []
169
190
  if @spans.key?(txn.id)
170
191
  spans_present = true
171
192
  @spans[txn.id].each do |span|
172
- if
173
- span.name.nil? ||
174
- span.type.nil? ||
175
- span.subtype.nil? ||
176
- span.timestamp.nil? ||
193
+ if
194
+ span.name.nil? ||
195
+ span.type.nil? ||
196
+ span.subtype.nil? ||
197
+ span.timestamp.nil? ||
177
198
  span.duration.nil?
178
199
  then
179
200
  next
@@ -244,9 +265,9 @@ module Atatus
244
265
  @spans.delete(txn.id)
245
266
  end
246
267
 
247
- if
248
- !txn.context.nil? &&
249
- !txn.context.response.nil? &&
268
+ if
269
+ !txn.context.nil? &&
270
+ !txn.context.response.nil? &&
250
271
  !txn.context.response.status_code.nil?
251
272
  then
252
273
  status_code = txn.context.response.status_code
@@ -317,6 +338,7 @@ module Atatus
317
338
  debug '%s: data collector', pid_str
318
339
 
319
340
  txns_data = nil
341
+ txn_hist_data = nil
320
342
  traces_data = nil
321
343
  error_metrics_data = nil
322
344
  error_requests_data = nil
@@ -327,6 +349,9 @@ module Atatus
327
349
  txns_data = @txns_agg
328
350
  @txns_agg = {}
329
351
 
352
+ txn_hist_data = @txn_hist_agg
353
+ @txn_hist_agg = {}
354
+
330
355
  traces_data = @traces_agg
331
356
  @traces_agg = []
332
357
 
@@ -341,7 +366,7 @@ module Atatus
341
366
  errors_data = @errors_aggs
342
367
  @errors_aggs = []
343
368
  end
344
-
369
+
345
370
  @metrics_lock.synchronize do
346
371
  metrics_data = @metrics_agg
347
372
  @metrics_agg = []
@@ -349,7 +374,14 @@ module Atatus
349
374
 
350
375
  @transport.txns(start_time, end_time, txns_data) unless txns_data.empty?
351
376
 
352
- @transport.traces(start_time, end_time, traces_data) unless traces_data.empty?
377
+ @transport.txn_hist(start_time, end_time, txn_hist_data) unless txn_hist_data.empty?
378
+
379
+ unless traces_data.empty?
380
+ traces_data.each do |trace|
381
+ individual_trace_data = [trace]
382
+ @transport.traces(start_time, end_time, individual_trace_data)
383
+ end
384
+ end
353
385
 
354
386
  @transport.error_metrics(start_time, end_time, error_metrics_data, error_requests_data) unless error_metrics_data.empty?
355
387
 
@@ -359,4 +391,4 @@ module Atatus
359
391
  end
360
392
  end
361
393
  end
362
- end
394
+ end