appsignal 4.0.3-java → 4.0.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +51 -0
  3. data/ext/agent.rb +27 -27
  4. data/lib/appsignal/check_in/cron.rb +2 -34
  5. data/lib/appsignal/check_in/scheduler.rb +192 -0
  6. data/lib/appsignal/check_in.rb +18 -0
  7. data/lib/appsignal/cli/diagnose.rb +1 -1
  8. data/lib/appsignal/config.rb +7 -0
  9. data/lib/appsignal/hooks/at_exit.rb +3 -1
  10. data/lib/appsignal/hooks/puma.rb +5 -1
  11. data/lib/appsignal/integrations/puma.rb +45 -0
  12. data/lib/appsignal/rack/abstract_middleware.rb +3 -47
  13. data/lib/appsignal/rack/body_wrapper.rb +15 -0
  14. data/lib/appsignal/rack/event_handler.rb +2 -0
  15. data/lib/appsignal/rack/hanami_middleware.rb +5 -1
  16. data/lib/appsignal/rack.rb +68 -0
  17. data/lib/appsignal/transmitter.rb +30 -7
  18. data/lib/appsignal/utils/ndjson.rb +15 -0
  19. data/lib/appsignal/utils.rb +1 -0
  20. data/lib/appsignal/version.rb +1 -1
  21. data/lib/appsignal.rb +1 -0
  22. data/spec/lib/appsignal/check_in/cron_spec.rb +202 -0
  23. data/spec/lib/appsignal/check_in/scheduler_spec.rb +443 -0
  24. data/spec/lib/appsignal/config_spec.rb +13 -0
  25. data/spec/lib/appsignal/environment_spec.rb +1 -1
  26. data/spec/lib/appsignal/hooks/at_exit_spec.rb +22 -0
  27. data/spec/lib/appsignal/hooks/puma_spec.rb +31 -23
  28. data/spec/lib/appsignal/integrations/puma_spec.rb +150 -0
  29. data/spec/lib/appsignal/probes_spec.rb +1 -6
  30. data/spec/lib/appsignal/rack/abstract_middleware_spec.rb +41 -122
  31. data/spec/lib/appsignal/rack/body_wrapper_spec.rb +29 -21
  32. data/spec/lib/appsignal/rack_spec.rb +180 -0
  33. data/spec/lib/appsignal/transmitter_spec.rb +48 -2
  34. data/spec/lib/appsignal_spec.rb +5 -0
  35. data/spec/spec_helper.rb +0 -7
  36. data/spec/support/helpers/config_helpers.rb +2 -1
  37. data/spec/support/helpers/take_at_most_helper.rb +21 -0
  38. data/spec/support/matchers/contains_log.rb +10 -3
  39. data/spec/support/mocks/hash_like.rb +10 -0
  40. data/spec/support/mocks/puma_mock.rb +43 -0
  41. metadata +11 -3
  42. data/spec/lib/appsignal/check_in_spec.rb +0 -136
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ea322f2119a448a34c283e7a5cac3000f0e9acfec8f5d1a406265eb42f859d6
4
- data.tar.gz: 1ab95f249d94eedba7e86e36d3f74d217bd57deed236e6b352b190dc34007eb5
3
+ metadata.gz: 265860dc1cb9e7ef2035d4dcc288d6789d14b6d656c6812ab26f69fc6e3d9e2e
4
+ data.tar.gz: 702e3e82530b79db9d8b26519d14cb9664d728a12f3d6c5999b63bfbda022fd1
5
5
  SHA512:
