appsignal 3.1.2 → 3.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 938b418fbe79e961c55384db366497c427d979ad2d7762f71ca59751483158dc
4
- data.tar.gz: 2e39e33bc84d311184d446d24adf10629e3c62adde66e832cc64870504b1c6d0
3
+ metadata.gz: 16fff6fc92481e9164ad7611fdb1de6d0ebee1d28906ebc95d7fe10100d69cfd
4
+ data.tar.gz: 790ec312c3349ee2b6889d8fca238e6cea7daa537c71cf81b7438152c2a2a2cb
5
5
  SHA512:
6
- metadata.gz: 1dc42a9dc66d1f8feedc65a82225577c5ca5147599fcc32e73657d67ac89a8feebe4a4dce585549d4f2339a652ceec8362d5e01371de0910ad5f7c5b9247640f
7
- data.tar.gz: 2a8b44f0c4c1a7828a6679000ed90a1f143a957757ee842fb59d078134502d88bb7dbc453a5dcf80e55ccaf7e265c191f6a2425853d570a6a48820c9972554f7
6
+ metadata.gz: c20ef4e52d38822a25880f8be5d88ea1f703b7c165813cdcd3d959383c03ff668f47a9c0bab41fe904eb8bf746911d4eb445f90a1804227847172bdc45e80c38
7
+ data.tar.gz: c5705bfeca7ec423ee1888572f84ef70fd15abdb5501cd0c9e73c2d5855b498d7e03ac8c50ab1aa88137d3335376cd2d0b767503c0ec17d9b2c7b209cd04e80f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.1.3
4
+
5
+ ### Added
6
+
7
+ - [811a1082](https://github.com/appsignal/appsignal-ruby/commit/811a10825043ed584f23d870e3a420ee409eb151) patch - Add the `Transaction.current?` helper to determine if any Transaction is currently active or not. AppSignal `NilTransaction`s are not considered active transactions.
8
+
9
+ ### Changed
10
+
11
+ - [dc50d889](https://github.com/appsignal/appsignal-ruby/commit/dc50d8892699bf17b2399865ead8b27ce45b60ed) patch - Rename the (so far privately reported) `gc_total_time` metric to `gc_time`. It no longer reports the total time of Garbage Collection measured, but only the time between two (minutely) measurements.
12
+
13
+ ### Fixed
14
+
15
+ - [7cfed987](https://github.com/appsignal/appsignal-ruby/commit/7cfed98761cf81d475261c553486b24843460cf3) patch - Fix error on unknown HTTP request method. When a request is made with an unknown request method, triggering and `ActionController::UnknownHttpMethod`, it will no longer break the AppSignal instrumentation but omit the request method in the sample data.
16
+
3
17
  ## 3.1.2
4
18
 
5
19
  ### Changed
@@ -325,7 +325,8 @@ module Appsignal
325
325
  "value is not an exception: #{exception.inspect}"
326
326
  return
327
327
  end
328
- return if !active? || Appsignal::Transaction.current.nil?
328
+ return if !active? || !Appsignal::Transaction.current?
329
+
329
330
  transaction = Appsignal::Transaction.current
330
331
  transaction.set_error(exception)
331
332
  transaction.set_tags(tags) if tags
@@ -359,7 +360,7 @@ module Appsignal
359
360
  # @since 2.2.0
360
361
  def set_action(action)
361
362
  return if !active? ||
362
- Appsignal::Transaction.current.nil? ||
363
+ !Appsignal::Transaction.current? ||
363
364
  action.nil?
364
365
  Appsignal::Transaction.current.set_action(action)
365
366
  end
@@ -398,7 +399,7 @@ module Appsignal
398
399
  # @since 2.2.0
399
400
  def set_namespace(namespace)
400
401
  return if !active? ||
401
- Appsignal::Transaction.current.nil? ||
402
+ !Appsignal::Transaction.current? ||
402
403
  namespace.nil?
403
404
  Appsignal::Transaction.current.set_namespace(namespace)
404
405
  end
@@ -438,8 +439,9 @@ module Appsignal
438
439
  # Tagging guide
439
440
  def tag_request(tags = {})
440
441
  return unless active?
442
+ return unless Appsignal::Transaction.current?
443
+
441
444
  transaction = Appsignal::Transaction.current
442
- return false unless transaction
443
445
  transaction.set_tags(tags)
444
446
  end
445
447
  alias :tag_job :tag_request
@@ -471,8 +473,9 @@ module Appsignal
471
473
  # @since 2.12.0
472
474
  def add_breadcrumb(category, action, message = "", metadata = {}, time = Time.now.utc)
473
475
  return unless active?
476
+ return unless Appsignal::Transaction.current?
477
+
474
478
  transaction = Appsignal::Transaction.current
475
- return false unless transaction
476
479
  transaction.add_breadcrumb(category, action, message, metadata, time)
477
480
  end
478
481
 
@@ -20,9 +20,11 @@ module Appsignal
20
20
  module ActiveJobClassInstrumentation
21
21
  def execute(job)
22
22
  job_status = nil
23
- current_transaction = Appsignal::Transaction.current
23
+ has_wrapper_transaction = Appsignal::Transaction.current?
24
24
  transaction =
25
- if current_transaction.nil_transaction?
25
+ if has_wrapper_transaction
26
+ Appsignal::Transaction.current
27
+ else
26
28
  # No standalone integration started before ActiveJob integration.
27
29
  # We don't have a separate integration for this QueueAdapter like
28
30
  # we do for Sidekiq.
@@ -33,8 +35,6 @@ module Appsignal
33
35
  Appsignal::Transaction::BACKGROUND_JOB,
34
36
  Appsignal::Transaction::GenericRequest.new({})
35
37
  )
36
- else
37
- current_transaction
38
38
  end
39
39
 
40
40
  super
@@ -64,7 +64,7 @@ module Appsignal
64
64
  transaction.set_queue_start((Time.parse(enqueued_at).to_f * 1_000).to_i)
65
65
  end
66
66
 
67
- if current_transaction.nil_transaction?
67
+ unless has_wrapper_transaction
68
68
  # Only complete transaction if ActiveJob is not wrapped in
69
69
  # another supported integration, such as Sidekiq.
70
70
  Appsignal::Transaction.complete_current!
@@ -6,8 +6,9 @@ module Appsignal
6
6
  class MongoMonitorSubscriber
7
7
  # Called by Mongo::Monitor when query starts
8
8
  def started(event)
9
+ return unless Appsignal::Transaction.current?
10
+
9
11
  transaction = Appsignal::Transaction.current
10
- return if transaction.nil_transaction?
11
12
  return if transaction.paused?
12
13
 
13
14
  # Format the command
@@ -36,8 +37,9 @@ module Appsignal
36
37
 
37
38
  # Finishes the event in the AppSignal extension
38
39
  def finish(result, event)
40
+ return unless Appsignal::Transaction.current?
41
+
39
42
  transaction = Appsignal::Transaction.current
40
- return if transaction.nil_transaction?
41
43
  return if transaction.paused?
42
44
 
43
45
  # Get the query from the transaction store
@@ -10,21 +10,24 @@ module Appsignal
10
10
  # @api private
11
11
  class SidekiqErrorHandler
12
12
  def call(exception, sidekiq_context)
13
- transaction = Appsignal::Transaction.current
14
-
15
- if transaction.nil_transaction?
16
- # Sidekiq error outside of the middleware scope.
17
- # Can be a job JSON parse error or some other error happening in
18
- # Sidekiq.
19
- transaction = Appsignal::Transaction.create(
20
- SecureRandom.uuid, # Newly generated job id
21
- Appsignal::Transaction::BACKGROUND_JOB,
22
- Appsignal::Transaction::GenericRequest.new({})
23
- )
24
- transaction.set_action_if_nil("SidekiqInternal")
25
- transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
26
- transaction.params = { :jobstr => sidekiq_context[:jobstr] }
27
- end
13
+ transaction =
14
+ if Appsignal::Transaction.current?
15
+ Appsignal::Transaction.current
16
+ else
17
+ # Sidekiq error outside of the middleware scope.
18
+ # Can be a job JSON parse error or some other error happening in
19
+ # Sidekiq.
20
+ transaction =
21
+ Appsignal::Transaction.create(
22
+ SecureRandom.uuid, # Newly generated job id
23
+ Appsignal::Transaction::BACKGROUND_JOB,
24
+ Appsignal::Transaction::GenericRequest.new({})
25
+ )
26
+ transaction.set_action_if_nil("SidekiqInternal")
27
+ transaction.set_metadata("sidekiq_error", sidekiq_context[:context])
28
+ transaction.params = { :jobstr => sidekiq_context[:jobstr] }
29
+ transaction
30
+ end
28
31
 
29
32
  transaction.set_error(exception)
30
33
  Appsignal::Transaction.complete_current!
@@ -31,8 +31,8 @@ module Appsignal
31
31
  )
32
32
 
33
33
  set_gauge("thread_count", Thread.list.size)
34
- gauge_delta(:gc_total_time, @gc_profiler.total_time) do |total_time|
35
- set_gauge("gc_total_time", total_time) if total_time > 0
34
+ gauge_delta(:gc_time, @gc_profiler.total_time) do |gc_time|
35
+ set_gauge("gc_time", gc_time) if gc_time > 0
36
36
  end
37
37
 
38
38
  gc_stats = GC.stat
@@ -40,7 +40,11 @@ module Appsignal
40
40
  end
41
41
  transaction.set_http_or_background_queue_start
42
42
  transaction.set_metadata("path", request.path)
43
- transaction.set_metadata("method", request.request_method)
43
+ begin
44
+ transaction.set_metadata("method", request.request_method)
45
+ rescue => error
46
+ Appsignal.logger.error("Unable to report HTTP request method: '#{error}'")
47
+ end
44
48
  Appsignal::Transaction.complete_current!
45
49
  end
46
50
  end
@@ -35,10 +35,24 @@ module Appsignal
35
35
  end
36
36
  end
37
37
 
38
+ # Returns currently active transaction or a {NilTransaction} if none is
39
+ # active.
40
+ #
41
+ # @see .current?
42
+ # @return [Boolean]
38
43
  def current
39
44
  Thread.current[:appsignal_transaction] || NilTransaction.new
40
45
  end
41
46
 
47
+ # Returns if any transaction is currently active or not. A
48
+ # {NilTransaction} is not considered an active transaction.
49
+ #
50
+ # @see .current
51
+ # @return [Boolean]
52
+ def current?
53
+ current && !current.nil_transaction?
54
+ end
55
+
42
56
  def complete_current!
43
57
  current.complete
44
58
  rescue => e
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Appsignal
4
- VERSION = "3.1.2".freeze
4
+ VERSION = "3.1.3".freeze
5
5
  end
@@ -52,11 +52,11 @@ describe Appsignal::Probes::MriProbe do
52
52
  expect_gauge_value("thread_count")
53
53
  end
54
54
 
55
- it "tracks GC total time" do
55
+ it "tracks GC time between measurements" do
56
56
  expect(gc_profiler_mock).to receive(:total_time).and_return(10, 15)
57
57
  probe.call
58
58
  probe.call
59
- expect_gauge_value("gc_total_time", 5)
59
+ expect_gauge_value("gc_time", 5)
60
60
  end
61
61
 
62
62
  context "when GC total time overflows" do
@@ -66,7 +66,7 @@ describe Appsignal::Probes::MriProbe do
66
66
  probe.call # Report delta value based on cached value
67
67
  probe.call # The value overflows and reports no value. Then stores 0 in the cache
68
68
  probe.call # Report new value based on cache of 0
69
- expect_gauges([["gc_total_time", 5], ["gc_total_time", 10]])
69
+ expect_gauges([["gc_time", 5], ["gc_time", 10]])
70
70
  end
71
71
  end
72
72
 
@@ -3,20 +3,37 @@ if DependencyHelper.rails_present?
3
3
  end
4
4
 
5
5
  describe Appsignal::Rack::RailsInstrumentation do
6
- before :context do
6
+ let(:log) { StringIO.new }
7
+ before do
7
8
  start_agent
9
+ Appsignal.logger = test_logger(log)
8
10
  end
9
11
 
12
+ let(:params) do
13
+ {
14
+ "controller" => "blog_posts",
15
+ "action" => "show",
16
+ "id" => "1",
17
+ "my_custom_param" => "my custom secret",
18
+ "password" => "super secret"
19
+ }
20
+ end
21
+ let(:env_extra) { {} }
10
22
  let(:app) { double(:call => true) }
11
23
  let(:env) do
12
- http_request_env_with_data("action_dispatch.request_id" => "1").tap do |request|
13
- request["action_controller.instance"] = double(
24
+ http_request_env_with_data({
25
+ :params => params,
26
+ :with_queue_start => true,
27
+ "action_dispatch.request_id" => "1",
28
+ "action_dispatch.parameter_filter" => [:my_custom_param, :password],
29
+ "action_controller.instance" => double(
14
30
  :class => MockController,
15
31
  :action_name => "index"
16
32
  )
17
- end
33
+ }.merge(env_extra))
18
34
  end
19
35
  let(:middleware) { Appsignal::Rack::RailsInstrumentation.new(app, {}) }
36
+ around { |example| keep_transactions { example.run } }
20
37
 
21
38
  describe "#call" do
22
39
  before do
@@ -46,30 +63,62 @@ if DependencyHelper.rails_present?
46
63
  after { middleware.call(env) }
47
64
  end
48
65
 
49
- describe "#call_with_appsignal_monitoring", :error => false do
50
- it "should create a transaction" do
51
- expect(Appsignal::Transaction).to receive(:create).with(
52
- "1",
53
- Appsignal::Transaction::HTTP_REQUEST,
54
- kind_of(ActionDispatch::Request),
55
- :params_method => :filtered_parameters
56
- ).and_return(
57
- instance_double(
58
- "Appsignal::Transaction",
59
- :set_action => nil,
60
- :set_action_if_nil => nil,
61
- :set_http_or_background_queue_start => nil,
62
- :set_metadata => nil
66
+ describe "#call_with_appsignal_monitoring" do
67
+ def run
68
+ middleware.call(env)
69
+ end
70
+
71
+ it "calls the wrapped app" do
72
+ run
73
+ expect(app).to have_received(:call).with(env)
74
+ end
75
+
76
+ it "creates one transaction with metadata" do
77
+ run
78
+
79
+ expect(created_transactions.length).to eq(1)
80
+ transaction_hash = last_transaction.to_h
81
+ expect(transaction_hash).to include(
82
+ "namespace" => Appsignal::Transaction::HTTP_REQUEST,
83
+ "action" => "MockController#index",
84
+ "metadata" => hash_including(
85
+ "method" => "GET",
86
+ "path" => "/blog"
87
+ )
88
+ )
89
+ expect(last_transaction.ext.queue_start).to eq(
90
+ fixed_time * 1_000.0
91
+ )
92
+ end
93
+
94
+ it "filter parameters in Rails" do
95
+ run
96
+
97
+ transaction_hash = last_transaction.to_h
98
+ expect(transaction_hash).to include(
99
+ "sample_data" => hash_including(
100
+ "params" => params.merge(
101
+ "my_custom_param" => "[FILTERED]",
102
+ "password" => "[FILTERED]"
103
+ )
63
104
  )
64
105
  )
65
106
  end
66
107
 
67
- it "should call the app" do
68
- expect(app).to receive(:call).with(env)
108
+ context "with an invalid HTTP request method" do
109
+ let(:env_extra) { { :request_method => "FOO", "REQUEST_METHOD" => "FOO" } }
110
+
111
+ it "does not store the HTTP request method" do
112
+ run
113
+
114
+ transaction_hash = last_transaction.to_h
115
+ expect(transaction_hash["metadata"]).to_not have_key("method")
116
+ expect(log_contents(log)).to contains_log(:error, "Unable to report HTTP request method: '")
117
+ end
69
118
  end
70
119
 
71
- context "with an exception", :error => true do
72
- let(:error) { ExampleException }
120
+ context "with an exception" do
121
+ let(:error) { ExampleException.new("ExampleException message") }
73
122
  let(:app) do
74
123
  double.tap do |d|
75
124
  allow(d).to receive(:call).and_raise(error)
@@ -77,21 +126,16 @@ if DependencyHelper.rails_present?
77
126
  end
78
127
 
79
128
  it "records the exception" do
80
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_error).with(error)
81
- end
82
- end
129
+ expect { run }.to raise_error(error)
83
130
 
84
- it "should set metadata" do
85
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_metadata).twice
86
- end
87
-
88
- it "should set the action and queue start" do
89
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_action_if_nil).with("MockController#index")
90
- expect_any_instance_of(Appsignal::Transaction).to receive(:set_http_or_background_queue_start)
131
+ transaction_hash = last_transaction.to_h
132
+ expect(transaction_hash["error"]).to include(
133
+ "name" => "ExampleException",
134
+ "message" => "ExampleException message",
135
+ "backtrace" => kind_of(String)
136
+ )
137
+ end
91
138
  end
92
-
93
- after(:error => false) { middleware.call(env) }
94
- after(:error => true) { expect { middleware.call(env) }.to raise_error(error) }
95
139
  end
96
140
 
97
141
  describe "#request_id" do
@@ -85,7 +85,9 @@ describe Appsignal::Transaction do
85
85
  end
86
86
 
87
87
  describe ".current" do
88
- subject { Appsignal::Transaction.current }
88
+ def current_transaction
89
+ Appsignal::Transaction.current
90
+ end
89
91
 
90
92
  context "when there is a current transaction" do
91
93
  let!(:transaction) do
@@ -93,13 +95,17 @@ describe Appsignal::Transaction do
93
95
  end
94
96
 
95
97
  it "reads :appsignal_transaction from the current Thread" do
96
- expect(subject).to eq Thread.current[:appsignal_transaction]
97
- expect(subject).to eq transaction
98
+ expect(current_transaction).to eq Thread.current[:appsignal_transaction]
99
+ expect(current_transaction).to eq transaction
98
100
  end
99
101
 
100
102
  it "is not a NilTransaction" do
101
- expect(subject.nil_transaction?).to eq false
102
- expect(subject).to be_a Appsignal::Transaction
103
+ expect(current_transaction.nil_transaction?).to eq false
104
+ expect(current_transaction).to be_a Appsignal::Transaction
105
+ end
106
+
107
+ it "returns true for current?" do
108
+ expect(Appsignal::Transaction.current?).to be(true)
103
109
  end
104
110
  end
105
111
 
@@ -109,8 +115,12 @@ describe Appsignal::Transaction do
109
115
  end
110
116
 
111
117
  it "returns a NilTransaction stub" do
112
- expect(subject.nil_transaction?).to eq true
113
- expect(subject).to be_a Appsignal::Transaction::NilTransaction
118
+ expect(current_transaction.nil_transaction?).to eq true
119
+ expect(current_transaction).to be_a Appsignal::Transaction::NilTransaction
120
+ end
121
+
122
+ it "returns false for current?" do
123
+ expect(Appsignal::Transaction.current?).to be(false)
114
124
  end
115
125
  end
116
126
  end
@@ -511,7 +511,14 @@ describe Appsignal do
511
511
  before { allow(Appsignal::Transaction).to receive(:current).and_return(transaction) }
512
512
 
513
513
  context "with transaction" do
514
- let(:transaction) { double }
514
+ let(:transaction) { http_request_transaction }
515
+ around do |example|
516
+ Appsignal.config = project_fixture_config
517
+ set_current_transaction transaction do
518
+ example.run
519
+ end
520
+ end
521
+
515
522
  it "should call add_breadcrumb on transaction" do
516
523
  expect(transaction).to receive(:add_breadcrumb)
517
524
  .with("Network", "http", "User made network request", { :response => 200 }, fixed_time)
@@ -1,7 +1,8 @@
1
1
  module EnvHelpers
2
2
  def http_request_env_with_data(args = {})
3
+ with_queue_start = args.delete(:with_queue_start)
3
4
  path = args.delete(:path) || "/blog"
4
- Rack::MockRequest.env_for(
5
+ request = Rack::MockRequest.env_for(
5
6
  path,
6
7
  :params => args[:params] || {
7
8
  "controller" => "blog_posts",
@@ -18,6 +19,13 @@ module EnvHelpers
18
19
  :db_runtime => 500,
19
20
  :metadata => { :key => "value" }
20
21
  ).merge(args)
22
+
23
+ # Set default queue value
24
+ if with_queue_start
25
+ request["HTTP_X_QUEUE_START"] = "t=#{(fixed_time * 1_000).to_i}" # in milliseconds
26
+ end
27
+
28
+ request
21
29
  end
22
30
 
23
31
  def background_env_with_data(args = {})
@@ -46,8 +46,14 @@ module TransactionHelpers
46
46
 
47
47
  # Set current transaction manually.
48
48
  # Cleared by {clear_current_transaction!}
49
+ #
50
+ # When a block is given, the current transaction is automatically unset after
51
+ # the block.
49
52
  def set_current_transaction(transaction) # rubocop:disable Naming/AccessorMethodName
50
53
  Thread.current[:appsignal_transaction] = transaction
54
+ yield if block_given?
55
+ ensure
56
+ clear_current_transaction! if block_given?
51
57
  end
52
58
 
53
59
  # Use when {Appsignal::Transaction.clear_current_transaction!} is stubbed to
@@ -50,6 +50,17 @@ module Appsignal
50
50
 
51
51
  class Extension
52
52
  class Transaction
53
+ if Appsignal.extension_loaded?
54
+ attr_reader :queue_start
55
+ alias original_set_queue_start set_queue_start
56
+ # Temporary helper until the extension returns this information
57
+ # https://github.com/appsignal/appsignal-agent/issues/293
58
+ def set_queue_start(start) # rubocop:disable Naming/AccessorMethodName
59
+ @queue_start = start
60
+ original_set_queue_start(start)
61
+ end
62
+ end
63
+
53
64
  alias original_finish finish if method_defined? :finish
54
65
 
55
66
  # Override default {Extension::Transaction#finish} behavior to always
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appsignal
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Beekman
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-08-02 00:00:00.000000000 Z
13
+ date: 2022-08-04 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rack