scout_apm 3.0.0.pre26 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +49 -0
  3. data/.gitignore +1 -1
  4. data/.rubocop.yml +5 -5
  5. data/.travis.yml +19 -14
  6. data/CHANGELOG.markdown +143 -4
  7. data/Gemfile +1 -7
  8. data/README.markdown +13 -4
  9. data/Rakefile +1 -1
  10. data/ext/allocations/allocations.c +2 -0
  11. data/gems/README.md +28 -0
  12. data/gems/octoshark.gemfile +4 -0
  13. data/gems/rails3.gemfile +5 -0
  14. data/gems/rails4.gemfile +4 -0
  15. data/gems/rails5.gemfile +4 -0
  16. data/gems/rails6.gemfile +4 -0
  17. data/lib/scout_apm.rb +39 -9
  18. data/lib/scout_apm/agent.rb +29 -10
  19. data/lib/scout_apm/agent/exit_handler.rb +0 -1
  20. data/lib/scout_apm/agent_context.rb +22 -3
  21. data/lib/scout_apm/app_server_load.rb +7 -2
  22. data/lib/scout_apm/attribute_arranger.rb +0 -2
  23. data/lib/scout_apm/auto_instrument.rb +5 -0
  24. data/lib/scout_apm/auto_instrument/instruction_sequence.rb +31 -0
  25. data/lib/scout_apm/auto_instrument/layer.rb +23 -0
  26. data/lib/scout_apm/auto_instrument/parser.rb +27 -0
  27. data/lib/scout_apm/auto_instrument/rails.rb +175 -0
  28. data/lib/scout_apm/background_job_integrations/legacy_sneakers.rb +55 -0
  29. data/lib/scout_apm/background_job_integrations/que.rb +134 -0
  30. data/lib/scout_apm/background_job_integrations/resque.rb +6 -2
  31. data/lib/scout_apm/background_job_integrations/shoryuken.rb +124 -0
  32. data/lib/scout_apm/background_job_integrations/sidekiq.rb +5 -19
  33. data/lib/scout_apm/background_job_integrations/sneakers.rb +87 -0
  34. data/lib/scout_apm/config.rb +45 -8
  35. data/lib/scout_apm/detailed_trace.rb +217 -0
  36. data/lib/scout_apm/environment.rb +19 -1
  37. data/lib/scout_apm/error.rb +27 -0
  38. data/lib/scout_apm/error_service.rb +32 -0
  39. data/lib/scout_apm/error_service/error_buffer.rb +39 -0
  40. data/lib/scout_apm/error_service/error_record.rb +211 -0
  41. data/lib/scout_apm/error_service/ignored_exceptions.rb +66 -0
  42. data/lib/scout_apm/error_service/middleware.rb +32 -0
  43. data/lib/scout_apm/error_service/notifier.rb +33 -0
  44. data/lib/scout_apm/error_service/payload.rb +47 -0
  45. data/lib/scout_apm/error_service/periodic_work.rb +17 -0
  46. data/lib/scout_apm/error_service/railtie.rb +11 -0
  47. data/lib/scout_apm/error_service/sidekiq.rb +80 -0
  48. data/lib/scout_apm/extensions/transaction_callback_payload.rb +1 -1
  49. data/lib/scout_apm/fake_store.rb +3 -0
  50. data/lib/scout_apm/framework_integrations/rails_2.rb +2 -1
  51. data/lib/scout_apm/framework_integrations/rails_3_or_4.rb +3 -1
  52. data/lib/scout_apm/git_revision.rb +6 -3
  53. data/lib/scout_apm/instant/middleware.rb +2 -1
  54. data/lib/scout_apm/instrument_manager.rb +9 -7
  55. data/lib/scout_apm/instruments/action_controller_rails_2.rb +3 -1
  56. data/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb +56 -55
  57. data/lib/scout_apm/instruments/action_view.rb +126 -26
  58. data/lib/scout_apm/instruments/active_record.rb +66 -18
  59. data/lib/scout_apm/instruments/http.rb +48 -0
  60. data/lib/scout_apm/instruments/memcached.rb +43 -0
  61. data/lib/scout_apm/instruments/mongoid.rb +9 -4
  62. data/lib/scout_apm/instruments/net_http.rb +8 -1
  63. data/lib/scout_apm/instruments/typhoeus.rb +87 -0
  64. data/lib/scout_apm/job_record.rb +4 -2
  65. data/lib/scout_apm/layaway_file.rb +4 -0
  66. data/lib/scout_apm/layer.rb +6 -57
  67. data/lib/scout_apm/layer_children_set.rb +9 -8
  68. data/lib/scout_apm/layer_converters/converter_base.rb +15 -30
  69. data/lib/scout_apm/layer_converters/database_converter.rb +2 -15
  70. data/lib/scout_apm/layer_converters/slow_job_converter.rb +12 -2
  71. data/lib/scout_apm/layer_converters/slow_request_converter.rb +14 -4
  72. data/lib/scout_apm/layer_converters/trace_converter.rb +184 -0
  73. data/lib/scout_apm/limited_layer.rb +0 -7
  74. data/lib/scout_apm/metric_stats.rb +0 -8
  75. data/lib/scout_apm/middleware.rb +1 -1
  76. data/lib/scout_apm/periodic_work.rb +19 -0
  77. data/lib/scout_apm/remote/message.rb +4 -0
  78. data/lib/scout_apm/remote/server.rb +13 -1
  79. data/lib/scout_apm/reporter.rb +8 -3
  80. data/lib/scout_apm/reporting.rb +2 -1
  81. data/lib/scout_apm/request_histograms.rb +8 -0
  82. data/lib/scout_apm/serializers/app_server_load_serializer.rb +4 -0
  83. data/lib/scout_apm/serializers/directive_serializer.rb +4 -0
  84. data/lib/scout_apm/serializers/payload_serializer.rb +2 -2
  85. data/lib/scout_apm/serializers/payload_serializer_to_json.rb +30 -15
  86. data/lib/scout_apm/slow_job_record.rb +5 -1
  87. data/lib/scout_apm/slow_policy/age_policy.rb +33 -0
  88. data/lib/scout_apm/slow_policy/percent_policy.rb +22 -0
  89. data/lib/scout_apm/slow_policy/percentile_policy.rb +24 -0
  90. data/lib/scout_apm/slow_policy/policy.rb +21 -0
  91. data/lib/scout_apm/slow_policy/speed_policy.rb +16 -0
  92. data/lib/scout_apm/slow_request_policy.rb +18 -77
  93. data/lib/scout_apm/slow_transaction.rb +3 -1
  94. data/lib/scout_apm/store.rb +12 -8
  95. data/lib/scout_apm/tracked_request.rb +39 -30
  96. data/lib/scout_apm/utils/active_record_metric_name.rb +16 -3
  97. data/lib/scout_apm/utils/backtrace_parser.rb +3 -0
  98. data/lib/scout_apm/utils/marshal_logging.rb +90 -0
  99. data/lib/scout_apm/utils/sql_sanitizer.rb +10 -1
  100. data/lib/scout_apm/utils/sql_sanitizer_regex.rb +8 -1
  101. data/lib/scout_apm/utils/sql_sanitizer_regex_1_8_7.rb +6 -0
  102. data/lib/scout_apm/utils/unique_id.rb +27 -0
  103. data/lib/scout_apm/version.rb +1 -1
  104. data/scout_apm.gemspec +13 -7
  105. data/test/test_helper.rb +2 -2
  106. data/test/unit/agent_context_test.rb +29 -0
  107. data/test/unit/auto_instrument/assignments-instrumented.rb +31 -0
  108. data/test/unit/auto_instrument/assignments.rb +31 -0
  109. data/test/unit/auto_instrument/controller-ast.txt +57 -0
  110. data/test/unit/auto_instrument/controller-instrumented.rb +49 -0
  111. data/test/unit/auto_instrument/controller.rb +49 -0
  112. data/test/unit/auto_instrument/rescue_from-instrumented.rb +13 -0
  113. data/test/unit/auto_instrument/rescue_from.rb +13 -0
  114. data/test/unit/auto_instrument_test.rb +54 -0
  115. data/test/unit/environment_test.rb +2 -2
  116. data/test/unit/error_service/error_buffer_test.rb +25 -0
  117. data/test/unit/error_service/ignored_exceptions_test.rb +49 -0
  118. data/test/unit/instruments/active_record_test.rb +40 -0
  119. data/test/unit/layer_children_set_test.rb +9 -0
  120. data/test/unit/request_histograms_test.rb +17 -0
  121. data/test/unit/serializers/payload_serializer_test.rb +39 -5
  122. data/test/unit/slow_request_policy_test.rb +41 -13
  123. data/test/unit/sql_sanitizer_test.rb +78 -0
  124. data/test/unit/utils/active_record_metric_name_test.rb +10 -2
  125. metadata +100 -18
  126. data/ext/stacks/extconf.rb +0 -37
  127. data/ext/stacks/scout_atomics.h +0 -86
  128. data/ext/stacks/stacks.c +0 -814
  129. data/lib/scout_apm/slow_job_policy.rb +0 -111
  130. data/lib/scout_apm/trace_compactor.rb +0 -312
  131. data/lib/scout_apm/utils/fake_stacks.rb +0 -88
  132. data/test/unit/instruments/active_record_instruments_test.rb +0 -5
  133. data/test/unit/slow_job_policy_test.rb +0 -6
  134. data/tester.rb +0 -53