6
- metadata.gz: e4d10c4f4684675cf908972044fb11be5eafd1041fb6b80d684580285ab9681a746e5fc36fbc79e84816766a33c507a233b22df7ee911b1cc9253abfa14a2a46
7
- data.tar.gz: d25b331756ceafe336d5b430577fb98da0696f5f92613a47d89f30007b910a4057cf981ddb1d841d295cfdb31f48d69bb8fecb1e7071d933dba47b1160d701bf
6
+ metadata.gz: 8e5589a001f66450dd0c2f7250134be2813bcab36a9c4576886cc3997206109e8e6ae53fbd60fb8924ffa9e34bddd225617a92dfec6b442e6565bc2ea3433d53
7
+ data.tar.gz: bf73768bfbbc2db0c042cb84ddc185362707f7b0b622d0565ce7a5142ddd0479daea6e76d7d87d9b7542ff01929b7a24cf566ca9036e3bd66a5fb0f2de1e622a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # AppSignal for Ruby gem Changelog
2
2
 
3
+ ## 4.0.5
4
+
5
+ _Published on 2024-09-02._
6
+
7
+ ### Added
8
+
9
+ - Report Puma low-level errors using the `lowlevel_error` reporter. This will report errors previously not caught by our instrumentation middleware. (patch [70cc21f4](https://github.com/appsignal/appsignal-ruby/commit/70cc21f49e19faa9fd2d12a051620cd48e036dcb))
10
+
11
+ ### Changed
12
+
13
+ - Log a warning when loader defaults are added after AppSignal has already been configured.
14
+
15
+ ```ruby
16
+ # Bad
17
+ Appsignal.configure # or Appsignal.start
18
+ Appsignal.load(:sinatra)
19
+
20
+ # Good
21
+ Appsignal.load(:sinatra)
22
+ Appsignal.configure # or Appsignal.start
23
+ ```
24
+
25
+ (patch [0997dd9c](https://github.com/appsignal/appsignal-ruby/commit/0997dd9c0430123a697b8100785f5676163e20ef))
26
+ - Rename the `path` and `method` transaction metadata to `request_path` and `request_method` to make it more clear what context this metadata is from. The `path` and `method` metadata will continue to be reported until the next major/minor version. (patch [fa314b5f](https://github.com/appsignal/appsignal-ruby/commit/fa314b5fb6fdfbf3e9746df377b0145cde0cfa36))
27
+ - Internal change to how OpenTelemetry metrics are sent. (patch [e66d1d70](https://github.com/appsignal/appsignal-ruby/commit/e66d1d702d5010cb5b8084ba790b24d9e70a9e08))
28
+
29
+ ### Removed
30
+
31
+ - Drop support for Puma version 2 and lower. (patch [4fab861c](https://github.com/appsignal/appsignal-ruby/commit/4fab861cae74b08aa71bf64e1b134ae4b1df1dff))
32
+
33
+ ### Fixed
34
+
35
+ - Fix the error log message about our `at_exit` hook reporting no error on process exit when the process exits without an error. (patch [b71f3966](https://github.com/appsignal/appsignal-ruby/commit/b71f39661e9b05c10fa78b821ba0e45bde0c941b))
36
+
37
+ ## 4.0.4
38
+
39
+ _Published on 2024-08-29._
40
+
41
+ ### Changed
42
+
43
+ - Send check-ins concurrently. When calling `Appsignal::CheckIn.cron`, instead of blocking the current thread while the check-in events are sent, schedule them to be sent in a separate thread.
44
+
45
+ When shutting down your application manually, call `Appsignal.stop` to block until all scheduled check-ins have been sent.
46
+
47
+ (patch [46d4ca74](https://github.com/appsignal/appsignal-ruby/commit/46d4ca74f4c188cc011653ed23969ad7ec770812))
48
+
49
+ ### Fixed
50
+
51
+ - Make our Rack BodyWrapper behave like a Rack BodyProxy. If a method doesn't exist on our BodyWrapper class, but it does exist on the body, behave like the Rack BodyProxy and call the method on the wrapped body. (patch [e2376305](https://github.com/appsignal/appsignal-ruby/commit/e23763058a3fb980f1054e9c1eaf7e0f25f75666))
52
+ - Do not report `SignalException` errors from our `at_exit` error reporter. (patch [3ba3ce31](https://github.com/appsignal/appsignal-ruby/commit/3ba3ce31ee3f3e84665c9f2f18d488c689cff6c2))
53
+
3
54
  ## 4.0.3
4
55
 
5
56
  _Published on 2024-08-26._
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.35.21",
9
+ "version" => "0.35.22",
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" => "0e7b5bc5bf1e9903276b9fbe105af1128011613f9dc7ac7cedd4d230d92011bd",
17
+ "checksum" => "024cef88b24032d7187488c00682f92095da406d80ae642ac858e6318b840ad6",
18
18
  "filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
19
19
  },
20
20
  "dynamic" => {
21
- "checksum" => "56cc9aed70014b4fda2b30ff34819370cab2eb2bda588f59d7d86816d60a458f",
21
+ "checksum" => "ddddf86a951fe57f50e351a449df65d2c6428d703581b8ef5922fad23a688f1d",
22
22
  "filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
23
23
  }
24
24
  },
25
25
  "universal-darwin" => {
26
26
  "static" => {
27
- "checksum" => "0e7b5bc5bf1e9903276b9fbe105af1128011613f9dc7ac7cedd4d230d92011bd",
27
+ "checksum" => "024cef88b24032d7187488c00682f92095da406d80ae642ac858e6318b840ad6",
28
28
  "filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
29
29
  },
30
30
  "dynamic" => {
31
- "checksum" => "56cc9aed70014b4fda2b30ff34819370cab2eb2bda588f59d7d86816d60a458f",
31
+ "checksum" => "ddddf86a951fe57f50e351a449df65d2c6428d703581b8ef5922fad23a688f1d",
32
32
  "filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
33
33
  }
34
34
  },
35
35
  "aarch64-darwin" => {
36
36
  "static" => {
37
- "checksum" => "423178f0178f15d7f663451e45a318ca92d8f7cca136b5e985e753520b4ad3e4",
37
+ "checksum" => "b8dfebd77322a1f66e0f0acf1717c2583189016e9749286f61956a3ebbc1a16a",
38
38
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
39
39
  },
40
40
  "dynamic" => {
41
- "checksum" => "c2c0aeba5ddb38584c8b1cc8b2ad2407431c71b944cd4dee47d45828b93619ae",
41
+ "checksum" => "d94ae45a9fa69566a30aaacf9f65bdf21b5d0d2a5990e4ba8f98dedc5956f62f",
42
42
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
43
43
  }
44
44
  },
45
45
  "arm64-darwin" => {
46
46
  "static" => {
47
- "checksum" => "423178f0178f15d7f663451e45a318ca92d8f7cca136b5e985e753520b4ad3e4",
47
+ "checksum" => "b8dfebd77322a1f66e0f0acf1717c2583189016e9749286f61956a3ebbc1a16a",
48
48
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
49
49
  },
50
50
  "dynamic" => {
51
- "checksum" => "c2c0aeba5ddb38584c8b1cc8b2ad2407431c71b944cd4dee47d45828b93619ae",
51
+ "checksum" => "d94ae45a9fa69566a30aaacf9f65bdf21b5d0d2a5990e4ba8f98dedc5956f62f",
52
52
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
53
53
  }
54
54
  },
55
55
  "arm-darwin" => {
56
56
  "static" => {
57
- "checksum" => "423178f0178f15d7f663451e45a318ca92d8f7cca136b5e985e753520b4ad3e4",
57
+ "checksum" => "b8dfebd77322a1f66e0f0acf1717c2583189016e9749286f61956a3ebbc1a16a",
58
58
  "filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
59
59
  },
60
60
  "dynamic" => {
61
- "checksum" => "c2c0aeba5ddb38584c8b1cc8b2ad2407431c71b944cd4dee47d45828b93619ae",
61
+ "checksum" => "d94ae45a9fa69566a30aaacf9f65bdf21b5d0d2a5990e4ba8f98dedc5956f62f",
62
62
  "filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
63
63
  }
64
64
  },
65
65
  "aarch64-linux" => {
66
66
  "static" => {
67
- "checksum" => "bf94592c9640f89d0321acd5a0a9bd6951154c37f97f4778ddc2fc536203dc4f",
67
+ "checksum" => "1f21e14f31b9b91f5ce3da0dcb91224bc9f4a0d834ada75e87f396ca9838cf59",
68
68
  "filename" => "appsignal-aarch64-linux-all-static.tar.gz"
69
69
  },
70
70
  "dynamic" => {
71
- "checksum" => "8f8e60daae9ac10bae66fcf9ea33984046b5c92d911eae71ac1a8bdbaa7fa3eb",
71
+ "checksum" => "cf81cabcd216cf41d55ad2ad50f5eccfc1b3edc0dc78159973f3c0687f5ad86d",
72
72
  "filename" => "appsignal-aarch64-linux-all-dynamic.tar.gz"
73
73
  }
74
74
  },
75
75
  "i686-linux" => {
76
76
  "static" => {
77
- "checksum" => "1553674c377a3e6fa56e31754d2af175e734531a44a76c680ff14cc49d4f0031",
77
+ "checksum" => "6111532d1958f9bbbdbea9104c509636d7b6d311090c609a0c134b68e6ef0dd6",
78
78
  "filename" => "appsignal-i686-linux-all-static.tar.gz"
79
79
  },
80
80
  "dynamic" => {
81
- "checksum" => "93e53b3cb2e2fb89cc3dff690da910fccb46503c3d58e4d3fb1a525fb222eea7",
81
+ "checksum" => "705fd88f0e2a4370766f64c93d123476d37f169190550cc3a2c85d34ce1b1893",
82
82
  "filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
83
83
  }
84
84
  },
85
85
  "x86-linux" => {
86
86
  "static" => {
87
- "checksum" => "1553674c377a3e6fa56e31754d2af175e734531a44a76c680ff14cc49d4f0031",
87
+ "checksum" => "6111532d1958f9bbbdbea9104c509636d7b6d311090c609a0c134b68e6ef0dd6",
88
88
  "filename" => "appsignal-i686-linux-all-static.tar.gz"
89
89
  },
90
90
  "dynamic" => {
91
- "checksum" => "93e53b3cb2e2fb89cc3dff690da910fccb46503c3d58e4d3fb1a525fb222eea7",
91
+ "checksum" => "705fd88f0e2a4370766f64c93d123476d37f169190550cc3a2c85d34ce1b1893",
92
92
  "filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
93
93
  }
94
94
  },
95
95
  "x86_64-linux" => {
96
96
  "static" => {
97
- "checksum" => "497d280dff46f7e72875af69f7b3756f3925bcad9e1922f030f818c8c5fa5d67",
97
+ "checksum" => "9e325faf63b7fe9b9804b4e8ce4bac7deefa6c47aec47faf47d90133d44dbaa5",
98
98
  "filename" => "appsignal-x86_64-linux-all-static.tar.gz"
99
99
  },
100
100
  "dynamic" => {
101
- "checksum" => "1b230cc3e80ef4013defc9d6e80713d1d318ed7f673121433d864b47e239ff73",
101
+ "checksum" => "11bac70bee7e620b31a26a01093a0c9ef01f7eb7508ac0c0a0eae27973cc643f",
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" => "2bcbc6b57a0298a97e6549e025ec092f8b3bd2b4e8f430fe7b040f7a2e274527",
107
+ "checksum" => "a76de00904eb40a61a7e68082cb06379aa13f61ec0d25ead828fd2ba022f9be9",
108
108
  "filename" => "appsignal-x86_64-linux-musl-all-static.tar.gz"
109
109
  },
110
110
  "dynamic" => {
111
- "checksum" => "7e89c78e9375d55b035f0e75e13d8dd278c2ddf4618fbe005c26f13d249b303f",
111
+ "checksum" => "b7b34ed9243059259a5fa10daa65adb6c4748e8b9715f64e6e559f88aa7aec03",
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" => "f9bfd06e0107f3a491ff6009a1e8c0d6d2598c128e8ee84bd852a2f8d3e8e16b",
117
+ "checksum" => "2e197b99b7052357e466a7d93450d87cbdc07d0e5749630e98adcf7c7fad8936",
118
118
  "filename" => "appsignal-aarch64-linux-musl-all-static.tar.gz"
119
119
  },
120
120
  "dynamic" => {
121
- "checksum" => "187a9a8e58617a0f78077b412dee18351cc975af41500443d7450fc44f2f65d3",
121
+ "checksum" => "ace9ab040e4c412f9d6c0b7d2209388e81f40882f8719a67cc30c60e0bdc50ed",
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" => "cf8043c78e0e628cf0747dd57327d6c0ffd4147d3bff7b2426110fd2fada0960",
127
+ "checksum" => "0c263f693ba5c06373b9fb4062f14d2b6bf1ff33bc0d737abcccc77bab66a634",
128
128
  "filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
129
129
  },
130
130
  "dynamic" => {
131
- "checksum" => "072c2312aadf961c3f851552ee255db77f0c4050e47d438dd9f82e536b68bcd6",
131
+ "checksum" => "7545a50f4d127da466e87d9504df4911ed9f8b0ec67b86023e08f8a58ea2b8c2",
132
132
  "filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
133
133
  }
134
134
  },
135
135
  "amd64-freebsd" => {
136
136
  "static" => {
137
- "checksum" => "cf8043c78e0e628cf0747dd57327d6c0ffd4147d3bff7b2426110fd2fada0960",
137
+ "checksum" => "0c263f693ba5c06373b9fb4062f14d2b6bf1ff33bc0d737abcccc77bab66a634",
138
138
  "filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
139
139
  },
140
140
  "dynamic" => {
141
- "checksum" => "072c2312aadf961c3f851552ee255db77f0c4050e47d438dd9f82e536b68bcd6",
141
+ "checksum" => "7545a50f4d127da466e87d9504df4911ed9f8b0ec67b86023e08f8a58ea2b8c2",
142
142
  "filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
143
143
  }
144
144
  }
@@ -3,15 +3,6 @@
3
3
  module Appsignal
4
4
  module CheckIn
5
5
  class Cron
6
- class << self
7
- # @api private
8
- def transmitter
9
- @transmitter ||= Appsignal::Transmitter.new(
10
- "#{Appsignal.config[:logging_endpoint]}/check_ins/json"
11
- )
12
- end
13
- end
14
-
15
6
  # @api private
16
7
  attr_reader :identifier, :digest
17
8
 
@@ -21,11 +12,11 @@ module Appsignal
21
12
  end
22
13
 
23
14
  def start
24
- transmit_event("start")
15
+ CheckIn.scheduler.schedule(event("start"))
25
16
  end
26
17
 
27
18
  def finish
28
- transmit_event("finish")
19
+ CheckIn.scheduler.schedule(event("finish"))
29
20
  end
30
21
 
31
22
  private
@@ -39,29 +30,6 @@ module Appsignal
39
30
  :check_in_type => "cron"
40
31
  }
41
32
  end
42
-
43
- def transmit_event(kind)
44
- unless Appsignal.active?
45
- Appsignal.internal_logger.debug(
46
- "AppSignal not active, not transmitting cron check-in event"
47
- )
48
- return
49
- end
50
-
51
- response = self.class.transmitter.transmit(event(kind))
52
-
53
- if response.code.to_i >= 200 && response.code.to_i < 300
54
- Appsignal.internal_logger.debug(
55
- "Transmitted cron check-in `#{identifier}` (#{digest}) #{kind} event"
56
- )
57
- else
58
- Appsignal.internal_logger.error(
59
- "Failed to transmit cron check-in #{kind} event: status code was #{response.code}"
60
- )
61
- end
62
- rescue => e
63
- Appsignal.internal_logger.error("Failed to transmit cron check-in #{kind} event: #{e}")
64
- end
65
33
  end
66
34
  end
67
35
  end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module CheckIn
5
+ class Scheduler
6
+ INITIAL_DEBOUNCE_SECONDS = 0.1
7
+ BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS = 10
8
+
9
+ def initialize
10
+ # The mutex is used to synchronize access to the events array, the
11
+ # waker thread and the main thread, as well as queue writes
12
+ # (which depend on the events array) and closes (so they do not
13
+ # happen at the same time that an event is added to the scheduler)
14
+ @mutex = Mutex.new
15
+ # The transmitter thread will be started when an event is first added.
16
+ @thread = nil
17
+ @queue = Thread::Queue.new
18
+ # Scheduled events that have not been sent to the transmitter thread
19
+ # yet. A copy of this array is pushed to the queue by the waker thread
20
+ # after it has awaited the debounce period.
21
+ @events = []
22
+ # The waker thread is used to schedule debounces. It will be started
23
+ # when an event is first added.
24
+ @waker = nil
25
+ # For internal testing purposes.
26
+ @transmitted = 0
27
+ end
28
+
29
+ def schedule(event)
30
+ unless Appsignal.active?
31
+ Appsignal.internal_logger.debug(
32
+ "Cannot transmit #{describe([event])}: AppSignal is not active"
33
+ )
34
+ return
35
+ end
36
+
37
+ @mutex.synchronize do
38
+ if @queue.closed?
39
+ Appsignal.internal_logger.debug(
40
+ "Cannot transmit #{describe([event])}: AppSignal is stopped"
41
+ )
42
+ return
43
+ end
44
+ add_event(event)
45
+ # If we're not already waiting to be awakened from a scheduled
46
+ # debounce, schedule a short debounce, which will push the events
47
+ # to the queue and schedule a long debounce.
48
+ start_waker(INITIAL_DEBOUNCE_SECONDS) if @waker.nil?
49
+
50
+ Appsignal.internal_logger.debug(
51
+ "Scheduling #{describe([event])} to be transmitted"
52
+ )
53
+
54
+ # Make sure to start the thread after an event has been added.
55
+ @thread ||= Thread.new(&method(:run))
56
+ end
57
+ end
58
+
59
+ def stop
60
+ @mutex.synchronize do
61
+ # Flush all events before closing the queue.
62
+ push_events
63
+ rescue ClosedQueueError
64
+ # The queue is already closed (by a previous call to `#stop`)
65
+ # so it is not possible to push events to it anymore.
66
+ ensure
67
+ # Ensure calling `#stop` closes the queue and kills
68
+ # the waker thread, disallowing any further events from being
69
+ # scheduled with `#schedule`.
70
+ stop_waker
71
+ @queue.close
72
+
73
+ # Block until the thread has finished.
74
+ @thread&.join
75
+ end
76
+ end
77
+
78
+ # @api private
79
+ # For internal testing purposes.
80
+ attr_reader :thread, :waker, :queue, :events, :transmitted
81
+
82
+ private
83
+
84
+ def run
85
+ loop do
86
+ events = @queue.pop
87
+ break if events.nil?
88
+
89
+ transmit(events)
90
+ @transmitted += 1
91
+ end
92
+ end
93
+
94
+ def transmit(events)
95
+ description = describe(events)
96
+
97
+ begin
98
+ response = CheckIn.transmitter.transmit(events, :format => :ndjson)
99
+
100
+ if (200...300).include?(response.code.to_i)
101
+ Appsignal.internal_logger.debug(
102
+ "Transmitted #{description}"
103
+ )
104
+ else
105
+ Appsignal.internal_logger.error(
106
+ "Failed to transmit #{description}: #{response.code} status code"
107
+ )
108
+ end
109
+ rescue => e
110
+ Appsignal.internal_logger.error("Failed to transmit #{description}: #{e.message}")
111
+ end
112
+ end
113
+
114
+ def describe(events)
115
+ if events.empty?
116
+ # This shouldn't happen.
117
+ "no check-in events"
118
+ elsif events.length > 1
119
+ "#{events.length} check-in events"
120
+ else
121
+ event = events.first
122
+ if event[:check_in_type] == "cron"
123
+ "cron check-in `#{event[:identifier] || "unknown"}` " \
124
+ "#{event[:kind] || "unknown"} event (digest #{event[:digest] || "unknown"})" \
125
+ else
126
+ "unknown check-in event"
127
+ end
128
+ end
129
+ end
130
+
131
+ # Must be called from within a `@mutex.synchronize` block.
132
+ def add_event(event)
133
+ # Remove redundant events, keeping the newly added one, which
134
+ # should be the one with the most recent timestamp.
135
+ if event[:check_in_type] == "cron"
136
+ # Remove any existing cron check-in event with the same identifier,
137
+ # digest and kind as the one we're adding.
138
+ @events.reject! do |existing_event|
139
+ next unless existing_event[:identifier] == event[:identifier] &&
140
+ existing_event[:digest] == event[:digest] &&
141
+ existing_event[:kind] == event[:kind] &&
142
+ existing_event[:check_in_type] == "cron"
143
+
144
+ Appsignal.internal_logger.debug(
145
+ "Replacing previously scheduled #{describe([existing_event])}"
146
+ )
147
+
148
+ true
149
+ end
150
+ end
151
+
152
+ @events << event
153
+ end
154
+
155
+ # Must be called from within a `@mutex.synchronize` block.
156
+ def start_waker(debounce)
157
+ stop_waker
158
+
159
+ @waker = Thread.new do
160
+ sleep(debounce)
161
+
162
+ @mutex.synchronize do
163
+ # Make sure this waker doesn't get killed, so it can push
164
+ # events and schedule a new waker.
165
+ @waker = nil
166
+ push_events
167
+ end
168
+ end
169
+ end
170
+
171
+ # Must be called from within a `@mutex.synchronize` block.
172
+ def stop_waker
173
+ @waker&.kill
174
+ @waker&.join
175
+ @waker = nil
176
+ end
177
+
178
+ # Must be called from within a `@mutex.synchronize` block.
179
+ def push_events
180
+ return if @events.empty?
181
+
182
+ # Push a copy of the events to the queue, and clear the events array.
183
+ # This ensures that `@events` always contains events that have not
184
+ # yet been pushed to the queue.
185
+ @queue.push(@events.dup)
186
+ @events.clear
187
+
188
+ start_waker(BETWEEN_TRANSMISSIONS_DEBOUNCE_SECONDS)
189
+ end
190
+ end
191
+ end
192
+ end
@@ -39,8 +39,26 @@ module Appsignal
39
39
  cron.finish
40
40
  output
41
41
  end
42
+
43
+ # @api private
44
+ def transmitter
45
+ @transmitter ||= Transmitter.new(
46
+ "#{Appsignal.config[:logging_endpoint]}/check_ins/json"
47
+ )
48
+ end
49
+
50
+ # @api private
51
+ def scheduler
52
+ @scheduler ||= Scheduler.new
53
+ end
54
+
55
+ # @api private
56
+ def stop
57
+ scheduler&.stop
58
+ end
42
59
  end
43
60
  end
44
61
  end
45
62
 
63
+ require "appsignal/check_in/scheduler"
46
64
  require "appsignal/check_in/cron"
@@ -150,7 +150,7 @@ module Appsignal
150
150
  ENV.fetch("APPSIGNAL_DIAGNOSE_ENDPOINT", DIAGNOSE_ENDPOINT),
151
151
  Appsignal.config
152
152
  )
153
- response = transmitter.transmit(:diagnose => data)
153
+ response = transmitter.transmit({ :diagnose => data })
154
154
 
155
155
  unless response.code == "200"
156
156
  puts " Error: Something went wrong while submitting the report " \
@@ -15,6 +15,13 @@ module Appsignal
15
15
 
16
16
  # @api private
17
17
  def self.add_loader_defaults(name, env: nil, root_path: nil, **options)
18
+ if Appsignal.config
19
+ Appsignal.internal_logger.warn(
20
+ "The config defaults from the '#{name}' loader are ignored since " \
21
+ "the AppSignal config has already been initialized."
22
+ )
23
+ end
24
+
18
25
  loader_defaults << {
19
26
  :name => name,
20
27
  :env => env,
@@ -24,6 +24,7 @@ module Appsignal
24
24
  class AtExitCallback
25
25
  def self.call
26
26
  error = $! # rubocop:disable Style/SpecialGlobalVars
27
+ return unless error
27
28
  return if ignored_error?(error)
28
29
  return if Appsignal::Transaction.last_errors.include?(error)
29
30
 
@@ -35,7 +36,8 @@ module Appsignal
35
36
 
36
37
  IGNORED_ERRORS = [
37
38
  # Normal exits from the application we do not need to report
38
- SystemExit
39
+ SystemExit,
40
+ SignalException
39
41
  ].freeze
40
42
 
41
43
  def self.ignored_error?(error)
@@ -7,10 +7,14 @@ module Appsignal
7
7
  register :puma
8
8
 
9
9
  def dependencies_present?
10
- defined?(::Puma)
10
+ defined?(::Puma) &&
11
+ Gem::Version.new(Puma::Const::VERSION) >= Gem::Version.new("3.0.0")
11
12
  end
12
13
 
13
14
  def install
15
+ require "appsignal/integrations/puma"
16
+ ::Puma::Server.prepend(Appsignal::Integrations::PumaServer)
17
+
14
18
  return unless defined?(::Puma::Cluster)
15
19
 
16
20
  # For clustered mode with multiple workers
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Appsignal
4
+ module Integrations
5
+ # @api private
6
+ module PumaServer
7
+ def lowlevel_error(error, env, response_status = 500)
8
+ response =
9
+ if method(:lowlevel_error).super_method.arity.abs == 3 # Puma >= 5
10
+ super
11
+ else # Puma <= 4
12
+ super(error, env)
13
+ end
14
+
15
+ unless PumaServerHelper.ignored_error?(error)
16
+ Appsignal.report_error(error) do |transaction|
17
+ Appsignal::Rack::ApplyRackRequest
18
+ .new(::Rack::Request.new(env))
19
+ .apply_to(transaction)
20
+ transaction.add_tags(
21
+ :reported_by => :puma_lowlevel_error,
22
+ :response_status => response_status
23
+ )
24
+ end
25
+ end
26
+
27
+ response
28
+ end
29
+ end
30
+
31
+ module PumaServerHelper
32
+ IGNORED_ERRORS = [
33
+ # Ignore internal Puma Client IO errors
34
+ # https://github.com/puma/puma/blob/9ee922d28e1fffd02c1d5480a9e13376f92f46a3/lib/puma/server.rb#L536-L544
35
+ "Puma::MiniSSL::SSLError",
36
+ "Puma::HttpParserError",
37
+ "Puma::HttpParserError501"
38
+ ].freeze
39
+
40
+ def self.ignored_error?(error)
41
+ IGNORED_ERRORS.include?(error.class.to_s)
42
+ end
43
+ end
44
+ end
45
+ end