speedshop-cloudwatch 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +3 -1
- data/lib/speedshop/cloudwatch/metrics.rb +1 -1
- data/lib/speedshop/cloudwatch/observations/rack.rb +44 -4
- data/lib/speedshop/cloudwatch/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a4d2d47eedd3f9ad965073f7569d707bee70f2b4bb35f7b7f97f4197d9016b9c
|
|
4
|
+
data.tar.gz: d2bc25a79ba9893fa45a8a8843949ef42a7e70b5ab054ced8031446148bed927
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae73508f29611fba12c522b3e84d82331026638892d2b540ede54a951a9e5314254c59a1620f5d423c0f592a7017f526233b938873107348231faf0a8db1c4b6
|
|
7
|
+
data.tar.gz: eb65e33a190b8d4a33686dd2c63f59ba504b0f585a2cb3d730f09274e89809acfa9ba0034caaef272b4f02fe3a0029c1373f03f684c9d3db2e03e4f1e9b8eab2
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.1] - 2026-06-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Improved Rack request queue time parsing for common `X-Request-Start` and `X-Queue-Start` formats, including `t=` prefixes, seconds, milliseconds, microseconds, and comma-separated header values.
|
|
12
|
+
- Subtracted Puma request body wait time from Rack queue time when `env["puma.request_body_wait"]` is available, so slow uploads are not counted as upstream queueing.
|
|
13
|
+
|
|
8
14
|
## [0.2.0] - 2026-04-13
|
|
9
15
|
|
|
10
16
|
### Added
|
|
@@ -21,5 +27,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
21
27
|
### Added
|
|
22
28
|
- Initial public release.
|
|
23
29
|
|
|
30
|
+
[0.2.1]: https://github.com/speedshop/speedshop-cloudwatch/compare/v0.2.0...v0.2.1
|
|
24
31
|
[0.2.0]: https://github.com/speedshop/speedshop-cloudwatch/compare/v0.1.0...v0.2.0
|
|
25
32
|
[0.1.0]: https://github.com/speedshop/speedshop-cloudwatch/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -144,7 +144,9 @@ If you're using Rails, we'll automatically insert the correct middleware into th
|
|
|
144
144
|
|
|
145
145
|
If you're using some other Rack-based framework, insert the `Speedshop::Cloudwatch::Rack` high up (i.e. first) in the stack.
|
|
146
146
|
|
|
147
|
-
You will need a reverse proxy, such as nginx, adding an `X-Request-Start` or `X-Queue-Start` header
|
|
147
|
+
You will need a reverse proxy, such as nginx, adding an `X-Request-Start` or `X-Queue-Start` header to incoming requests. The header may use common queue-time formats such as epoch milliseconds (`1512379167574`), seconds with decimals (`t=1512379167.574`), or microseconds (`t=1570633834463123`). See [New Relic's instructions](https://docs.newrelic.com/docs/apm/applications-menu/features/configure-request-queue-reporting/) for more about how to do this.
|
|
148
|
+
|
|
149
|
+
When Puma exposes `env["puma.request_body_wait"]`, we subtract it from queue time so slow request-body uploads are not counted as upstream queueing.
|
|
148
150
|
|
|
149
151
|
We report the following metrics:
|
|
150
152
|
|
|
@@ -162,7 +162,7 @@ module Speedshop
|
|
|
162
162
|
description: "Time a request spent waiting in the reverse proxy before " \
|
|
163
163
|
"reaching the application. High values indicate requests " \
|
|
164
164
|
"backing up before reaching your application server.",
|
|
165
|
-
source: "
|
|
165
|
+
source: "parsed X-Request-Start/X-Queue-Start timestamp minus Puma request body wait"
|
|
166
166
|
)
|
|
167
167
|
],
|
|
168
168
|
|
|
@@ -4,17 +4,57 @@ module Speedshop
|
|
|
4
4
|
module Cloudwatch
|
|
5
5
|
module Observations
|
|
6
6
|
module Rack
|
|
7
|
+
class HeaderTimestampParser
|
|
8
|
+
MIN_EPOCH = Time.utc(2000, 1, 1).to_f
|
|
9
|
+
FUTURE_TOLERANCE = 30.0
|
|
10
|
+
DIVISORS = [1_000_000.0, 1_000.0, 1.0].freeze
|
|
11
|
+
NUMBER_RE = /[+-]?(?:\d+(?:\.\d+)?|\.\d+)/
|
|
12
|
+
T_EQUALS_RE = /t\s*=\s*(#{NUMBER_RE.source})/i
|
|
13
|
+
|
|
14
|
+
def parse(value, now:)
|
|
15
|
+
header_value = value.to_s.split(",", 2).first.to_s.strip
|
|
16
|
+
return if header_value.empty?
|
|
17
|
+
|
|
18
|
+
token = header_value[T_EQUALS_RE, 1] || header_value[NUMBER_RE, 0]
|
|
19
|
+
normalize(Float(token), now) if token
|
|
20
|
+
rescue ArgumentError, TypeError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def normalize(raw, now)
|
|
26
|
+
max = now + FUTURE_TOLERANCE
|
|
27
|
+
divisor = DIVISORS.find { |d| (raw / d).between?(MIN_EPOCH, max) }
|
|
28
|
+
raw / divisor if divisor
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
7
32
|
module_function
|
|
8
33
|
|
|
9
34
|
def request_queue_time(env, now_ms: current_time_ms)
|
|
10
|
-
|
|
11
|
-
|
|
35
|
+
now = now_ms / 1_000.0
|
|
36
|
+
request_start = header_timestamp_parser.parse(env["HTTP_X_REQUEST_START"], now: now) ||
|
|
37
|
+
header_timestamp_parser.parse(env["HTTP_X_QUEUE_START"], now: now)
|
|
38
|
+
return unless request_start
|
|
12
39
|
|
|
13
|
-
|
|
40
|
+
queue_time_ms = (now - request_start) * 1_000.0
|
|
41
|
+
return if queue_time_ms.negative?
|
|
42
|
+
|
|
43
|
+
[queue_time_ms - (request_body_wait_ms(env) || 0), 0.0].max
|
|
14
44
|
end
|
|
15
45
|
|
|
16
46
|
def current_time_ms
|
|
17
|
-
Time.now.to_f *
|
|
47
|
+
Time.now.to_f * 1_000.0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def header_timestamp_parser
|
|
51
|
+
@header_timestamp_parser ||= HeaderTimestampParser.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def request_body_wait_ms(env)
|
|
55
|
+
wait_ms = Float(env["puma.request_body_wait"])
|
|
56
|
+
wait_ms if wait_ms.finite? && !wait_ms.negative?
|
|
57
|
+
rescue ArgumentError, TypeError
|
|
18
58
|
end
|
|
19
59
|
end
|
|
20
60
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: speedshop-cloudwatch
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nate Berkopec
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: aws-sdk-cloudwatch
|
|
@@ -94,7 +93,6 @@ metadata:
|
|
|
94
93
|
allowed_push_host: https://rubygems.org
|
|
95
94
|
homepage_uri: https://github.com/nateberkopec/speedshop-cloudwatch
|
|
96
95
|
source_code_uri: https://github.com/nateberkopec/speedshop-cloudwatch
|
|
97
|
-
post_install_message:
|
|
98
96
|
rdoc_options: []
|
|
99
97
|
require_paths:
|
|
100
98
|
- lib
|
|
@@ -109,8 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
107
|
- !ruby/object:Gem::Version
|
|
110
108
|
version: '0'
|
|
111
109
|
requirements: []
|
|
112
|
-
rubygems_version: 3.
|
|
113
|
-
signing_key:
|
|
110
|
+
rubygems_version: 3.6.9
|
|
114
111
|
specification_version: 4
|
|
115
112
|
summary: Ruby application integration with AWS CloudWatch for Puma, Rack, Sidekiq,
|
|
116
113
|
and ActiveJob
|