puma_metrics_engine 1.1.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 248a19d8e7fb9d4392b1f4c4bf3cfa13d8e4872426d843f171732264783eaf94
4
- data.tar.gz: b09efaf7ede10a759b435b8be828f0dbebde7f54ece70efddb050180e474ac4a
3
+ metadata.gz: 32e8e883e66e79ca9d1ed80ee21f5b0b880241f0157e325b21c7b679d9ffa56f
4
+ data.tar.gz: de06e4bed7924f401382ad5e852936cc16bbb699e7c98569957d8a7dae203cf9
5
5
  SHA512:
6
- metadata.gz: 4bbfb1639f3157a75afc8bbc538eecfd52492c05240be8d96576884ea2448cd6d8598b16e0a87c8fd2a93153b0b051e186842083e18bb874b8c5bb14bb17813c
7
- data.tar.gz: 006066e431c7c1d7fed9cfb6c19b17715e2e7437a09ef23b8962838e33e063299a62ce7b47d6c6d535ebe535032ed8abd840f3851cf7e0505fab11bc023a041e
6
+ metadata.gz: d51049bfb157f4641cf41499ddd3c1769f7ffa8e13b0dba731124e748cfa2b80cfefb445ca7ecdc1fe06ba00aa31a137f7bc7978d84f962cb4ac413515a5bb7f
7
+ data.tar.gz: d2d192c9ceda0d7eed4de38ec1a2cce70f36d468cf3dc9de345cb7b3929fd4cd93df1b8fbda1d7deeb11546af6dea4cf0910291238ccb98393d9227572065599
data/README.md CHANGED
@@ -25,6 +25,12 @@ end
25
25
 
26
26
  Access metrics at `/matrix` endpoint.
27
27
 
28
+ For debugging production issues, use `/debug` endpoint to check:
29
+ - Middleware registration status
30
+ - Redis connectivity
31
+ - Header presence
32
+ - Current data in Redis
33
+
28
34
  ## Response Format
29
35
 
