appsignal 3.7.5 → 3.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/create_release_from_tag.yml +62 -0
- data/CHANGELOG.md +37 -0
- data/ext/agent.rb +27 -27
- data/gemfiles/grape.gemfile +3 -4
- data/lib/appsignal/heartbeat.rb +1 -13
- data/lib/appsignal/helpers/heartbeats.rb +44 -0
- data/lib/appsignal/integrations/railtie.rb +5 -0
- data/lib/appsignal/logger.rb +9 -0
- data/lib/appsignal/probes.rb +16 -4
- data/lib/appsignal/rack/event_handler.rb +90 -0
- data/lib/appsignal/rack/rails_instrumentation.rb +29 -14
- data/lib/appsignal/version.rb +1 -1
- data/lib/appsignal.rb +5 -2
- data/spec/lib/appsignal/logger_spec.rb +40 -0
- data/spec/lib/appsignal/probes_spec.rb +24 -5
- data/spec/lib/appsignal/rack/event_handler_spec.rb +218 -0
- data/spec/lib/appsignal/rack/rails_instrumentation_spec.rb +67 -33
- data/spec/lib/appsignal/transaction_spec.rb +0 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f68ce83624657d12b65c203950c7a92681d621bc9f5644fcc61d4123417e8e93
|
4
|
+
data.tar.gz: ecda706a29b6aea7d52d3620cebb984495c85937acf561a596826fd57c457095
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ec9d7754da4808d85efd335d8e9c00bca8f0e2c16f7a4a9246b9bd9d679519c7550202c1d2f809c786e117cde31c76c8c3405664b9878b9b8c61724a05dca14
|
7
|
+
data.tar.gz: 955a45291f05e0df7456ae7bef0c7d9be55824b764429a6fffd72985ee252dcf83893e824a610d086c82c0050ae14bb6ddebc6baaf600946c1d50ce55a3ae4d7
|
@@ -0,0 +1,62 @@
|
|
1
|
+
name: "Create release from tag"
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
tags:
|
6
|
+
- "v**"
|
7
|
+
|
8
|
+
permissions:
|
9
|
+
contents: write
|
10
|
+
actions: write
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
release:
|
14
|
+
name: "Create release"
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
env:
|
17
|
+
PACKAGE_NAME: "Ruby gem"
|
18
|
+
CHANGELOG_CATEGORY: "Ruby"
|
19
|
+
CHANGELOG_LINK: "https://github.com/appsignal/appsignal-ruby/blob/main/CHANGELOG.md"
|
20
|
+
steps:
|
21
|
+
- name: Checkout repository at tag
|
22
|
+
uses: actions/checkout@v4
|
23
|
+
with:
|
24
|
+
ref: "${{ github.ref }}"
|
25
|
+
|
26
|
+
- name: Get tag name
|
27
|
+
run: |
|
28
|
+
echo "TAG_NAME=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
29
|
+
|
30
|
+
- name: Get changelog contents from tag
|
31
|
+
run: |
|
32
|
+
# Use sed to remove everything after "-----BEGIN PGP SIGNATURE-----" if it's present
|
33
|
+
# and also always remove the last line of the git show output
|
34
|
+
git show --format=oneline --no-color --no-patch "${{ env.TAG_NAME }}" \
|
35
|
+
| sed '1,2d' \
|
36
|
+
| sed '$d' \
|
37
|
+
| sed '/-----BEGIN PGP SIGNATURE-----/,$d' \
|
38
|
+
> CHANGELOG_TEXT.txt
|
39
|
+
|
40
|
+
echo "" >> CHANGELOG_TEXT.txt
|
41
|
+
echo "" >> CHANGELOG_TEXT.txt
|
42
|
+
|
43
|
+
TAG_NAME_FOR_LINK=$(echo "${{ env.TAG_NAME }}" | sed 's/^v//' | tr -d '.')
|
44
|
+
echo "View the [$PACKAGE_NAME ${{ env.TAG_NAME }} changelog]($CHANGELOG_LINK#$TAG_NAME_FOR_LINK) for more information." >> CHANGELOG_TEXT.txt
|
45
|
+
|
46
|
+
- name: Submit changelog entry
|
47
|
+
run: |
|
48
|
+
# Prepare JSON payload using jq to ensure proper escaping
|
49
|
+
payload=$(jq -n \
|
50
|
+
--arg title "$PACKAGE_NAME ${{ env.TAG_NAME }}" \
|
51
|
+
--arg category "$CHANGELOG_CATEGORY" \
|
52
|
+
--arg version "$(echo "${{ env.TAG_NAME }}" | sed 's/^v//')" \
|
53
|
+
--arg changelog "$(cat CHANGELOG_TEXT.txt)" \
|
54
|
+
--arg assignee "${{ github.actor }}" \
|
55
|
+
'{ref: "main", inputs: {title: $title, category: $category, version: $version, changelog: $changelog, assignee: $assignee}}')
|
56
|
+
|
57
|
+
curl -X POST \
|
58
|
+
-H "Authorization: token ${{ secrets.INTEGRATIONS_CHANGELOG_TOKEN }}" \
|
59
|
+
-H "Accept: application/vnd.github+json" \
|
60
|
+
--fail-with-body \
|
61
|
+
https://api.github.com/repos/appsignal/appsignal.com/actions/workflows/102125282/dispatches \
|
62
|
+
-d "$payload"
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,42 @@
|
|
1
1
|
# AppSignal for Ruby gem Changelog
|
2
2
|
|
3
|
+
## 3.8.1
|
4
|
+
|
5
|
+
_Published on 2024-06-17._
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- [5459a021](https://github.com/appsignal/appsignal-ruby/commit/5459a021d7d4bbbd09a0dcbdf5f3af7bf861b6f5) patch - Report the response status for Rails requests as the `response_status` tag on samples, e.g. 200, 301, 500. This tag is visible on the sample detail page.
|
10
|
+
|
11
|
+
The response status is also reported as the `response_status` metric.
|
12
|
+
|
13
|
+
## 3.8.0
|
14
|
+
|
15
|
+
_Published on 2024-06-17._
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
- [ca53b043](https://github.com/appsignal/appsignal-ruby/commit/ca53b04360ae123498640d043ee7ba74efc4b295) minor - Report the time spent in Rails middleware as part of the request duration. The AppSignal Rack middleware is now higher in the middleware stack and reports more time of the request to give insights in how long other middleware took. This is reported under the new `process_request.rack` event in the event timeline.
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
|
23
|
+
- [37fbae5a](https://github.com/appsignal/appsignal-ruby/commit/37fbae5a0f1a4e964baceb21837e5d5f0cf903c0) patch - Fix ArgumentError being raised on Ruby logger and Rails.logger error calls. This fixes the error from being raised from within the AppSignal Ruby gem.
|
24
|
+
Please do not use this for error reporting. We recommend using our error reporting feature instead to be notified of new errors. Read more on [exception handling in Ruby with our Ruby gem](https://docs.appsignal.com/ruby/instrumentation/exception-handling.html).
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
# No longer raises an error
|
28
|
+
Rails.logger.error StandardError.new("StandardError log message")
|
29
|
+
```
|
30
|
+
|
31
|
+
## 3.7.6
|
32
|
+
|
33
|
+
_Published on 2024-06-11._
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
|
37
|
+
- [704a7d29](https://github.com/appsignal/appsignal-ruby/commit/704a7d29ae428f93549000a2c606bff948040c96) patch - When the minutely probes thread takes more than 60 seconds to run all the registered probes, log an error. This helps find issues with the metrics reported by the probes not being accurately reported for every minute.
|
38
|
+
- [5f4cc8be](https://github.com/appsignal/appsignal-ruby/commit/5f4cc8beb0ad88a0a58265d990626a7ee39fddd3) patch - Internal agent changes for the Ruby gem.
|
39
|
+
|
3
40
|
## 3.7.5
|
4
41
|
|
5
42
|
_Published on 2024-05-14._
|
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.
|
9
|
+
"version" => "0.35.12",
|
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" => "
|
17
|
+
"checksum" => "61210c40be70e0616a356d06040961b096e2d47332021a52f3779912a9fe0e4c",
|
18
18
|
"filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
|
19
19
|
},
|
20
20
|
"dynamic" => {
|
21
|
-
"checksum" => "
|
21
|
+
"checksum" => "dd165289445c80e4dcc148ea09c613b23b001c90ad885aef1de08db65ab5bf1c",
|
22
22
|
"filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
|
23
23
|
}
|
24
24
|
},
|
25
25
|
"universal-darwin" => {
|
26
26
|
"static" => {
|
27
|
-
"checksum" => "
|
27
|
+
"checksum" => "61210c40be70e0616a356d06040961b096e2d47332021a52f3779912a9fe0e4c",
|
28
28
|
"filename" => "appsignal-x86_64-darwin-all-static.tar.gz"
|
29
29
|
},
|
30
30
|
"dynamic" => {
|
31
|
-
"checksum" => "
|
31
|
+
"checksum" => "dd165289445c80e4dcc148ea09c613b23b001c90ad885aef1de08db65ab5bf1c",
|
32
32
|
"filename" => "appsignal-x86_64-darwin-all-dynamic.tar.gz"
|
33
33
|
}
|
34
34
|
},
|
35
35
|
"aarch64-darwin" => {
|
36
36
|
"static" => {
|
37
|
-
"checksum" => "
|
37
|
+
"checksum" => "9b97c42561450f9af9ae63816d32b8db69be6f2745226f63d6eada4369c68a20",
|
38
38
|
"filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
|
39
39
|
},
|
40
40
|
"dynamic" => {
|
41
|
-
"checksum" => "
|
41
|
+
"checksum" => "0c81959ab5de3c98c70b7e308826d7deee8e208ee47b7637d505b0a1d70af8c4",
|
42
42
|
"filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
|
43
43
|
}
|
44
44
|
},
|
45
45
|
"arm64-darwin" => {
|
46
46
|
"static" => {
|
47
|
-
"checksum" => "
|
47
|
+
"checksum" => "9b97c42561450f9af9ae63816d32b8db69be6f2745226f63d6eada4369c68a20",
|
48
48
|
"filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
|
49
49
|
},
|
50
50
|
"dynamic" => {
|
51
|
-
"checksum" => "
|
51
|
+
"checksum" => "0c81959ab5de3c98c70b7e308826d7deee8e208ee47b7637d505b0a1d70af8c4",
|
52
52
|
"filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
|
53
53
|
}
|
54
54
|
},
|
55
55
|
"arm-darwin" => {
|
56
56
|
"static" => {
|
57
|
-
"checksum" => "
|
57
|
+
"checksum" => "9b97c42561450f9af9ae63816d32b8db69be6f2745226f63d6eada4369c68a20",
|
58
58
|
"filename" => "appsignal-aarch64-darwin-all-static.tar.gz"
|
59
59
|
},
|
60
60
|
"dynamic" => {
|
61
|
-
"checksum" => "
|
61
|
+
"checksum" => "0c81959ab5de3c98c70b7e308826d7deee8e208ee47b7637d505b0a1d70af8c4",
|
62
62
|
"filename" => "appsignal-aarch64-darwin-all-dynamic.tar.gz"
|
63
63
|
}
|
64
64
|
},
|
65
65
|
"aarch64-linux" => {
|
66
66
|
"static" => {
|
67
|
-
"checksum" => "
|
67
|
+
"checksum" => "358db07cfa85d6bd048bd2bb05fc9607d4fe0d4396fd023d658e945e4a675fba",
|
68
68
|
"filename" => "appsignal-aarch64-linux-all-static.tar.gz"
|
69
69
|
},
|
70
70
|
"dynamic" => {
|
71
|
-
"checksum" => "
|
71
|
+
"checksum" => "9e76651d52f78882ab126d94a8af61794d1ce0cdffa6dd01a3e032599a1b2796",
|
72
72
|
"filename" => "appsignal-aarch64-linux-all-dynamic.tar.gz"
|
73
73
|
}
|
74
74
|
},
|
75
75
|
"i686-linux" => {
|
76
76
|
"static" => {
|
77
|
-
"checksum" => "
|
77
|
+
"checksum" => "315bf1fc5d9c97b6f26e61f5e39919e0ba425b1d96ea6243cdb2f650487c407e",
|
78
78
|
"filename" => "appsignal-i686-linux-all-static.tar.gz"
|
79
79
|
},
|
80
80
|
"dynamic" => {
|
81
|
-
"checksum" => "
|
81
|
+
"checksum" => "8cf0b5e6ef70a7758b98457012bfebb7964acb2d47648f3817c9f32a70bc0ab1",
|
82
82
|
"filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
|
83
83
|
}
|
84
84
|
},
|
85
85
|
"x86-linux" => {
|
86
86
|
"static" => {
|
87
|
-
"checksum" => "
|
87
|
+
"checksum" => "315bf1fc5d9c97b6f26e61f5e39919e0ba425b1d96ea6243cdb2f650487c407e",
|
88
88
|
"filename" => "appsignal-i686-linux-all-static.tar.gz"
|
89
89
|
},
|
90
90
|
"dynamic" => {
|
91
|
-
"checksum" => "
|
91
|
+
"checksum" => "8cf0b5e6ef70a7758b98457012bfebb7964acb2d47648f3817c9f32a70bc0ab1",
|
92
92
|
"filename" => "appsignal-i686-linux-all-dynamic.tar.gz"
|
93
93
|
}
|
94
94
|
},
|
95
95
|
"x86_64-linux" => {
|
96
96
|
"static" => {
|
97
|
-
"checksum" => "
|
97
|
+
"checksum" => "3fe42df2a52706c23f967b8421ac816fa37a38998bd24b1d6aafd59a324b23ff",
|
98
98
|
"filename" => "appsignal-x86_64-linux-all-static.tar.gz"
|
99
99
|
},
|
100
100
|
"dynamic" => {
|
101
|
-
"checksum" => "
|
101
|
+
"checksum" => "8781f0a4c4810229f19000ebb882b7d8e5e0440ffcf8e5ffea7d68d082be8e69",
|
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" => "
|
107
|
+
"checksum" => "1fe0ed0c0ca51eccd4c2ec3bb94bb1834bae19bc2c185b67c3f940f704abe9fc",
|
108
108
|
"filename" => "appsignal-x86_64-linux-musl-all-static.tar.gz"
|
109
109
|
},
|
110
110
|
"dynamic" => {
|
111
|
-
"checksum" => "
|
111
|
+
"checksum" => "f241f905555e17178a72b217dc373ead33daa97757e100b895f2d1318e4dce0d",
|
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" => "
|
117
|
+
"checksum" => "5027782008872f8091608cc5531a6dd90f0652e9ebb0404f7e86eb73f0807ba0",
|
118
118
|
"filename" => "appsignal-aarch64-linux-musl-all-static.tar.gz"
|
119
119
|
},
|
120
120
|
"dynamic" => {
|
121
|
-
"checksum" => "
|
121
|
+
"checksum" => "ae3147790f25cef200142f61eb6ce1f8b3ac5fa2647ad7a4234f1bbb976bde98",
|
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" => "
|
127
|
+
"checksum" => "1337268caaddd66bb170298968d50d76cc66f17e808c46a677ba00d1b78eb317",
|
128
128
|
"filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
|
129
129
|
},
|
130
130
|
"dynamic" => {
|
131
|
-
"checksum" => "
|
131
|
+
"checksum" => "2b93af244d1d214b59c2657677bf96445f67cade2fa1bfd6cda78c8bec75cbca",
|
132
132
|
"filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
|
133
133
|
}
|
134
134
|
},
|
135
135
|
"amd64-freebsd" => {
|
136
136
|
"static" => {
|
137
|
-
"checksum" => "
|
137
|
+
"checksum" => "1337268caaddd66bb170298968d50d76cc66f17e808c46a677ba00d1b78eb317",
|
138
138
|
"filename" => "appsignal-x86_64-freebsd-all-static.tar.gz"
|
139
139
|
},
|
140
140
|
"dynamic" => {
|
141
|
-
"checksum" => "
|
141
|
+
"checksum" => "2b93af244d1d214b59c2657677bf96445f67cade2fa1bfd6cda78c8bec75cbca",
|
142
142
|
"filename" => "appsignal-x86_64-freebsd-all-dynamic.tar.gz"
|
143
143
|
}
|
144
144
|
}
|
data/gemfiles/grape.gemfile
CHANGED
data/lib/appsignal/heartbeat.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
module Appsignal
|
4
4
|
class Heartbeat
|
5
5
|
class << self
|
6
|
+
# @api private
|
6
7
|
def transmitter
|
7
8
|
@transmitter ||= Appsignal::Transmitter.new(
|
8
9
|
"#{Appsignal.config[:logging_endpoint]}/heartbeats/json"
|
@@ -55,17 +56,4 @@ module Appsignal
|
|
55
56
|
Appsignal.internal_logger.error("Failed to transmit heartbeat event: #{e}")
|
56
57
|
end
|
57
58
|
end
|
58
|
-
|
59
|
-
def self.heartbeat(name)
|
60
|
-
heartbeat = Appsignal::Heartbeat.new(:name => name)
|
61
|
-
output = nil
|
62
|
-
|
63
|
-
if block_given?
|
64
|
-
heartbeat.start
|
65
|
-
output = yield
|
66
|
-
end
|
67
|
-
|
68
|
-
heartbeat.finish
|
69
|
-
output
|
70
|
-
end
|
71
59
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Helpers
|
5
|
+
module Heartbeats
|
6
|
+
# Track heartbeats
|
7
|
+
#
|
8
|
+
# Track the execution of certain processes by sending a hearbeat.
|
9
|
+
#
|
10
|
+
# To track the duration of a piece of code, pass a block to {.heartbeat}
|
11
|
+
# to report both when the process starts, and when it finishes.
|
12
|
+
#
|
13
|
+
# If an exception is raised within the block, the finish event will not
|
14
|
+
# be reported, triggering a notification about the missing heartbeat. The
|
15
|
+
# exception will bubble outside of the heartbeat block.
|
16
|
+
#
|
17
|
+
# @example Send a heartbeat
|
18
|
+
# Appsignal.heartbeat("send_invoices")
|
19
|
+
#
|
20
|
+
# @example Send a heartbeat with duration
|
21
|
+
# Appsignal.heartbeat("send_invoices") do
|
22
|
+
# # your code
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @param name [String] name of the heartbeat to report.
|
26
|
+
# @yield the block to monitor.
|
27
|
+
# @return [void]
|
28
|
+
# @since 3.7.0
|
29
|
+
# @see https://docs.appsignal.com/heartbeats
|
30
|
+
def heartbeat(name)
|
31
|
+
heartbeat = Appsignal::Heartbeat.new(:name => name)
|
32
|
+
output = nil
|
33
|
+
|
34
|
+
if block_given?
|
35
|
+
heartbeat.start
|
36
|
+
output = yield
|
37
|
+
end
|
38
|
+
|
39
|
+
heartbeat.finish
|
40
|
+
output
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -29,6 +29,11 @@ module Appsignal
|
|
29
29
|
# Start logger
|
30
30
|
Appsignal.start_logger
|
31
31
|
|
32
|
+
app.middleware.insert(
|
33
|
+
0,
|
34
|
+
::Rack::Events,
|
35
|
+
[Appsignal::Rack::EventHandler.new]
|
36
|
+
)
|
32
37
|
app.middleware.insert_after(
|
33
38
|
ActionDispatch::DebugExceptions,
|
34
39
|
Appsignal::Rack::RailsInstrumentation
|
data/lib/appsignal/logger.rb
CHANGED
@@ -53,6 +53,13 @@ module Appsignal
|
|
53
53
|
|
54
54
|
message = formatter.call(severity, Time.now, group, message) if formatter
|
55
55
|
|
56
|
+
unless message.is_a?(String)
|
57
|
+
Appsignal.internal_logger.warn(
|
58
|
+
"Logger message was ignored, because it was not a String: #{message.inspect}"
|
59
|
+
)
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
56
63
|
Appsignal::Extension.log(
|
57
64
|
group,
|
58
65
|
SEVERITY_MAP.fetch(severity, 0),
|
@@ -112,6 +119,8 @@ module Appsignal
|
|
112
119
|
message = yield if message.nil? && block_given?
|
113
120
|
return if message.nil?
|
114
121
|
|
122
|
+
message = "#{message.class}: #{message.message}" if message.is_a?(Exception)
|
123
|
+
|
115
124
|
add_with_attributes(ERROR, message, @group, attributes)
|
116
125
|
end
|
117
126
|
|
data/lib/appsignal/probes.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Appsignal
|
4
|
-
module Probes
|
4
|
+
module Probes # rubocop:disable Metrics/ModuleLength
|
5
|
+
ITERATION_IN_SECONDS = 60
|
6
|
+
|
5
7
|
class ProbeCollection
|
6
8
|
def initialize
|
7
9
|
@probes = {}
|
@@ -180,6 +182,7 @@ module Appsignal
|
|
180
182
|
sleep initial_wait_time
|
181
183
|
initialize_probes
|
182
184
|
loop do
|
185
|
+
start_time = Time.now
|
183
186
|
logger = Appsignal.internal_logger
|
184
187
|
mutex.synchronize do
|
185
188
|
logger.debug("Gathering minutely metrics with #{probe_instances.count} probes")
|
@@ -191,6 +194,15 @@ module Appsignal
|
|
191
194
|
logger.debug ex.backtrace.join("\n")
|
192
195
|
end
|
193
196
|
end
|
197
|
+
end_time = Time.now
|
198
|
+
duration = end_time - start_time
|
199
|
+
if duration >= ITERATION_IN_SECONDS
|
200
|
+
logger.error(
|
201
|
+
"The minutely probes took more than 60 seconds. " \
|
202
|
+
"The probes should not take this long as metrics will not " \
|
203
|
+
"be accurately reported."
|
204
|
+
)
|
205
|
+
end
|
194
206
|
sleep wait_time
|
195
207
|
end
|
196
208
|
end
|
@@ -214,16 +226,16 @@ module Appsignal
|
|
214
226
|
|
215
227
|
# @api private
|
216
228
|
def wait_time
|
217
|
-
|
229
|
+
ITERATION_IN_SECONDS - Time.now.sec
|
218
230
|
end
|
219
231
|
|
220
232
|
private
|
221
233
|
|
222
234
|
def initial_wait_time
|
223
|
-
remaining_seconds =
|
235
|
+
remaining_seconds = ITERATION_IN_SECONDS - Time.now.sec
|
224
236
|
return remaining_seconds if remaining_seconds > 30
|
225
237
|
|
226
|
-
remaining_seconds +
|
238
|
+
remaining_seconds + ITERATION_IN_SECONDS
|
227
239
|
end
|
228
240
|
|
229
241
|
def initialize_probes
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Appsignal
|
4
|
+
module Rack
|
5
|
+
APPSIGNAL_TRANSACTION = "appsignal.transaction"
|
6
|
+
RACK_AFTER_REPLY = "rack.after_reply"
|
7
|
+
|
8
|
+
class EventHandler
|
9
|
+
include ::Rack::Events::Abstract
|
10
|
+
|
11
|
+
def self.safe_execution(name)
|
12
|
+
yield
|
13
|
+
rescue => e
|
14
|
+
Appsignal.internal_logger.error(
|
15
|
+
"Error occurred in #{name}: #{e.class}: #{e}: #{e.backtrace}"
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def on_start(request, _response)
|
20
|
+
self.class.safe_execution("Appsignal::Rack::EventHandler#on_start") do
|
21
|
+
transaction = Appsignal::Transaction.create(
|
22
|
+
SecureRandom.uuid,
|
23
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
24
|
+
request
|
25
|
+
)
|
26
|
+
request.env[APPSIGNAL_TRANSACTION] = transaction
|
27
|
+
|
28
|
+
request.env[RACK_AFTER_REPLY] ||= []
|
29
|
+
request.env[RACK_AFTER_REPLY] << proc do
|
30
|
+
Appsignal::Rack::EventHandler
|
31
|
+
.safe_execution("Appsignal::Rack::EventHandler's after_reply") do
|
32
|
+
transaction.finish_event("process_request.rack", "", "")
|
33
|
+
transaction.set_http_or_background_queue_start
|
34
|
+
|
35
|
+
# Make sure the current transaction is always closed when the request
|
36
|
+
# is finished. This is a fallback for in case the `on_finish`
|
37
|
+
# callback is not called. This is supported by servers like Puma and
|
38
|
+
# Unicorn.
|
39
|
+
#
|
40
|
+
# The EventHandler.on_finish callback should be called first, this is
|
41
|
+
# just a fallback if that doesn't get called.
|
42
|
+
Appsignal::Transaction.complete_current!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
transaction.start_event
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_error(request, _response, error)
|
50
|
+
self.class.safe_execution("Appsignal::Rack::EventHandler#on_error") do
|
51
|
+
transaction = request.env[APPSIGNAL_TRANSACTION]
|
52
|
+
return unless transaction
|
53
|
+
|
54
|
+
transaction.set_error(error)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_finish(request, response)
|
59
|
+
self.class.safe_execution("Appsignal::Rack::EventHandler#on_finish") do
|
60
|
+
transaction = request.env[APPSIGNAL_TRANSACTION]
|
61
|
+
return unless transaction
|
62
|
+
|
63
|
+
transaction.finish_event("process_request.rack", "", "")
|
64
|
+
transaction.set_tags(:response_status => response.status)
|
65
|
+
transaction.set_http_or_background_queue_start
|
66
|
+
Appsignal.increment_counter(
|
67
|
+
:response_status,
|
68
|
+
1,
|
69
|
+
:status => response.status,
|
70
|
+
:namespace => format_namespace(transaction.namespace)
|
71
|
+
)
|
72
|
+
|
73
|
+
# Make sure the current transaction is always closed when the request
|
74
|
+
# is finished
|
75
|
+
Appsignal::Transaction.complete_current!
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def format_namespace(namespace)
|
82
|
+
if namespace == Appsignal::Transaction::HTTP_REQUEST
|
83
|
+
:web
|
84
|
+
else
|
85
|
+
namespace
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -22,13 +22,14 @@ module Appsignal
|
|
22
22
|
|
23
23
|
def call_with_appsignal_monitoring(env)
|
24
24
|
request = ActionDispatch::Request.new(env)
|
25
|
-
transaction =
|
26
|
-
|
27
|
-
Appsignal::Transaction::
|
28
|
-
request,
|
29
|
-
:params_method => :filtered_parameters
|
25
|
+
transaction = env.fetch(
|
26
|
+
Appsignal::Rack::APPSIGNAL_TRANSACTION,
|
27
|
+
Appsignal::Transaction::NilTransaction.new
|
30
28
|
)
|
29
|
+
|
31
30
|
begin
|
31
|
+
transaction.params = fetch_params(request)
|
32
|
+
|
32
33
|
@app.call(env)
|
33
34
|
rescue Exception => error # rubocop:disable Lint/RescueException
|
34
35
|
transaction.set_error(error)
|
@@ -38,19 +39,33 @@ module Appsignal
|
|
38
39
|
if controller
|
39
40
|
transaction.set_action_if_nil("#{controller.class}##{controller.action_name}")
|
40
41
|
end
|
41
|
-
|
42
|
+
request_id = fetch_request_id(env)
|
43
|
+
transaction.set_tags(:request_id => request_id) if request_id
|
42
44
|
transaction.set_metadata("path", request.path)
|
43
|
-
|
44
|
-
|
45
|
-
rescue => error
|
46
|
-
Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
|
47
|
-
end
|
48
|
-
Appsignal::Transaction.complete_current!
|
45
|
+
request_method = fetch_request_method(request)
|
46
|
+
transaction.set_metadata("method", request_method) if request_method
|
49
47
|
end
|
50
48
|
end
|
51
49
|
|
52
|
-
def
|
53
|
-
env["action_dispatch.request_id"]
|
50
|
+
def fetch_request_id(env)
|
51
|
+
env["action_dispatch.request_id"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def fetch_params(request)
|
55
|
+
return unless request.respond_to?(:filtered_parameters)
|
56
|
+
|
57
|
+
request.filtered_parameters
|
58
|
+
rescue => error
|
59
|
+
# Getting params from the request has been know to fail.
|
60
|
+
Appsignal.internal_logger.debug "Exception while getting Rails params: #{error}"
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def fetch_request_method(request)
|
65
|
+
request.request_method
|
66
|
+
rescue => error
|
67
|
+
Appsignal.internal_logger.error("Unable to report HTTP request method: '#{error}'")
|
68
|
+
nil
|
54
69
|
end
|
55
70
|
end
|
56
71
|
end
|
data/lib/appsignal/version.rb
CHANGED
data/lib/appsignal.rb
CHANGED
@@ -6,6 +6,7 @@ require "stringio"
|
|
6
6
|
|
7
7
|
require "appsignal/logger"
|
8
8
|
require "appsignal/utils/stdout_and_logger_message"
|
9
|
+
require "appsignal/helpers/heartbeats"
|
9
10
|
require "appsignal/helpers/instrumentation"
|
10
11
|
require "appsignal/helpers/metrics"
|
11
12
|
|
@@ -17,6 +18,7 @@ require "appsignal/helpers/metrics"
|
|
17
18
|
# {Appsignal::Helpers::Metrics}) for ease of use.
|
18
19
|
module Appsignal
|
19
20
|
class << self
|
21
|
+
include Helpers::Heartbeats
|
20
22
|
include Helpers::Instrumentation
|
21
23
|
include Helpers::Metrics
|
22
24
|
|
@@ -95,11 +97,11 @@ module Appsignal
|
|
95
97
|
# @since 0.7.0
|
96
98
|
def start
|
97
99
|
unless extension_loaded?
|
98
|
-
internal_logger.info("Not starting
|
100
|
+
internal_logger.info("Not starting AppSignal, extension is not loaded")
|
99
101
|
return
|
100
102
|
end
|
101
103
|
|
102
|
-
internal_logger.debug("
|
104
|
+
internal_logger.debug("Loading AppSignal gem")
|
103
105
|
|
104
106
|
@config ||= Config.new(
|
105
107
|
Dir.pwd,
|
@@ -325,5 +327,6 @@ require "appsignal/integrations/railtie" if defined?(::Rails)
|
|
325
327
|
require "appsignal/transaction"
|
326
328
|
require "appsignal/version"
|
327
329
|
require "appsignal/rack/generic_instrumentation"
|
330
|
+
require "appsignal/rack/event_handler"
|
328
331
|
require "appsignal/transmitter"
|
329
332
|
require "appsignal/heartbeat"
|
@@ -1,6 +1,12 @@
|
|
1
1
|
describe Appsignal::Logger do
|
2
|
+
let(:log_stream) { StringIO.new }
|
3
|
+
let(:logs) { log_contents(log_stream) }
|
2
4
|
let(:logger) { Appsignal::Logger.new("group", :level => ::Logger::DEBUG) }
|
3
5
|
|
6
|
+
before do
|
7
|
+
Appsignal.internal_logger = test_logger(log_stream)
|
8
|
+
end
|
9
|
+
|
4
10
|
it "should not create a logger with a nil group" do
|
5
11
|
expect do
|
6
12
|
Appsignal::Logger.new(nil)
|
@@ -14,6 +20,19 @@ describe Appsignal::Logger do
|
|
14
20
|
logger.add(::Logger::INFO, "Log message")
|
15
21
|
end
|
16
22
|
|
23
|
+
it "does not log a message that's not a String" do
|
24
|
+
expect(Appsignal::Extension).to_not receive(:log)
|
25
|
+
logger.add(::Logger::INFO, 123)
|
26
|
+
logger.add(::Logger::INFO, {})
|
27
|
+
logger.add(::Logger::INFO, [])
|
28
|
+
expect(logs)
|
29
|
+
.to contains_log(:warn, "Logger message was ignored, because it was not a String: 123")
|
30
|
+
expect(logs)
|
31
|
+
.to contains_log(:warn, "Logger message was ignored, because it was not a String: []")
|
32
|
+
expect(logs)
|
33
|
+
.to contains_log(:warn, "Logger message was ignored, because it was not a String: {}")
|
34
|
+
end
|
35
|
+
|
17
36
|
it "should log with a block" do
|
18
37
|
expect(Appsignal::Extension).to receive(:log)
|
19
38
|
.with("group", 3, 0, "Log message", instance_of(Appsignal::Extension::Data))
|
@@ -162,4 +181,25 @@ describe Appsignal::Logger do
|
|
162
181
|
end
|
163
182
|
end
|
164
183
|
end
|
184
|
+
|
185
|
+
describe "#error with exception object" do
|
186
|
+
it "logs the exception class and its message" do
|
187
|
+
error =
|
188
|
+
begin
|
189
|
+
raise ExampleStandardError, "oh no!"
|
190
|
+
rescue => e
|
191
|
+
# This makes the exception include a backtrace, so we can assert it's NOT included
|
192
|
+
e
|
193
|
+
end
|
194
|
+
expect(Appsignal::Extension).to receive(:log)
|
195
|
+
.with(
|
196
|
+
"group",
|
197
|
+
6,
|
198
|
+
0,
|
199
|
+
"ExampleStandardError: oh no!",
|
200
|
+
instance_of(Appsignal::Extension::Data)
|
201
|
+
)
|
202
|
+
logger.error(error)
|
203
|
+
end
|
204
|
+
end
|
165
205
|
end
|
@@ -215,6 +215,29 @@ describe Appsignal::Probes do
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
+
context "with a probe that takes 60 seconds" do
|
219
|
+
it "logs an error and continues calling the probes every <wait_time>" do
|
220
|
+
stub_const("Appsignal::Probes::ITERATION_IN_SECONDS", 0.2)
|
221
|
+
calls = 0
|
222
|
+
probe = lambda do
|
223
|
+
calls += 1
|
224
|
+
sleep 0.2
|
225
|
+
end
|
226
|
+
Appsignal::Probes.register :my_probe, probe
|
227
|
+
Appsignal::Probes.register :other_probe, lambda {}
|
228
|
+
Appsignal::Probes.start
|
229
|
+
|
230
|
+
wait_for("enough probe calls") { calls >= 2 }
|
231
|
+
|
232
|
+
expect(log).to contains_log(
|
233
|
+
:error,
|
234
|
+
"The minutely probes took more than 60 seconds. " \
|
235
|
+
"The probes should not take this long as metrics will not " \
|
236
|
+
"be accurately reported."
|
237
|
+
)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
218
241
|
it "ensures only one minutely probes thread is active at a time" do
|
219
242
|
alive_thread_counter = proc { Thread.list.reject { |t| t.status == "dead" }.length }
|
220
243
|
probe = MockProbe.new
|
@@ -240,10 +263,6 @@ describe Appsignal::Probes do
|
|
240
263
|
end
|
241
264
|
|
242
265
|
context "with thread already started" do
|
243
|
-
before do
|
244
|
-
allow(Appsignal::Probes).to receive(:initial_wait_time).and_return(0.00001)
|
245
|
-
end
|
246
|
-
|
247
266
|
it "auto starts probes added after the thread is started" do
|
248
267
|
Appsignal::Probes.start
|
249
268
|
wait_for("Probes thread to start") { Appsignal::Probes.started? }
|
@@ -290,7 +309,7 @@ describe Appsignal::Probes do
|
|
290
309
|
|
291
310
|
describe ".stop" do
|
292
311
|
before do
|
293
|
-
|
312
|
+
speed_up_tests!
|
294
313
|
end
|
295
314
|
|
296
315
|
it "stops the minutely thread" do
|
@@ -0,0 +1,218 @@
|
|
1
|
+
describe Appsignal::Rack::EventHandler do
|
2
|
+
let(:queue_start_time) { fixed_time * 1_000 }
|
3
|
+
let(:env) do
|
4
|
+
{
|
5
|
+
"HTTP_X_REQUEST_START" => "t=#{queue_start_time.to_i}", # in milliseconds
|
6
|
+
"REQUEST_METHOD" => "GET",
|
7
|
+
"PATH_INFO" => "/path"
|
8
|
+
}
|
9
|
+
end
|
10
|
+
let(:request) { Rack::Request.new(env) }
|
11
|
+
let(:response) { nil }
|
12
|
+
let(:log_stream) { StringIO.new }
|
13
|
+
let(:log) { log_contents(log_stream) }
|
14
|
+
before do
|
15
|
+
start_agent
|
16
|
+
Appsignal.internal_logger = test_logger(log_stream)
|
17
|
+
end
|
18
|
+
around { |example| keep_transactions { example.run } }
|
19
|
+
|
20
|
+
def on_start
|
21
|
+
described_class.new.on_start(request, response)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#on_start" do
|
25
|
+
it "creates a new transaction" do
|
26
|
+
expect { on_start }.to change { created_transactions.length }.by(1)
|
27
|
+
|
28
|
+
transaction = last_transaction
|
29
|
+
expect(transaction.to_h).to include(
|
30
|
+
"id" => kind_of(String),
|
31
|
+
"namespace" => Appsignal::Transaction::HTTP_REQUEST
|
32
|
+
)
|
33
|
+
|
34
|
+
expect(Appsignal::Transaction.current).to eq(last_transaction)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "registers transaction on the request environment" do
|
38
|
+
on_start
|
39
|
+
|
40
|
+
expect(request.env[Appsignal::Rack::APPSIGNAL_TRANSACTION])
|
41
|
+
.to eq(last_transaction)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "registers an rack.after_reply callback that completes the transaction" do
|
45
|
+
request.env[Appsignal::Rack::RACK_AFTER_REPLY] = []
|
46
|
+
expect do
|
47
|
+
on_start
|
48
|
+
end.to change { request.env[Appsignal::Rack::RACK_AFTER_REPLY].length }.by(1)
|
49
|
+
|
50
|
+
expect(Appsignal::Transaction.current).to eq(last_transaction)
|
51
|
+
|
52
|
+
callback = request.env[Appsignal::Rack::RACK_AFTER_REPLY].first
|
53
|
+
callback.call
|
54
|
+
|
55
|
+
expect(Appsignal::Transaction.current).to be_kind_of(Appsignal::Transaction::NilTransaction)
|
56
|
+
|
57
|
+
expect(last_transaction.ext.queue_start).to eq(queue_start_time)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "logs errors from rack.after_reply callbacks" do
|
61
|
+
on_start
|
62
|
+
|
63
|
+
expect(request.env[Appsignal::Rack::APPSIGNAL_TRANSACTION])
|
64
|
+
.to receive(:finish_event)
|
65
|
+
.and_raise(ExampleStandardError, "oh no")
|
66
|
+
callback = request.env[Appsignal::Rack::RACK_AFTER_REPLY].first
|
67
|
+
callback.call
|
68
|
+
|
69
|
+
expect(log).to contains_log(
|
70
|
+
:error,
|
71
|
+
"Error occurred in Appsignal::Rack::EventHandler's after_reply: ExampleStandardError: oh no"
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "logs an error in case of an error" do
|
76
|
+
expect(Appsignal::Transaction)
|
77
|
+
.to receive(:create).and_raise(ExampleStandardError, "oh no")
|
78
|
+
|
79
|
+
on_start
|
80
|
+
|
81
|
+
expect(log).to contains_log(
|
82
|
+
:error,
|
83
|
+
"Error occurred in Appsignal::Rack::EventHandler#on_start: ExampleStandardError: oh no"
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#on_error" do
|
89
|
+
def on_error(error)
|
90
|
+
described_class.new.on_error(request, response, error)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "reports the error" do
|
94
|
+
on_start
|
95
|
+
on_error(ExampleStandardError.new("the error"))
|
96
|
+
|
97
|
+
expect(last_transaction.to_h).to include(
|
98
|
+
"error" => {
|
99
|
+
"name" => "ExampleStandardError",
|
100
|
+
"message" => "the error",
|
101
|
+
"backtrace" => kind_of(String)
|
102
|
+
}
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "logs an error in case of an internal error" do
|
107
|
+
on_start
|
108
|
+
|
109
|
+
expect(request.env[Appsignal::Rack::APPSIGNAL_TRANSACTION])
|
110
|
+
.to receive(:set_error).and_raise(ExampleStandardError, "oh no")
|
111
|
+
|
112
|
+
on_error(ExampleStandardError.new("the error"))
|
113
|
+
|
114
|
+
expect(log).to contains_log(
|
115
|
+
:error,
|
116
|
+
"Error occurred in Appsignal::Rack::EventHandler#on_error: ExampleStandardError: oh no"
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#on_finish" do
|
122
|
+
let(:response) { Rack::Events::BufferedResponse.new(200, {}, ["body"]) }
|
123
|
+
|
124
|
+
def on_finish
|
125
|
+
described_class.new.on_finish(request, response)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "doesn't do anything without a transaction" do
|
129
|
+
on_start
|
130
|
+
|
131
|
+
request.env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = nil
|
132
|
+
|
133
|
+
on_finish
|
134
|
+
|
135
|
+
expect(last_transaction.to_h).to include(
|
136
|
+
"action" => nil,
|
137
|
+
"sample_data" => {},
|
138
|
+
"events" => []
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
it "completes the transaction" do
|
143
|
+
on_start
|
144
|
+
on_finish
|
145
|
+
|
146
|
+
expect(last_transaction.to_h).to include(
|
147
|
+
# The action is not set on purpose, as we can't set a normalized route
|
148
|
+
# It requires the app to set an action name
|
149
|
+
"action" => nil,
|
150
|
+
"sample_data" => hash_including(
|
151
|
+
"environment" => {
|
152
|
+
"REQUEST_METHOD" => "GET",
|
153
|
+
"PATH_INFO" => "/path"
|
154
|
+
}
|
155
|
+
)
|
156
|
+
)
|
157
|
+
expect(last_transaction.ext.queue_start).to eq(queue_start_time)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "doesn't set the action name if already set" do
|
161
|
+
on_start
|
162
|
+
last_transaction.set_action("My action")
|
163
|
+
on_finish
|
164
|
+
|
165
|
+
expect(last_transaction.to_h).to include(
|
166
|
+
"action" => "My action"
|
167
|
+
)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "finishes the process_request.rack event" do
|
171
|
+
on_start
|
172
|
+
on_finish
|
173
|
+
|
174
|
+
expect(last_transaction.to_h).to include(
|
175
|
+
"events" => [
|
176
|
+
hash_including(
|
177
|
+
"name" => "process_request.rack",
|
178
|
+
"title" => "",
|
179
|
+
"body" => "",
|
180
|
+
"body_format" => Appsignal::EventFormatter::DEFAULT
|
181
|
+
)
|
182
|
+
]
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "sets the response status as a tag" do
|
187
|
+
on_start
|
188
|
+
on_finish
|
189
|
+
|
190
|
+
expect(last_transaction.to_h).to include(
|
191
|
+
"sample_data" => hash_including(
|
192
|
+
"tags" => { "response_status" => 200 }
|
193
|
+
)
|
194
|
+
)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "increments the response status counter for response status" do
|
198
|
+
expect(Appsignal).to receive(:increment_counter)
|
199
|
+
.with(:response_status, 1, :status => 200, :namespace => :web)
|
200
|
+
|
201
|
+
on_start
|
202
|
+
on_finish
|
203
|
+
end
|
204
|
+
|
205
|
+
it "logs an error in case of an error" do
|
206
|
+
expect(Appsignal::Transaction)
|
207
|
+
.to receive(:complete_current!).and_raise(ExampleStandardError, "oh no")
|
208
|
+
|
209
|
+
on_start
|
210
|
+
on_finish
|
211
|
+
|
212
|
+
expect(log).to contains_log(
|
213
|
+
:error,
|
214
|
+
"Error occurred in Appsignal::Rack::EventHandler#on_finish: ExampleStandardError: oh no"
|
215
|
+
)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -1,14 +1,21 @@
|
|
1
1
|
if DependencyHelper.rails_present?
|
2
|
-
class MockController
|
3
|
-
end
|
4
|
-
|
5
2
|
describe Appsignal::Rack::RailsInstrumentation do
|
3
|
+
class MockController; end
|
4
|
+
|
6
5
|
let(:log) { StringIO.new }
|
7
6
|
before do
|
8
7
|
start_agent
|
9
8
|
Appsignal.internal_logger = test_logger(log)
|
10
9
|
end
|
11
10
|
|
11
|
+
let(:transaction) do
|
12
|
+
Appsignal::Transaction.new(
|
13
|
+
"transaction_id",
|
14
|
+
Appsignal::Transaction::HTTP_REQUEST,
|
15
|
+
Rack::Request.new(env)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
let(:app) { double(:call => true) }
|
12
19
|
let(:params) do
|
13
20
|
{
|
14
21
|
"controller" => "blog_posts",
|
@@ -19,12 +26,11 @@ if DependencyHelper.rails_present?
|
|
19
26
|
}
|
20
27
|
end
|
21
28
|
let(:env_extra) { {} }
|
22
|
-
let(:app) { double(:call => true) }
|
23
29
|
let(:env) do
|
24
30
|
http_request_env_with_data({
|
25
31
|
:params => params,
|
26
32
|
:with_queue_start => true,
|
27
|
-
"action_dispatch.request_id" => "
|
33
|
+
"action_dispatch.request_id" => "request_id123",
|
28
34
|
"action_dispatch.parameter_filter" => [:my_custom_param, :password],
|
29
35
|
"action_controller.instance" => double(
|
30
36
|
:class => MockController,
|
@@ -34,6 +40,9 @@ if DependencyHelper.rails_present?
|
|
34
40
|
end
|
35
41
|
let(:middleware) { Appsignal::Rack::RailsInstrumentation.new(app, {}) }
|
36
42
|
around { |example| keep_transactions { example.run } }
|
43
|
+
before do
|
44
|
+
env[Appsignal::Rack::APPSIGNAL_TRANSACTION] = transaction
|
45
|
+
end
|
37
46
|
|
38
47
|
describe "#call" do
|
39
48
|
before do
|
@@ -43,7 +52,7 @@ if DependencyHelper.rails_present?
|
|
43
52
|
context "when appsignal is active" do
|
44
53
|
before { allow(Appsignal).to receive(:active?).and_return(true) }
|
45
54
|
|
46
|
-
it "
|
55
|
+
it "calls with monitoring" do
|
47
56
|
expect(middleware).to receive(:call_with_appsignal_monitoring).with(env)
|
48
57
|
end
|
49
58
|
end
|
@@ -51,11 +60,11 @@ if DependencyHelper.rails_present?
|
|
51
60
|
context "when appsignal is not active" do
|
52
61
|
before { allow(Appsignal).to receive(:active?).and_return(false) }
|
53
62
|
|
54
|
-
it "
|
63
|
+
it "does not call with monitoring" do
|
55
64
|
expect(middleware).to_not receive(:call_with_appsignal_monitoring)
|
56
65
|
end
|
57
66
|
|
58
|
-
it "
|
67
|
+
it "calls the app" do
|
59
68
|
expect(app).to receive(:call).with(env)
|
60
69
|
end
|
61
70
|
end
|
@@ -66,36 +75,34 @@ if DependencyHelper.rails_present?
|
|
66
75
|
describe "#call_with_appsignal_monitoring" do
|
67
76
|
def run
|
68
77
|
middleware.call(env)
|
78
|
+
last_transaction.complete # Manually close transaction to set sample data
|
69
79
|
end
|
70
80
|
|
71
81
|
it "calls the wrapped app" do
|
72
|
-
run
|
82
|
+
expect { run }.to_not(change { created_transactions.length })
|
73
83
|
expect(app).to have_received(:call).with(env)
|
74
84
|
end
|
75
85
|
|
76
|
-
it "
|
86
|
+
it "sets request metadata on the transaction" do
|
77
87
|
run
|
78
88
|
|
79
|
-
expect(
|
80
|
-
transaction_hash = last_transaction.to_h
|
81
|
-
expect(transaction_hash).to include(
|
89
|
+
expect(last_transaction.to_h).to include(
|
82
90
|
"namespace" => Appsignal::Transaction::HTTP_REQUEST,
|
83
91
|
"action" => "MockController#index",
|
84
92
|
"metadata" => hash_including(
|
85
93
|
"method" => "GET",
|
86
94
|
"path" => "/blog"
|
95
|
+
),
|
96
|
+
"sample_data" => hash_including(
|
97
|
+
"tags" => { "request_id" => "request_id123" }
|
87
98
|
)
|
88
99
|
)
|
89
|
-
expect(last_transaction.ext.queue_start).to eq(
|
90
|
-
fixed_time * 1_000.0
|
91
|
-
)
|
92
100
|
end
|
93
101
|
|
94
|
-
it "filter parameters
|
102
|
+
it "reports Rails filter parameters" do
|
95
103
|
run
|
96
104
|
|
97
|
-
|
98
|
-
expect(transaction_hash).to include(
|
105
|
+
expect(last_transaction.to_h).to include(
|
99
106
|
"sample_data" => hash_including(
|
100
107
|
"params" => params.merge(
|
101
108
|
"my_custom_param" => "[FILTERED]",
|
@@ -105,6 +112,26 @@ if DependencyHelper.rails_present?
|
|
105
112
|
)
|
106
113
|
end
|
107
114
|
|
115
|
+
context "with custom params" do
|
116
|
+
let(:app) do
|
117
|
+
lambda do |env|
|
118
|
+
env[Appsignal::Rack::APPSIGNAL_TRANSACTION].params = { "custom_param" => "yes" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "allows custom params to be set" do
|
123
|
+
run
|
124
|
+
|
125
|
+
expect(last_transaction.to_h).to include(
|
126
|
+
"sample_data" => hash_including(
|
127
|
+
"params" => {
|
128
|
+
"custom_param" => "yes"
|
129
|
+
}
|
130
|
+
)
|
131
|
+
)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
108
135
|
context "with an invalid HTTP request method" do
|
109
136
|
let(:env_extra) { { :request_method => "FOO", "REQUEST_METHOD" => "FOO" } }
|
110
137
|
|
@@ -113,8 +140,8 @@ if DependencyHelper.rails_present?
|
|
113
140
|
|
114
141
|
transaction_hash = last_transaction.to_h
|
115
142
|
expect(transaction_hash["metadata"]).to_not have_key("method")
|
116
|
-
expect(log_contents(log))
|
117
|
-
"Unable to report HTTP request method: '")
|
143
|
+
expect(log_contents(log))
|
144
|
+
.to contains_log(:error, "Unable to report HTTP request method: '")
|
118
145
|
end
|
119
146
|
end
|
120
147
|
|
@@ -137,25 +164,32 @@ if DependencyHelper.rails_present?
|
|
137
164
|
)
|
138
165
|
end
|
139
166
|
end
|
140
|
-
end
|
141
167
|
|
142
|
-
|
143
|
-
|
168
|
+
context "with a request path that's not a route" do
|
169
|
+
let(:env_extra) do
|
170
|
+
{
|
171
|
+
:path => "/unknown-route",
|
172
|
+
"action_controller.instance" => nil
|
173
|
+
}
|
174
|
+
end
|
144
175
|
|
145
|
-
|
146
|
-
|
176
|
+
it "doesn't set an action name" do
|
177
|
+
run
|
147
178
|
|
148
|
-
|
149
|
-
|
179
|
+
expect(last_transaction.to_h).to include(
|
180
|
+
"action" => nil
|
181
|
+
)
|
150
182
|
end
|
151
183
|
end
|
184
|
+
end
|
152
185
|
|
153
|
-
|
154
|
-
|
186
|
+
describe "#fetch_request_id" do
|
187
|
+
subject { middleware.fetch_request_id(env) }
|
155
188
|
|
156
|
-
|
157
|
-
|
158
|
-
|
189
|
+
let(:env) { { "action_dispatch.request_id" => "id" } }
|
190
|
+
|
191
|
+
it "returns the action dispatch id" do
|
192
|
+
is_expected.to eq "id"
|
159
193
|
end
|
160
194
|
end
|
161
195
|
end
|
@@ -719,7 +719,6 @@ describe Appsignal::Transaction do
|
|
719
719
|
|
720
720
|
sample_data = transaction.to_h["sample_data"]
|
721
721
|
expect(sample_data["environment"]).to include(
|
722
|
-
"CONTENT_LENGTH" => "0",
|
723
722
|
"REQUEST_METHOD" => "GET",
|
724
723
|
"SERVER_NAME" => "example.org",
|
725
724
|
"SERVER_PORT" => "80",
|
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.
|
4
|
+
version: 3.8.1
|
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: 2024-
|
13
|
+
date: 2024-06-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rack
|
@@ -135,6 +135,7 @@ extra_rdoc_files: []
|
|
135
135
|
files:
|
136
136
|
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
137
137
|
- ".github/ISSUE_TEMPLATE/chore.md"
|
138
|
+
- ".github/workflows/create_release_from_tag.yml"
|
138
139
|
- ".gitignore"
|
139
140
|
- ".gitmodules"
|
140
141
|
- ".rspec"
|
@@ -208,6 +209,7 @@ files:
|
|
208
209
|
- lib/appsignal/extension/jruby.rb
|
209
210
|
- lib/appsignal/garbage_collection.rb
|
210
211
|
- lib/appsignal/heartbeat.rb
|
212
|
+
- lib/appsignal/helpers/heartbeats.rb
|
211
213
|
- lib/appsignal/helpers/instrumentation.rb
|
212
214
|
- lib/appsignal/helpers/metrics.rb
|
213
215
|
- lib/appsignal/hooks.rb
|
@@ -269,6 +271,7 @@ files:
|
|
269
271
|
- lib/appsignal/probes/helpers.rb
|
270
272
|
- lib/appsignal/probes/mri.rb
|
271
273
|
- lib/appsignal/probes/sidekiq.rb
|
274
|
+
- lib/appsignal/rack/event_handler.rb
|
272
275
|
- lib/appsignal/rack/generic_instrumentation.rb
|
273
276
|
- lib/appsignal/rack/rails_instrumentation.rb
|
274
277
|
- lib/appsignal/rack/sinatra_instrumentation.rb
|
@@ -369,6 +372,7 @@ files:
|
|
369
372
|
- spec/lib/appsignal/probes/mri_spec.rb
|
370
373
|
- spec/lib/appsignal/probes/sidekiq_spec.rb
|
371
374
|
- spec/lib/appsignal/probes_spec.rb
|
375
|
+
- spec/lib/appsignal/rack/event_handler_spec.rb
|
372
376
|
- spec/lib/appsignal/rack/generic_instrumentation_spec.rb
|
373
377
|
- spec/lib/appsignal/rack/rails_instrumentation_spec.rb
|
374
378
|
- spec/lib/appsignal/rack/sinatra_instrumentation_spec.rb
|
@@ -454,7 +458,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
454
458
|
- !ruby/object:Gem::Version
|
455
459
|
version: '0'
|
456
460
|
requirements: []
|
457
|
-
rubygems_version: 3.
|
461
|
+
rubygems_version: 3.5.11
|
458
462
|
signing_key:
|
459
463
|
specification_version: 4
|
460
464
|
summary: Logs performance and exception data from your app to appsignal.com
|