appsignal 3.5.6 → 3.6.1

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: be7696e045cc16b22ae798290afadb6e15a95b2da220f8c04af271aaa96020ff
4
- data.tar.gz: cba7acfc22ae7ac666ab8ae5ac82949dfd9b47fee07df7a6671022ddcec4f833
3
+ metadata.gz: 8990517cea95c50e6516636e35d845bfeab2d2fa936d356be9b3f2b976b9fc7a
4
+ data.tar.gz: 7729d6172b3f0df044241548f347e2d22bdd0b295454a9bae93cfbc6f72119db
5
5
  SHA512:
6
- metadata.gz: dc1f8e1846c73d714d38815066447433aaf0e3ad5116ead7ec28508b48aee24c859faed0f9be2b46b49ae00aedf6402d938369321d0741f40003a05ac88e695c
7
- data.tar.gz: 166b565dd6dca8275105cabd5c2853b8558f724dced55e0fa810a9eae16a4fba2fbc437545faeac6cfc1fdee25d524590a13d341ad69712bb56508e7aef815bc
6
+ metadata.gz: b866452cb869cf8da793720d18f65927cb867eeb150b299d25c23ea6d417638735c3fc871bcf4218cabb047b93913e0051d9880b957a3c17f1ff4de93debdc09
7
+ data.tar.gz: 252ed529380e877ba34d3a5b7d921f042835e5d9baab95010fda67d195aa0ee647895f8d0c12a18a086bfc7594179155c83c3635eb3afd66ef265a3fa484a36b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 3.6.1
4
+
5
+ _Published on 2024-03-05._
6
+
7
+ ### Added
8
+
9
+ - [8974d201](https://github.com/appsignal/appsignal-ruby/commit/8974d20144407fce7a274ebaeb771ef76705d901) patch - Add `activejob_report_errors` config option. When set to `"none"`, ActiveJob jobs will no longer report errors. This can be used in combination with [custom exception reporting](https://docs.appsignal.com/ruby/instrumentation/exception-handling.html). By default, the config option has the value `"all"`, which reports all errors.
10
+
11
+ ## 3.6.0
12
+
13
+ _Published on 2024-02-26._
14
+
15
+ ### Added
16
+
17
+ - [9984156f](https://github.com/appsignal/appsignal-ruby/commit/9984156faea0a76cb0fe81594e1ddf40d55dabbe) minor - Add instrumentation for all Rack responses, including streaming responses. New `response_body_each.rack`, `response_body_call.rack` and `response_body_to_ary.rack` events will be shown in the event timeline. This will show how long it takes to complete responses, depending on the response implementation.
18
+
19
+ This Sinatra route with a streaming response will be better instrumented, for example:
20
+
21
+ ```ruby
22
+ get "/stream" do
23
+ stream do |out|
24
+ sleep 1
25
+ out << "1"
26
+ sleep 1
27
+ out << "2"
28
+ sleep 1
29
+ out << "3"
30
+ end
31
+ end
32
+ ```
33
+ - [e7706038](https://github.com/appsignal/appsignal-ruby/commit/e7706038d8b2f52ea90441cfa62d5ee867d893a2) patch - Add histogram support to the OpenTelemetry HTTP server. This allows OpenTelemetry-based instrumentations to report histogram data to AppSignal as distribution metrics.
34
+
35
+ ### Changed
36
+
37
+ - [11220302](https://github.com/appsignal/appsignal-ruby/commit/112203023a58e53e607a9fd7d545044fa7d896d5) minor - **Breaking change**: Normalize CPU metrics for cgroups v1 systems. When we can detect how many CPUs are configured in the container's limits, we will normalize the CPU percentages to a maximum of 100%. This is a breaking change. Triggers for CPU percentages that are configured for a CPU percentage higher than 100% will no longer trigger after this update. Please configure triggers to a percentage with a maximum of 100% CPU percentage.
38
+ - [11220302](https://github.com/appsignal/appsignal-ruby/commit/112203023a58e53e607a9fd7d545044fa7d896d5) patch - Support fractional CPUs for cgroups v2 metrics. Previously a CPU count of 0.5 would be interpreted as 1 CPU. Now it will be correctly seen as half a CPU and calculate CPU percentages accordingly.
39
+ - [14aefc35](https://github.com/appsignal/appsignal-ruby/commit/14aefc3594b3f55a4c2ab14ba1259a4f10499467) patch - Update bundled trusted root certificates.
40
+
41
+ ### Fixed
42
+
43
+ - [f2abbd6a](https://github.com/appsignal/appsignal-ruby/commit/f2abbd6aeb2230d79139cbdf82af98557bbe5b54) patch - Fix (sub)traces not being reported in their entirety when the OpenTelemetry exporter sends one trace in multiple export requests. This would be an issue for long running traces, that are exported in several requests.
44
+
3
45
  ## 3.5.6
4
46
 
5
47
  ### Changed
@@ -31,11 +73,11 @@ _Published on 2024-02-01._
31
73
 
32
74
  ### Deprecated
33
75
 
34
- - [bb98744b](https://github.com/appsignal/appsignal-ruby/commit/bb98744b1b6d34db71b5f46279b1a9b26039bd0f) patch - Deprecate the `Appsignal.set_host_guage` and `Appsignal.set_process_gauge` helper methods in the Ruby gem. These methods would already log deprecation warnings in the `appsignal.log` file, but now also as a Ruby warning. These methods will be removed in the next major version. These methods already did not report any metrics, and still do not.
76
+ - [bb98744b](https://github.com/appsignal/appsignal-ruby/commit/bb98744b1b6d34db71b5f46279b1a9b26039bd0f) patch - Deprecate the `Appsignal.set_host_gauge` and `Appsignal.set_process_gauge` helper methods in the Ruby gem. These methods would already log deprecation warnings in the `appsignal.log` file, but now also as a Ruby warning. These methods will be removed in the next major version. These methods already did not report any metrics, and still do not.
35
77
 
36
78
  ### Removed
37
79
 
38
- - [1a863490](https://github.com/appsignal/appsignal-ruby/commit/1a863490046318b8cee5fff2ac341fb73065f252) patch - Remove the `appsignal_set_host_guage` and `appsignal_set_process_gauge` extension functions. These functions were already deprecated and did not report any metrics.
80
+ - [1a863490](https://github.com/appsignal/appsignal-ruby/commit/1a863490046318b8cee5fff2ac341fb73065f252) patch - Remove the `appsignal_set_host_gauge` and `appsignal_set_process_gauge` extension functions. These functions were already deprecated and did not report any metrics.
39
81
 
40
82
  ### Fixed
41
83
 
data/ext/agent.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  # Modifications to this file will be overwritten with the next agent release.
7
7
 
8
8
  APPSIGNAL_AGENT_CONFIG = {
9
- "version" => "0.31.2",
9
+ "version" => "0.33.2",
10
10
  "mirrors" => [
11
11
  "https://appsignal-agent-releases.global.ssl.fastly.net",
12
12
  "https://d135dj0rjqvssy.cloudfront.net"
@@ -14,131 +14,131 @@ APPSIGNAL_AGENT_CONFIG = {
14
14
  "triples" => {
15
15
  "x86_64-darwin" => {
16
16
  "static" => {
17
- "checksum" => "42cdf814a89e5d6bd6e5cd9ba84103df82b43418012bb4f9251e98d0c3627759",
17
+ "checksum" => "0864691f001133fa479b34b00a682e76f374c40c161e7715756a3c036e3c8798",
18
18
  "filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
19
19
  },
20
20
  "dynamic" => {
21
- "checksum" => "7497d64b125849d306ebca71a2a2ce83e3f76dbfbc809b43b5948e12b7351b96",
21
+ "checksum" => "5141528c4293e4bd619107ae79afc8e07fdc8b33835899c5cf3f82ab3d31de8f",
22
22
  "filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
23
23
  }
24
24
  },
25
25
  "universal-darwin" => {
26
26
  "static" => {
27
- "checksum" => "42cdf814a89e5d6bd6e5cd9ba84103df82b43418012bb4f9251e98d0c3627759",
27
+ "checksum" => "0864691f001133fa479b34b00a682e76f374c40c161e7715756a3c036e3c8798",
28
28
  "filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
29
29
  },
30
30
  "dynamic" => {
31
- "checksum" => "7497d64b125849d306ebca71a2a2ce83e3f76dbfbc809b43b5948e12b7351b96",
31
+ "checksum" => "5141528c4293e4bd619107ae79afc8e07fdc8b33835899c5cf3f82ab3d31de8f",
32
32
  "filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
33
33
  }
34
34
  },
35
35
  "aarch64-darwin" => {
36
36
  "static" => {
37
- "checksum" => "8db9e31e090e767b1157d969521967f322be9dd73eb1b677b6192eb4c987af72",
37
+ "checksum" => "13506e5911523e7107a8cb714e18b3bcb690f3eeef88bf9aff54777ba540fdc4",
38
38
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
39
39
  },
40
40
  "dynamic" => {
41
- "checksum" => "612d68620836b324bc61d8d4fd42630e50641116688ac78f2a7c806e2ee10ad1",
41
+ "checksum" => "9d4deef17f42dc54981344a5af6b872e06dbd3d317be68b6abeb2403ffd65e23",
42
42
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
43
43
  }
44
44
  },
45
45
  "arm64-darwin" => {
46
46
  "static" => {
47
- "checksum" => "8db9e31e090e767b1157d969521967f322be9dd73eb1b677b6192eb4c987af72",
47
+ "checksum" => "13506e5911523e7107a8cb714e18b3bcb690f3eeef88bf9aff54777ba540fdc4",
48
48
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
49
49
  },
50
50
  "dynamic" => {
51
- "checksum" => "612d68620836b324bc61d8d4fd42630e50641116688ac78f2a7c806e2ee10ad1",
51
+ "checksum" => "9d4deef17f42dc54981344a5af6b872e06dbd3d317be68b6abeb2403ffd65e23",
52
52
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
53
53
  }
54
54
  },
55
55
  "arm-darwin" => {
56
56
  "static" => {
57
- "checksum" => "8db9e31e090e767b1157d969521967f322be9dd73eb1b677b6192eb4c987af72",
57
+ "checksum" => "13506e5911523e7107a8cb714e18b3bcb690f3eeef88bf9aff54777ba540fdc4",
58
58
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
59
59
  },
60
60
  "dynamic" => {
61
- "checksum" => "612d68620836b324bc61d8d4fd42630e50641116688ac78f2a7c806e2ee10ad1",
61
+ "checksum" => "9d4deef17f42dc54981344a5af6b872e06dbd3d317be68b6abeb2403ffd65e23",
62
62
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
63
63
  }
64
64
  },
65
65
  "aarch64-linux" => {
66
66
  "static" => {
67
- "checksum" => "72873d1c7ad2d4d744fe3dd4370fb07b4c9d8a4f4d87febb8dbe08c532eebff8",
67
+ "checksum" => "76702b5755d5bb45cc05df17dd38389b7e20e105a52324120a45ae1b481c7881",
68
68
  "filename" => "appsignal-aarch64-linux-all-static.tar.gz"
69
69
  },
70
70
  "dynamic" => {
71
- "checksum" => "b1510e425a0719cc7e7fe08e760c4794ece81108647ef73babffbb1eec93b5c7",
71
+ "checksum" => "bf518ce2cb4a9041fe819b6bf43e1bc793fe52b3e73527687d7812618c8e7407",
72
72
  "filename" => "appsignal-aarch64-linux-all-dynamic.tar.gz"
73
73
  }
74
74
  },
75
75
  "i686-linux" => {
76
76
  "static" => {
77
- "checksum" => "8779fdd2f02b034463900456b5b65a92d3a9165b87ba896b01baded96729685a",
77
+ "checksum" => "22cbda11a8d801d75e9394033f5cf28f0ddcff66a2138720f827441bdcf919c2",
78
78
  "filename" => "appsignal-i686-linux-all-static.tar.gz"
79
79
  },
80
80
  "dynamic" => {
81
- "checksum" => "be30f7ff817a048af9f133ffd0da5c408f50b38e75bb3f25e47cd3916d5e4fac",
81
+ "checksum" => "157492663e434421499f9cc0b510178387c8968e53bdc6e216db374b86d5c3dc",
82
82
  "filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
83
83
  }
84
84
  },
85
85
  "x86-linux" => {
86
86
  "static" => {
87
- "checksum" => "8779fdd2f02b034463900456b5b65a92d3a9165b87ba896b01baded96729685a",
87
+ "checksum" => "22cbda11a8d801d75e9394033f5cf28f0ddcff66a2138720f827441bdcf919c2",
88
88
  "filename" => "appsignal-i686-linux-all-static.tar.gz"
89
89
  },
90
90
  "dynamic" => {
91
- "checksum" => "be30f7ff817a048af9f133ffd0da5c408f50b38e75bb3f25e47cd3916d5e4fac",
91
+ "checksum" => "157492663e434421499f9cc0b510178387c8968e53bdc6e216db374b86d5c3dc",
92
92
  "filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
93
93
  }
94
94
  },
95
95
  "x86_64-linux" => {
96
96
  "static" => {
97
- "checksum" => "36fc29655d13e4dfe7bcbe2c798bfc16d68194610ff354d43e977f3768f31458",
97
+ "checksum" => "8ff0b1d7bf0cfc1c66e918545a9ab5c29be35c371cde48f64a01c725290599ed",
98
98
  "filename" => "appsignal-x86_64-linux-all-static.tar.gz"
99
99
  },
100
100
  "dynamic" => {
101
- "checksum" => "d37e57f55f44ac88a2b803fc17942f60137fbfa819134388941d22f579518170",
101
+ "checksum" => "a186c18536c3b7ec4802e852a62154cc976dcb5f554c3d0d8472d5cd7131b02b",
102
102
  "filename" => "appsignal-x86_64-linux-all-dynamic.tar.gz"
103
103
  }
104
104
  },
105
105
  "x86_64-linux-musl" => {
106
106
  "static" => {
107
- "checksum" => "59b6cef9797746da9d6717effc0892a2f2219767734a0e76f8b3d1578dc0d9e0",
107
+ "checksum" => "a5e0af3e5e1ad908792e79c7c46b59119272e9836e5ea96791c78e3cb12ed132",
108
108
  "filename" => "appsignal-x86_64-linux-musl-all-static.tar.gz"
109
109
  },
110
110
  "dynamic" => {
111
- "checksum" => "703d657ee15b69563c000fe150e368a91a12c9b39f16976f23a4b191284204f3",
111
+ "checksum" => "17c108a83dff86b2531bf7f348481bb31ece53b4cc62615ca0a34332c0df2970",
112
112
  "filename" => "appsignal-x86_64-linux-musl-all-dynamic.tar.gz"
113
113
  }
114
114
  },
115
115
  "aarch64-linux-musl" => {
116
116
  "static" => {
117
- "checksum" => "ec3ab8fcc20d1f31df6003e2ca3dcf257abfeddd1b7912fa6189f1f6905a89ab",
117
+ "checksum" => "92460560115d540a8140cbc360bd98beba8477e8a73eafd20ee611543b4528df",
118
118
  "filename" => "appsignal-aarch64-linux-musl-all-static.tar.gz"
119
119
  },
120
120
  "dynamic" => {
121
- "checksum" => "b7a00a09b7e10f500b54913feefd6a16873849324f0e8ec65b36c58bdc1901b0",
121
+ "checksum" => "d4749b10a1803080e0b1b0d8f95ef9d1fef0aa694fa0fc405df97812937d8e7c",
122
122
  "filename" => "appsignal-aarch64-linux-musl-all-dynamic.tar.gz"
123
123
  }
124
124
  },
125
125
  "x86_64-freebsd" => {
126
126
  "static" => {
127
- "checksum" => "bd654d5c555f6006e4145d76e27f02c5d22b285f411675a520455d6db6c8e165",
127
+ "checksum" => "8d8733c2adc0f750553be11b5e54fd614b13207be67863d95c57e4739021a92f",
128
128
  "filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
129
129
  },
130
130
  "dynamic" => {
131
- "checksum" => "b06445f38b7b8b3e47f407540fb24f196dbf0a84583a2ad7f25bbba750930108",
131
+ "checksum" => "8a9cbdc645b3833766458a252c2a8fefda76c62fceee8be795b286d65cc513c6",
132
132
  "filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
133
133
  }
134
134
  },
135
135
  "amd64-freebsd" => {
136
136
  "static" => {
137
- "checksum" => "bd654d5c555f6006e4145d76e27f02c5d22b285f411675a520455d6db6c8e165",
137
+ "checksum" => "8d8733c2adc0f750553be11b5e54fd614b13207be67863d95c57e4739021a92f",
138
138
  "filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
139
139
  },
140
140
  "dynamic" => {
141
- "checksum" => "b06445f38b7b8b3e47f407540fb24f196dbf0a84583a2ad7f25bbba750930108",
141
+ "checksum" => "8a9cbdc645b3833766458a252c2a8fefda76c62fceee8be795b286d65cc513c6",
142
142
  "filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
143
143
  }
144
144
  }
@@ -11,6 +11,7 @@ module Appsignal
11
11
  include Appsignal::Utils::DeprecationMessage
12
12
 
13
13
  DEFAULT_CONFIG = {
14
+ :activejob_report_errors => "all",
14
15
  :ca_file_path => File.expand_path(File.join("../../../resources/cacert.pem"), __FILE__),
15
16
  :debug => false,
16
17
  :dns_servers => [],
@@ -65,6 +66,7 @@ module Appsignal
65
66
 
66
67
  ENV_TO_KEY_MAPPING = {
67
68
  "APPSIGNAL_ACTIVE" => :active,
69
+ "APPSIGNAL_ACTIVE_JOB_REPORT_ERRORS" => :activejob_report_errors,
68
70
  "APPSIGNAL_APP_NAME" => :name,
69
71
  "APPSIGNAL_BIND_ADDRESS" => :bind_address,
70
72
  "APPSIGNAL_CA_FILE_PATH" => :ca_file_path,
@@ -112,6 +114,7 @@ module Appsignal
112
114
  }.freeze
113
115
  # @api private
114
116
  ENV_STRING_KEYS = %w[
117
+ APPSIGNAL_ACTIVE_JOB_REPORT_ERRORS
115
118
  APPSIGNAL_APP_NAME
116
119
  APPSIGNAL_BIND_ADDRESS
117
120
  APPSIGNAL_CA_FILE_PATH
@@ -56,7 +56,7 @@ module Appsignal
56
56
  super
57
57
  rescue Exception => exception # rubocop:disable Lint/RescueException
58
58
  job_status = :failed
59
- transaction.set_error(exception)
59
+ transaction_set_error(transaction, exception)
60
60
  raise exception
61
61
  ensure
62
62
  if transaction
@@ -82,6 +82,14 @@ module Appsignal
82
82
  tags.merge(:status => :processed)
83
83
  end
84
84
  end
85
+
86
+ private
87
+
88
+ def transaction_set_error(transaction, exception)
89
+ return if Appsignal.config[:activejob_report_errors] == "none"
90
+
91
+ transaction.set_error(exception)
92
+ end
85
93
  end
86
94
 
87
95
  module ActiveJobHelpers
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ # @api private
5
+ module Rack
6
+ class BodyWrapper
7
+ def self.wrap(original_body, appsignal_transaction)
8
+ # The logic of how Rack treats a response body differs based on which methods
9
+ # the body responds to. This means that to support the Rack 3.x spec in full
10
+ # we need to return a wrapper which matches the API of the wrapped body as closely
11
+ # as possible. Pick the wrapper from the most specific to the least specific.
12
+ # See https://github.com/rack/rack/blob/main/SPEC.rdoc#the-body-
13
+ #
14
+ # What is important is that our Body wrapper responds to the same methods Rack
15
+ # (or a webserver) would be checking and calling, and passes through that functionality
16
+ # to the original body. This can be done using delegation via i.e. SimpleDelegate
17
+ # but we also need "close" to get called correctly so that the Appsignal transaction
18
+ # gets completed - which will not happen, for example, when #to_ary gets called
19
+ # just on the delegated Rack body.
20
+ #
21
+ # This comment https://github.com/rails/rails/pull/49627#issuecomment-1769802573
22
+ # is of particular interest to understand why this has to be somewhat complicated.
23
+ if original_body.respond_to?(:to_path)
24
+ PathableBodyWrapper.new(original_body, appsignal_transaction)
25
+ elsif original_body.respond_to?(:to_ary)
26
+ ArrayableBodyWrapper.new(original_body, appsignal_transaction)
27
+ elsif !original_body.respond_to?(:each) && original_body.respond_to?(:call)
28
+ # This body only supports #call, so we must be running a Rack 3 application
29
+ # It is possible that a body exposes both `each` and `call` in the hopes of
30
+ # being backwards-compatible with both Rack 3.x and Rack 2.x, however
31
+ # this is not going to work since the SPEC says that if both are available,
32
+ # `each` should be used and `call` should be ignored.
33
+ # So for that case we can drop by to our default EnumerableBodyWrapper
34
+ CallableBodyWrapper.new(original_body, appsignal_transaction)
35
+ else
36
+ EnumerableBodyWrapper.new(original_body, appsignal_transaction)
37
+ end
38
+ end
39
+
40
+ def initialize(body, appsignal_transaction)
41
+ @body_already_closed = false
42
+ @body = body
43
+ @transaction = appsignal_transaction
44
+ end
45
+
46
+ # This must be present in all Rack bodies and will be called by the serving adapter
47
+ def close
48
+ # The @body_already_closed check is needed so that if `to_ary`
49
+ # of the body has already closed itself (as prescribed) we do not
50
+ # attempt to close it twice
51
+ if !@body_already_closed && @body.respond_to?(:close)
52
+ Appsignal.instrument("response_body_close.rack") { @body.close }
53
+ end
54
+ @body_already_closed = true
55
+ rescue Exception => error # rubocop:disable Lint/RescueException
56
+ @transaction.set_error(error)
57
+ raise error
58
+ ensure
59
+ complete_transaction!
60
+ end
61
+
62
+ def complete_transaction!
63
+ # We need to call the Transaction class method and not
64
+ # @transaction.complete because the transaction is still
65
+ # thread-local and it needs to remove itself from the
66
+ # thread variables correctly, which does not happen on
67
+ # Transaction#complete.
68
+ #
69
+ # In the future it would be a good idea to ensure
70
+ # that the current transaction is the same as @transaction,
71
+ # or allow @transaction to complete itself and remove
72
+ # itself from Thread.current
73
+ Appsignal::Transaction.complete_current!
74
+ end
75
+ end
76
+
77
+ # The standard Rack body wrapper which exposes "each" for iterating
78
+ # over the response body. This is supported across all 3 major Rack
79
+ # versions.
80
+ #
81
+ # @api private
82
+ class EnumerableBodyWrapper < BodyWrapper
83
+ def each(&blk)
84
+ # This is a workaround for the Rails bug when there was a bit too much
85
+ # eagerness in implementing to_ary, see:
86
+ # https://github.com/rails/rails/pull/44953
87
+ # https://github.com/rails/rails/pull/47092
88
+ # https://github.com/rails/rails/pull/49627
89
+ # https://github.com/rails/rails/issues/49588
90
+ # While the Rack SPEC does not mandate `each` to be callable
91
+ # in a blockless way it is still a good idea to have it in place.
92
+ return enum_for(:each) unless block_given?
93
+
94
+ Appsignal.instrument("process_response_body.rack", "Process Rack response body (#each)") do
95
+ @body.each(&blk)
96
+ end
97
+ rescue Exception => error # rubocop:disable Lint/RescueException
98
+ @transaction.set_error(error)
99
+ raise error
100
+ end
101
+ end
102
+
103
+ # The callable response bodies are a new Rack 3.x feature, and would not work
104
+ # with older Rack versions. They must not respond to `each` because
105
+ # "If it responds to each, you must call each and not call". This is why
106
+ # it inherits from BodyWrapper directly and not from EnumerableBodyWrapper
107
+ #
108
+ # @api private
109
+ class CallableBodyWrapper < BodyWrapper
110
+ def call(stream)
111
+ # `stream` will be closed by the app we are calling, no need for us
112
+ # to close it ourselves
113
+ Appsignal.instrument("process_response_body.rack", "Process Rack response body (#call)") do
114
+ @body.call(stream)
115
+ end
116
+ rescue Exception => error # rubocop:disable Lint/RescueException
117
+ @transaction.set_error(error)
118
+ raise error
119
+ end
120
+ end
121
+
122
+ # "to_ary" takes precedence over "each" and allows the response body
123
+ # to be read eagerly. If the body supports that method, it takes precedence
124
+ # over "each":
125
+ # "Middleware may call to_ary directly on the Body and return a new Body in its place"
126
+ # One could "fold" both the to_ary API and the each() API into one Body object, but
127
+ # to_ary must also call "close" after it executes - and in the Rails implementation
128
+ # this pecularity was not handled properly.
129
+ #
130
+ # @api private
131
+ class ArrayableBodyWrapper < EnumerableBodyWrapper
132
+ def to_ary
133
+ @body_already_closed = true
134
+ Appsignal.instrument(
135
+ "process_response_body.rack",
136
+ "Process Rack response body (#to_ary)"
137
+ ) do
138
+ @body.to_ary
139
+ end
140
+ rescue Exception => error # rubocop:disable Lint/RescueException
141
+ @transaction.set_error(error)
142
+ raise error
143
+ ensure
144
+ # We do not call "close" on ourselves as the only action
145
+ # we need to complete is completing the transaction.
146
+ complete_transaction!
147
+ end
148
+ end
149
+
150
+ # Having "to_path" on a body allows Rack to serve out a static file, or to
151
+ # pass that file to the downstream webserver for sending using X-Sendfile
152
+ class PathableBodyWrapper < EnumerableBodyWrapper
153
+ def to_path
154
+ Appsignal.instrument("response_body_to_path.rack") { @body.to_path }
155
+ rescue Exception => error # rubocop:disable Lint/RescueException
156
+ @transaction.set_error(error)
157
+ raise error
158
+ end
159
+ end
160
+ end
161
+ end
@@ -16,7 +16,9 @@ module Appsignal
16
16
  if Appsignal.active?
17
17
  call_with_appsignal_monitoring(env)
18
18
  else
19
- @app.call(env)
19
+ nil_transaction = Appsignal::Transaction::NilTransaction.new
20
+ status, headers, obody = @app.call(env)
21
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, nil_transaction)]
20
22
  end
21
23
  end
22
24
 
@@ -27,19 +29,30 @@ module Appsignal
27
29
  Appsignal::Transaction::HTTP_REQUEST,
28
30
  request
29
31
  )
32
+ # We need to complete the transaction if there is an exception inside the `call`
33
+ # of the app. If there isn't one and the app returns us a Rack response triplet, we let
34
+ # the BodyWrapper complete the transaction when #close gets called on it
35
+ # (guaranteed by the webserver)
36
+ complete_transaction_without_body = false
30
37
  begin
31
38
  Appsignal.instrument("process_action.generic") do
32
- @app.call(env)
39
+ status, headers, obody = @app.call(env)
40
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, transaction)]
33
41
  end
34
42
  rescue Exception => error # rubocop:disable Lint/RescueException
35
43
  transaction.set_error(error)
44
+ complete_transaction_without_body = true
36
45
  raise error
37
46
  ensure
38
- transaction.set_action_if_nil(env["appsignal.route"] || "unknown")
47
+ default_action = env["appsignal.route"] || env["appsignal.action"] || "unknown"
48
+ transaction.set_action_if_nil(default_action)
39
49
  transaction.set_metadata("path", request.path)
40
50
  transaction.set_metadata("method", request.request_method)
41
51
  transaction.set_http_or_background_queue_start
42
- Appsignal::Transaction.complete_current!
52
+
53
+ # Transaction gets completed when the body gets read out, except in cases when
54
+ # the app failed before returning us the Rack response triplet.
55
+ Appsignal::Transaction.complete_current! if complete_transaction_without_body
43
56
  end
44
57
  end
45
58
  end
@@ -16,7 +16,9 @@ module Appsignal
16
16
  if Appsignal.active?
17
17
  call_with_appsignal_monitoring(env)
18
18
  else
19
- @app.call(env)
19
+ nil_transaction = Appsignal::Transaction::NilTransaction.new
20
+ status, headers, obody = @app.call(env)
21
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, nil_transaction)]
20
22
  end
21
23
  end
22
24
 
@@ -28,10 +30,17 @@ module Appsignal
28
30
  request,
29
31
  :params_method => :filtered_parameters
30
32
  )
33
+ # We need to complete the transaction if there is an exception exception inside the `call`
34
+ # of the app. If there isn't one and the app returns us a Rack response triplet, we let
35
+ # the BodyWrapper complete the transaction when #close gets called on it
36
+ # (guaranteed by the webserver)
37
+ complete_transaction_without_body = false
31
38
  begin
32
- @app.call(env)
39
+ status, headers, obody = @app.call(env)
40
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, transaction)]
33
41
  rescue Exception => error # rubocop:disable Lint/RescueException
34
42
  transaction.set_error(error)
43
+ complete_transaction_without_body = true
35
44
  raise error
36
45
  ensure
37
46
  controller = env["action_controller.instance"]
@@ -45,7 +54,10 @@ module Appsignal
45
54
  rescue => error
46
55
  Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
47
56
  end
48
- Appsignal::Transaction.complete_current!
57
+
58
+ # Transaction gets completed when the body gets read out, except in cases when
59
+ # the app failed before returning us the Rack response triplet.
60
+ Appsignal::Transaction.complete_current! if complete_transaction_without_body
49
61
  end
50
62
  end
51
63
 
@@ -42,7 +42,9 @@ module Appsignal
42
42
  if Appsignal.active?
43
43
  call_with_appsignal_monitoring(env)
44
44
  else
45
- @app.call(env)
45
+ nil_transaction = Appsignal::Transaction::NilTransaction.new
46
+ status, headers, obody = @app.call(env)
47
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, nil_transaction)]
46
48
  end
47
49
  end
48
50
 
@@ -56,12 +58,19 @@ module Appsignal
56
58
  request,
57
59
  options
58
60
  )
61
+ # We need to complete the transaction if there is an exception exception inside the `call`
62
+ # of the app. If there isn't one and the app returns us a Rack response triplet, we let
63
+ # the BodyWrapper complete the transaction when #close gets called on it
64
+ # (guaranteed by the webserver)
65
+ complete_transaction_without_body = false
59
66
  begin
60
67
  Appsignal.instrument("process_action.sinatra") do
61
- @app.call(env)
68
+ status, headers, obody = @app.call(env)
69
+ [status, headers, Appsignal::Rack::BodyWrapper.wrap(obody, transaction)]
62
70
  end
63
71
  rescue Exception => error # rubocop:disable Lint/RescueException
64
72
  transaction.set_error(error)
73
+ complete_transaction_without_body = true
65
74
  raise error
66
75
  ensure
67
76
  # If raise_error is off versions of Sinatra don't raise errors, but store
@@ -73,7 +82,10 @@ module Appsignal
73
82
  transaction.set_metadata("path", request.path)
74
83
  transaction.set_metadata("method", request.request_method)
75
84
  transaction.set_http_or_background_queue_start
76
- Appsignal::Transaction.complete_current!
85
+
86
+ # Transaction gets completed when the body gets read out, except in cases when
87
+ # the app failed before returning us the Rack response triplet.
88
+ Appsignal::Transaction.complete_current! if complete_transaction_without_body
77
89
  end
78
90
  end
79
91