30
36
  ```json
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PumaMetricsEngine
4
+ class DebugController < ActionController::Base
5
+ skip_before_action :verify_authenticity_token
6
+
7
+ def show
8
+ render json: {
9
+ timestamp: Time.current,
10
+ middleware: middleware_status,
11
+ redis: redis_status,
12
+ headers: sample_headers,
13
+ data: redis_data_summary,
14
+ environment: environment_info
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ def middleware_status
21
+ middleware_stack = Rails.application.middleware.to_a.map(&:klass).map(&:name)
22
+ is_registered = middleware_stack.include?("PumaMetricsEngine::QueueTimeTracker")
23
+
24
+ {
25
+ registered: is_registered,
26
+ middleware_stack: middleware_stack,
27
+ note: is_registered ? "Middleware is registered" : "Middleware NOT found in stack"
28
+ }
29
+ rescue StandardError => e
30
+ { error: e.message }
31
+ end
32
+
33
+ def redis_status
34
+ client = redis
35
+ client.ping
36
+ {
37
+ connected: true,
38
+ url: ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" },
39
+ keys: {
40
+ queue_times: client.exists?(MatrixController::QUEUE_TIMES_KEY),
41
+ request_timestamps: client.exists?(MatrixController::REQUESTS_KEY)
42
+ }
43
+ }
44
+ rescue StandardError => e
45
+ {
46
+ connected: false,
47
+ error: e.message,
48
+ url: ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" }
49
+ }
50
+ end
51
+
52
+ def redis_data_summary
53
+ client = redis
54
+ {
55
+ queue_times_count: client.zcard(MatrixController::QUEUE_TIMES_KEY),
56
+ request_timestamps_count: client.zcard(MatrixController::REQUESTS_KEY),
57
+ recent_queue_times: client.zrange(MatrixController::QUEUE_TIMES_KEY, -10, -1),
58
+ recent_timestamps: client.zrange(MatrixController::REQUESTS_KEY, -10, -1)
59
+ }
60
+ rescue StandardError => e
61
+ { error: e.message }
62
+ end
63
+
64
+ def sample_headers
65
+ # Show what headers we're looking for
66
+ {
67
+ x_request_start: request.headers["X-Request-Start"],
68
+ http_x_request_start: request.headers["HTTP_X_REQUEST_START"],
69
+ all_x_headers: request.headers.select { |k, _| k.to_s.upcase.include?("X-REQUEST") },
70
+ note: "Check if X-Request-Start header is being sent by your load balancer"
71
+ }
72
+ end
73
+
74
+ def environment_info
75
+ {
76
+ rails_env: Rails.env,
77
+ redis_url_set: ENV.key?("REDIS_URL"),
78
+ puma_defined: defined?(Puma)
79
+ }
80
+ end
81
+
82
+ def redis
83
+ @redis ||= Redis.new(url: ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" })
84
+ end
85
+ end
86
+ end
87
+
data/config/routes.rb CHANGED
@@ -2,4 +2,5 @@
2
2
 
3
3
  PumaMetricsEngine::Engine.routes.draw do
4
4
  get "matrix", to: "matrix#show"
5
+ get "debug", to: "debug#show"
5
6
  end
@@ -14,6 +14,13 @@ module PumaMetricsEngine
14
14
  request_start_time = extract_request_start_time(env)
15
15
  process_start_time = Time.now.to_f
16
16
 
17
+ # Log header presence for debugging
18
+ if defined?(Rails)
19
+ header_value = env["HTTP_X_REQUEST_START"] || env["X-Request-Start"]
20
+ Rails.logger.debug("[QueueTimeTracker] X-Request-Start header: #{header_value.inspect}") if header_value
21
+ Rails.logger.debug("[QueueTimeTracker] No X-Request-Start header found") unless header_value
22
+ end
23
+
17
24
  status, headers, response = @app.call(env)
18
25
 
19
26
  # Calculate queue time if we have request start time
@@ -27,8 +34,10 @@ module PumaMetricsEngine
27
34
  timestamp = process_start_time
28
35
  # Store in Redis asynchronously to avoid blocking the request
29
36
  store_metrics_async(timestamp, queue_time_ms)
37
+ Rails.logger.debug("[QueueTimeTracker] Stored queue time: #{queue_time_ms}ms") if defined?(Rails)
30
38
  else
31
39
  # Still track request timestamp even if queue time is invalid
40
+ Rails.logger.warn("[QueueTimeTracker] Invalid queue time: #{queue_time_ms}ms (rejected)") if defined?(Rails)
32
41
  store_request_timestamp_async(process_start_time)
33
42
  end
34
43
  else
@@ -37,7 +46,8 @@ module PumaMetricsEngine
37
46
  end
38
47
  rescue StandardError => e
39
48
  # Don't let tracking errors break the request
40
- Rails.logger.error("QueueTimeTracker error: #{e.message}") if defined?(Rails)
49
+ Rails.logger.error("[QueueTimeTracker] Error: #{e.message}") if defined?(Rails)
50
+ Rails.logger.error("[QueueTimeTracker] Backtrace: #{e.backtrace.first(5).join("\n")}") if defined?(Rails)
41
51
  end
42
52
 
43
53
  [status, headers, response]
@@ -47,7 +57,7 @@ module PumaMetricsEngine
47
57
 
48
58
  def extract_request_start_time(env)
49
59
  # Try X-Request-Start header (common in nginx, HAProxy, etc.)
50
- # Format: "t=1234567890.123" or "1234567890.123" or Unix timestamp in microseconds
60
+ # Format: "t=1234567890.123" or "1234567890.123" or Unix timestamp in milliseconds/microseconds
51
61
  header_value = env["HTTP_X_REQUEST_START"] || env["X-Request-Start"]
52
62
 
53
63
  return nil unless header_value
@@ -65,10 +75,25 @@ module PumaMetricsEngine
65
75
 
66
76
  timestamp = timestamp_str.to_f
67
77
 
68
- # If timestamp is in microseconds (common with nginx), convert to seconds
69
- # Timestamps > year 2100 are likely in microseconds
78
+ # Detect timestamp format based on magnitude:
79
+ # - 10 digits or less: seconds (e.g., 1764410986)
80
+ # - 11-13 digits: milliseconds (e.g., 1764410986245)
81
+ # - 14+ digits: microseconds (e.g., 1764410986245000)
70
82
  if timestamp > 4_102_444_800 # Year 2100 in seconds
71
- timestamp = timestamp / 1_000_000.0
83
+ # Count digits in integer part to determine format
84
+ integer_part = timestamp.to_i.to_s
85
+ digit_count = integer_part.length
86
+
87
+ if digit_count <= 10
88
+ # Already in seconds
89
+ timestamp
90
+ elsif digit_count <= 13
91
+ # Milliseconds - convert to seconds
92
+ timestamp = timestamp / 1_000.0
93
+ else
94
+ # Microseconds - convert to seconds
95
+ timestamp = timestamp / 1_000_000.0
96
+ end
72
97
  end
73
98
 
74
99
  timestamp
@@ -89,7 +114,8 @@ module PumaMetricsEngine
89
114
  # Cleanup old data (older than TTL)
90
115
  cleanup_old_data(redis_client)
91
116
  rescue StandardError => e
92
- Rails.logger.error("Failed to store queue time metrics: #{e.message}") if defined?(Rails)
117
+ Rails.logger.error("[QueueTimeTracker] Failed to store metrics: #{e.message}") if defined?(Rails)
118
+ Rails.logger.error("[QueueTimeTracker] Redis error backtrace: #{e.backtrace.first(3).join("\n")}") if defined?(Rails)
93
119
  end
94
120
  end
95
121
  end
@@ -101,7 +127,7 @@ module PumaMetricsEngine
101
127
  redis_client.zadd(REQUESTS_KEY, timestamp, timestamp)
102
128
  cleanup_old_data(redis_client)
103
129
  rescue StandardError => e
104
- Rails.logger.error("Failed to store request timestamp: #{e.message}") if defined?(Rails)
130
+ Rails.logger.error("[QueueTimeTracker] Failed to store request timestamp: #{e.message}") if defined?(Rails)
105
131
  end
106
132
  end
107
133
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PumaMetricsEngine
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma_metrics_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neeto
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-11-28 00:00:00.000000000 Z
11
+ date: 2025-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -146,6 +146,7 @@ extra_rdoc_files: []
146
146
  files:
147
147
  - LICENSE.txt
148
148
  - README.md
149
+ - app/controllers/puma_metrics_engine/debug_controller.rb
149
150
  - app/controllers/puma_metrics_engine/matrix_controller.rb
150
151
  - config/routes.rb
151
152
  - lib/puma_metrics_engine.rb