@@ -0,0 +1,184 @@
1
+ module ScoutApm
2
+ module LayerConverters
3
+ class TraceConverter < ConverterBase
4
+ ###################
5
+ # Converter API #
6
+ ###################
7
+
8
+
9
+ def record!
10
+ @points = context.slow_request_policy.score(request)
11
+
12
+ # Let the store know we're here, and if it wants our data, it will call
13
+ # back into #call
14
+ @store.track_trace!(self)
15
+
16
+ nil # not returning anything in the layer results ... not used
17
+ end
18
+
19
+ #####################
20
+ # ScoreItemSet API #
21
+ #####################
22
+ def name; request.unique_name; end
23
+ def score; @points; end
24
+
25
+ # Unconditionally attempts to convert this into a DetailedTrace object.
26
+ # Can return nil if the request didn't have any scope_layer or if `timeline_traces` aren't enabled.
27
+ def call
28
+ return nil unless scope_layer
29
+ return nil unless context.config.value('timeline_traces')
30
+
31
+ # Since this request is being stored, update the needed counters
32
+ context.slow_request_policy.stored!(request)
33
+
34
+ # record the change in memory usage
35
+ mem_delta = ScoutApm::Instruments::Process::ProcessMemory.new(context).rss_to_mb(@request.capture_mem_delta!)
36
+
37
+ transaction_id = request.transaction_id
38
+ revision = context.environment.git_revision.sha
39
+ start_instant = request.root_layer.start_time
40
+ stop_instant = request.root_layer.stop_time
41
+ type = if request.web?
42
+ "Web"
43
+ elsif request.job?
44
+ "Job"
45
+ else
46
+ "Unknown"
47
+ end
48
+
49
+ # Create request tags
50
+ #
51
+ tags = {
52
+ :allocations => request.root_layer.total_allocations,
53
+ :mem_delta => mem_delta,
54
+ }.merge(request.context.to_flat_hash)
55
+
56
+ host = context.environment.hostname
57
+ path = request.annotations[:uri] || ""
58
+ code = "" # User#index for instance
59
+
60
+ spans = create_spans(request.root_layer)
61
+ if limited?
62
+ tags[:"scout.reached_span_cap"] = true
63
+ end
64
+
65
+ DetailedTrace.new(
66
+ transaction_id,
67
+ revision,
68
+ host,
69
+ start_instant,
70
+ stop_instant,
71
+ type,
72
+
73
+ path,
74
+ code,
75
+
76
+ spans,
77
+ tags
78
+
79
+ # total_score = 0,
80
+ # percentile_score = 0,
81
+ # age_score = 0,
82
+ # memory_delta_score = 0,
83
+ # memory_allocations_score = 0
84
+ )
85
+ end
86
+
87
+ # Returns an array of span objects. Uses recursion to get all children
88
+ # wired up w/ correct parent_ids
89
+ def create_spans(layer, parent_id = nil)
90
+ span_id = ScoutApm::Utils::SpanId.new.to_s
91
+
92
+ start_instant = layer.start_time
93
+ stop_instant = layer.stop_time
94
+ operation = layer.legacy_metric_name
95
+ tags = {
96
+ :start_allocations => layer.allocations_start,
97
+ :stop_allocations => layer.allocations_stop,
98
+ }
99
+ if layer.desc
100
+ tags[:desc] = layer.desc.to_s
101
+ end
102
+ if layer.annotations && layer.annotations[:record_count]
103
+ tags["db.record_count"] = layer.annotations[:record_count]
104
+ end
105
+ if layer.annotations && layer.annotations[:class_name]
106
+ tags["db.class_name"] = layer.annotations[:class_name]
107
+ end
108
+ if layer.backtrace
109
+ tags[:backtrace] = backtrace_parser(layer.backtrace) rescue nil
110
+ end
111
+
112
+ # Collect up self, and all children into result array
113
+ result = []
114
+ result << DetailedTraceSpan.new(
115
+ span_id.to_s,
116
+ parent_id.to_s,
117
+ start_instant,
118
+ stop_instant,
119
+ operation,
120
+ tags)
121
+
122
+ layer.children.each do |child|
123
+ # Don't create spans from limited layers. These don't have start/stop times and our processing can't
124
+ # handle these yet.
125
+ unless over_span_limit?(result) || child.is_a?(LimitedLayer)
126
+ result += create_spans(child, span_id)
127
+ end
128
+ end
129
+
130
+ return result
131
+ end
132
+
133
+ # Take an array of ruby backtrace lines and split it into an array of hashes like:
134
+ # ["/Users/cschneid/.rvm/rubies/ruby-2.2.7/lib/ruby/2.2.0/irb/workspace.rb:86:in `eval'", ...]
135
+ # turns into:
136
+ # [ {
137
+ # "file": "app/controllers/users_controller.rb",
138
+ # "line": 10,
139
+ # "function": "index"
140
+ # },
141
+ # ]
142
+ def backtrace_parser(lines)
143
+ bt = ScoutApm::Utils::BacktraceParser.new(lines).call
144
+
145
+ bt.map do |line|
146
+ match = line.match(/(.*):(\d+):in `(.*)'/)
147
+ {
148
+ "file" => match[1],
149
+ "line" => match[2],
150
+ "function" => match[3],
151
+ }
152
+ end
153
+
154
+ end
155
+
156
+ ################################################################################
157
+ # Limit Handling
158
+ ################################################################################
159
+
160
+ # To prevent huge traces from being generated, we stop collecting
161
+ # spans as we go beyond some reasonably large count.
162
+ MAX_SPANS = 1500
163
+
164
+ def over_span_limit?(spans)
165
+ if spans.size > MAX_SPANS
166
+ log_over_span_limit
167
+ @limited = true
168
+ else
169
+ false
170
+ end
171
+ end
172
+
173
+ def log_over_span_limit
174
+ unless limited?
175
+ context.logger.debug "Not recording additional spans for #{name}. Over the span limit."
176
+ end
177
+ end
178
+
179
+ def limited?
180
+ !! @limited
181
+ end
182
+ end
183
+ end
184
+ end
@@ -85,13 +85,6 @@ module ScoutApm
85
85
  nil
86
86
  end
87
87
 
88
- def add_traces(*)
89
- # noop
90
- end
91
-
92
- def traces
93
- ScoutApm::TraceSet.new
94
- end
95
88
 
96
89
  #######################################################################
97
90
  # Many methods don't make any sense on a limited layer. Raise errors #
@@ -9,7 +9,6 @@ class MetricStats
9
9
  attr_accessor :sum_of_squares
10
10
  attr_accessor :queue
11
11
  attr_accessor :latency
12
- attr_accessor :traces
13
12
 
14
13
  def initialize(scoped = false)
15
14
  @scoped = scoped
@@ -19,7 +18,6 @@ class MetricStats
19
18
  self.min_call_time = 0.0
20
19
  self.max_call_time = 0.0
21
20
  self.sum_of_squares = 0.0
22
- self.traces = []
23
21
  end
24
22
 
25
23
  # Note, that you must include exclusive_time if you wish to set
@@ -41,10 +39,6 @@ class MetricStats
41
39
  self
42
40
  end
43
41
 
44
- def add_traces(traces)
45
- self.traces += Array(traces)
46
- end
47
-
48
42
  # combines data from another MetricStats object
49
43
  def combine!(other)
50
44
  self.call_count += other.call_count
@@ -53,7 +47,6 @@ class MetricStats
53
47
  self.min_call_time = other.min_call_time if self.min_call_time.zero? or other.min_call_time < self.min_call_time
54
48
  self.max_call_time = other.max_call_time if other.max_call_time > self.max_call_time
55
49
  self.sum_of_squares += other.sum_of_squares
56
- self.traces = Array(self.traces) + Array(other.traces)
57
50
  self
58
51
  end
59
52
 
@@ -64,7 +57,6 @@ class MetricStats
64
57
  :min_call_time,
65
58
  :total_call_time,
66
59
  :total_exclusive_time,
67
- :traces,
68
60
  ]
69
61
  ScoutApm::AttributeArranger.call(self, json_attributes)
70
62
  end
@@ -26,7 +26,7 @@ module ScoutApm
26
26
  ScoutApm::Agent.instance.start
27
27
  @started = ScoutApm::Agent.instance.context.started? && ScoutApm::Agent.instance.background_worker_running?
28
28
  rescue => e
29
- ScoutApm::Agent.instance.context.logger("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
29
+ ScoutApm::Agent.instance.context.logger.info("Failed to start via Middleware: #{e.message}\n\t#{e.backtrace.join("\n\t")}")
30
30
  end
31
31
  end
32
32
  end
@@ -12,10 +12,29 @@ module ScoutApm
12
12
  ScoutApm::Debug.instance.call_periodic_hooks
13
13
  @reporting.process_metrics
14
14
  clean_old_percentiles
15
+
16
+ if context.config.value('auto_instruments')
17
+ log_autoinstrument_significant_counts rescue nil
18
+ end
15
19
  end
16
20
 
17
21
  private
18
22
 
23
+ def log_autoinstrument_significant_counts
24
+ # Ex key/value -
25
+ # "/Users/dlite/projects/scout/apm/app/controllers/application_controller.rb"=>[[0.0, 689], [1.0, 16]]
26
+ hists = context.auto_instruments_layer_histograms.as_json
27
+ hists_summary = hists.map { |file, buckets|
28
+ total = buckets.map(&:last).inject(0) { |sum, count| sum + count }
29
+ significant = (buckets.last.last / total.to_f).round(2)
30
+ [
31
+ file,
32
+ {:total => total, :significant => significant}
33
+ ]
34
+ }.to_h
35
+ context.logger.debug("AutoInstrument Significant Layer Histograms: #{hists_summary.pretty_inspect}")
36
+ end
37
+
19
38
  # XXX: Move logic into a RequestHistogramsByTime class that can keep the timeout logic in it
20
39
  def clean_old_percentiles
21
40
  context.
@@ -17,6 +17,10 @@ module ScoutApm
17
17
 
18
18
  def encode
19
19
  Marshal.dump(self)
20
+ rescue
21
+ ScoutApm::Agent.instance.logger.info("Failed Marshalling Remote::Message")
22
+ ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(self).dive) rescue nil
23
+ raise
20
24
  end
21
25
  end
22
26
  end
@@ -16,8 +16,20 @@ module ScoutApm
16
16
  @server = nil
17
17
  end
18
18
 
19
- def start
19
+ def require_webrick
20
20
  require 'webrick'
21
+ true
22
+ rescue LoadError
23
+ @logger.warn(
24
+ %q|Could not require Webrick. Ruby 3.0 stopped bundling it
25
+ automatically, but it is required to instrument Resque. Please add
26
+ Webrick to your Gemfile.|
27
+ )
28
+ false
29
+ end
30
+
31
+ def start
32
+ return false unless require_webrick
21
33
 
22
34
  @server = WEBrick::HTTPServer.new(
23
35
  :BindAddress => bind,
@@ -2,7 +2,6 @@ require 'openssl'
2
2
 
3
3
  module ScoutApm
4
4
  class Reporter
5
- CA_FILE = File.join( File.dirname(__FILE__), *%w[.. .. data cacert.pem] )
6
5
  VERIFY_MODE = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
7
6
 
8
7
  attr_reader :type
@@ -23,6 +22,7 @@ module ScoutApm
23
22
  context.logger
24
23
  end
25
24
 
25
+ # The fully serialized string payload to be sent
26
26
  def report(payload, headers = {})
27
27
  hosts = determine_hosts
28
28
 
@@ -36,6 +36,7 @@ module ScoutApm
36
36
  logger.debug("Original Size: #{original_payload_size} Compressed Size: #{compress_payload_size}")
37
37
  end
38
38
 
39
+ logger.info("Posting payload to #{hosts.inspect}")
39
40
  post_payload(hosts, payload, headers)
40
41
  end
41
42
 
@@ -52,6 +53,8 @@ module ScoutApm
52
53
  URI.parse("#{host}/apps/deploy.scout?key=#{key}&name=#{encoded_app_name}")
53
54
  when :instant_trace
54
55
  URI.parse("#{host}/apps/instant_trace.scout?key=#{key}&name=#{encoded_app_name}&instant_key=#{instant_key}")
56
+ when :errors
57
+ URI.parse("#{host}/apps/error.scout?key=#{key}&name=#{encoded_app_name}")
55
58
  end.tap { |u| logger.debug("Posting to #{u}") }
56
59
  end
57
60
 
@@ -90,7 +93,7 @@ module ScoutApm
90
93
  logger.debug "got response: #{response.inspect}"
91
94
  case response
92
95
  when Net::HTTPSuccess, Net::HTTPNotModified
93
- logger.debug "/#{type} OK"
96
+ logger.debug "#{type} OK"
94
97
  when Net::HTTPBadRequest
95
98
  logger.warn "/#{type} FAILED: The Account Key [#{config.value('key')}] is invalid."
96
99
  when Net::HTTPUnprocessableEntity
@@ -123,7 +126,7 @@ module ScoutApm
123
126
  proxy_uri.password).new(url.host, url.port)
124
127
  if url.is_a?(URI::HTTPS)
125
128
  http.use_ssl = true
126
- http.ca_file = CA_FILE
129
+ http.ca_file = config.value("ssl_cert_file")
127
130
  http.verify_mode = VERIFY_MODE
128
131
  end
129
132
  http
@@ -142,6 +145,8 @@ module ScoutApm
142
145
  def determine_hosts
143
146
  if [:deploy_hook, :instant_trace].include?(type)
144
147
  config.value('direct_host')
148
+ elsif [:errors].include?(type)
149
+ config.value('errors_host')
145
150
  else
146
151
  config.value('host')
147
152
  end
@@ -83,10 +83,11 @@ module ScoutApm
83
83
  slow_jobs = reporting_period.slow_jobs_payload
84
84
  histograms = reporting_period.histograms
85
85
  db_query_metrics = reporting_period.db_query_metrics_payload
86
+ traces = (slow_transactions.map(&:span_trace) + slow_jobs.map(&:span_trace)).compact
86
87
 
87
88
  log_deliver(metrics, slow_transactions, metadata, slow_jobs, histograms)
88
89
 
89
- payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
90
+ payload = ScoutApm::Serializers::PayloadSerializer.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
90
91
  logger.debug("Sending payload w/ Headers: #{headers.inspect}")
91
92
 
92
93
  reporter.report(payload, headers)
@@ -22,6 +22,14 @@ module ScoutApm
22
22
  @histograms.keys.each { |n| yield n }
23
23
  end
24
24
 
25
+ def as_json
26
+ Hash[
27
+ @histograms.map{ |key, histogram|
28
+ [key, histogram.as_json]
29
+ }
30
+ ]
31
+ end
32
+
25
33
  def add(item, value)
26
34
  @histograms[item].add(value)
27
35
  end
@@ -5,6 +5,10 @@ module ScoutApm
5
5
  class AppServerLoadSerializer
6
6
  def self.serialize(data)
7
7
  Marshal.dump(data)
8
+ rescue
9
+ ScoutApm::Agent.instance.logger.info("Failed Marshalling AppServerLoad")
10
+ ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
11
+ raise
8
12
  end
9
13
 
10
14
  def self.deserialize(data)
@@ -5,6 +5,10 @@ module ScoutApm
5
5
  class DirectiveSerializer
6
6
  def self.serialize(data)
7
7
  Marshal.dump(data)
8
+ rescue
9
+ ScoutApm::Agent.instance.logger.info("Failed Marshalling Directive")
10
+ ScoutApm::Agent.instance.logger.info(ScoutApm::Utils::MarshalLogging.new(data).dive) rescue nil
11
+ raise
8
12
  end
9
13
 
10
14
  def self.deserialize(data)
@@ -2,9 +2,9 @@
2
2
  module ScoutApm
3
3
  module Serializers
4
4
  class PayloadSerializer
5
- def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
5
+ def self.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
6
6
  if ScoutApm::Agent.instance.context.config.value("report_format") == 'json'
7
- ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
7
+ ScoutApm::Serializers::PayloadSerializerToJson.serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
8
8
  else
9
9
  metadata = metadata.dup
10
10
  metadata.default = nil
@@ -2,7 +2,7 @@ module ScoutApm
2
2
  module Serializers
3
3
  module PayloadSerializerToJson
4
4
  class << self
5
- def serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics)
5
+ def serialize(metadata, metrics, slow_transactions, jobs, slow_jobs, histograms, db_query_metrics, traces)
6
6
  metadata.merge!({:payload_version => 2})
7
7
 
8
8
  jsonify_hash({:metadata => metadata,
@@ -14,6 +14,7 @@ module ScoutApm
14
14
  :db_metrics => {
15
15
  :query => DbQuerySerializerToJson.new(db_query_metrics).as_json,
16
16
  },
17
+ :span_traces => traces.map{ |t| t.as_json },
17
18
  })
18
19
  end
19
20
 
@@ -44,18 +45,36 @@ module ScoutApm
44
45
  "{#{str_parts.join(",")}}"
45
46
  end
46
47
 
47
- ESCAPE_MAPPINGS = {
48
- "\b" => '\\b',
49
- "\t" => '\\t',
50
- "\n" => '\\n',
51
- "\f" => '\\f',
52
- "\r" => '\\r',
53
- '"' => '\\"',
54
- '\\' => '\\\\',
55
- }
48
+ # Ruby 1.8.7 seems to be fundamentally different in how gsub or regexes
49
+ # work. This is a hack and will be removed as soon as we can drop
50
+ # support
51
+ if RUBY_VERSION == "1.8.7"
52
+ ESCAPE_MAPPINGS = {
53
+ "\b" => '\\b',
54
+ "\t" => '\\t',
55
+ "\n" => '\\n',
56
+ "\f" => '\\f',
57
+ "\r" => '\\r',
58
+ '"' => '\\"',
59
+ '\\' => '\\\\',
60
+ }
61
+ else
62
+ ESCAPE_MAPPINGS = {
63
+ # Stackoverflow answer on gsub matches and backslashes - https://stackoverflow.com/a/4149087/2705125
64
+ '\\' => '\\\\\\\\',
65
+ "\b" => '\\b',
66
+ "\t" => '\\t',
67
+ "\n" => '\\n',
68
+ "\f" => '\\f',
69
+ "\r" => '\\r',
70
+ '"' => '\\"',
71
+ }
72
+ end
56
73
 
57
74
  def escape(string)
58
- ESCAPE_MAPPINGS.inject(string.to_s) {|s, (bad, good)| s.gsub(bad, good) }
75
+ ESCAPE_MAPPINGS.inject(string.to_s) {|s, (bad, good)|
76
+ s.gsub(bad, good)
77
+ }
59
78
  end
60
79
 
61
80
  def format_by_type(formatee)
@@ -71,10 +90,6 @@ module ScoutApm
71
90
  %Q["#{formatee.iso8601}"]
72
91
  when nil
73
92
  "null"
74
- when TrueClass
75
- "true"
76
- when FalseClass
77
- "false"
78
93
  else # strings and everything
79
94
  %Q["#{escape(formatee)}"]
80
95
  end