Class: Binocs::Request
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Binocs::Request
- Defined in:
- app/models/binocs/request.rb
Class Method Summary collapse
- .average_duration ⇒ Object
-
.client_identifiers ⇒ Object
Class methods for statistics.
- .controllers_list ⇒ Object
- .error_rate ⇒ Object
- .methods_breakdown ⇒ Object
- .status_breakdown ⇒ Object
Instance Method Summary collapse
- #client_error? ⇒ Boolean
- #client_label ⇒ Object
- #controller_action ⇒ Object
- #default_tag ⇒ Object
- #find_swagger_path_and_tag ⇒ Object
- #formatted_duration ⇒ Object
- #formatted_memory_delta ⇒ Object
- #full_url ⇒ Object
- #has_exception? ⇒ Boolean
-
#http_method ⇒ Object
Alias for ‘method’ to avoid conflict with Object#method.
- #method_class ⇒ Object
- #redirect? ⇒ Boolean
- #server_error? ⇒ Boolean
- #short_path ⇒ Object
- #status_class ⇒ Object
- #success? ⇒ Boolean
- #swagger_url ⇒ Object
Class Method Details
.average_duration ⇒ Object
207 208 209 |
# File 'app/models/binocs/request.rb', line 207 def self.average_duration average(:duration_ms)&.round(2) end |
.client_identifiers ⇒ Object
Class methods for statistics
200 201 202 203 204 205 |
# File 'app/models/binocs/request.rb', line 200 def self.client_identifiers where.not(client_identifier: nil) .group(:client_identifier) .order(Arel.sql("MAX(created_at) DESC")) .pluck(:client_identifier) end |
.controllers_list ⇒ Object
225 226 227 |
# File 'app/models/binocs/request.rb', line 225 def self.controllers_list distinct.pluck(:controller_name).compact.sort end |
.error_rate ⇒ Object
211 212 213 214 215 |
# File 'app/models/binocs/request.rb', line 211 def self.error_rate return 0 if count.zero? ((with_exception.count + by_status_range("5xx").count).to_f / count * 100).round(2) end |
.methods_breakdown ⇒ Object
217 218 219 |
# File 'app/models/binocs/request.rb', line 217 def self.methods_breakdown group(:method).count end |
.status_breakdown ⇒ Object
221 222 223 |
# File 'app/models/binocs/request.rb', line 221 def self.status_breakdown group(:status_code).count end |
Instance Method Details
#client_error? ⇒ Boolean
71 72 73 |
# File 'app/models/binocs/request.rb', line 71 def client_error? status_code.present? && status_code >= 400 && status_code < 500 end |
#client_label ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 |
# File 'app/models/binocs/request.rb', line 187 def client_label return "Unknown" if client_identifier.blank? prefix, value = client_identifier.split(":", 2) case prefix when "session" then "Session #{value.to_s[0, 8]}" when "auth" then "Auth #{value.to_s[0, 8]}" when "ip" then "IP #{value}" else client_identifier end end |
#controller_action ⇒ Object
138 139 140 141 142 |
# File 'app/models/binocs/request.rb', line 138 def controller_action return nil unless controller_name && action_name "#{controller_name}##{action_name}" end |
#default_tag ⇒ Object
179 180 181 182 183 184 185 |
# File 'app/models/binocs/request.rb', line 179 def default_tag # Fallback: derive tag from controller name # V1::CompaniesController -> "Companies" return "default" unless controller_name controller_name.demodulize.sub(/Controller$/, "").titleize end |
#find_swagger_path_and_tag ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'app/models/binocs/request.rb', line 155 def find_swagger_path_and_tag spec_path = Rails.root.join("swagger/v1/swagger.yaml") return [path, default_tag] unless File.exist?(spec_path) spec = YAML.load_file(spec_path) # Find matching swagger path by converting UUIDs to param placeholders spec["paths"]&.each do |swagger_path, methods| # Create regex from swagger path: /v1/channels/{channel_uuid} -> /v1/channels/[^/]+ pattern = swagger_path.gsub(/\{[^}]+\}/, "[^/]+") regex = /\A#{pattern}\z/ if path.match?(regex) && methods[method.downcase] tag = methods.dig(method.downcase, "tags")&.first || default_tag return [swagger_path, tag] end end [path, default_tag] rescue => e Rails.logger.debug "[Binocs] Failed to find swagger path: #{e.}" [path, default_tag] end |
#formatted_duration ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 |
# File 'app/models/binocs/request.rb', line 101 def formatted_duration return "N/A" unless duration_ms if duration_ms < 1 "< 1ms" elsif duration_ms < 1000 "#{duration_ms.round(1)}ms" else "#{(duration_ms / 1000).round(2)}s" end end |
#formatted_memory_delta ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 |
# File 'app/models/binocs/request.rb', line 113 def formatted_memory_delta return "N/A" unless memory_delta if memory_delta.abs < 1024 "#{memory_delta} B" elsif memory_delta.abs < 1024 * 1024 "#{(memory_delta / 1024.0).round(2)} KB" else "#{(memory_delta / (1024.0 * 1024)).round(2)} MB" end end |
#full_url ⇒ Object
131 132 133 134 135 136 |
# File 'app/models/binocs/request.rb', line 131 def full_url # Construct URL from host header if available host = request_headers&.dig('Host') || request_headers&.dig('host') || 'localhost' scheme = request_headers&.dig('X-Forwarded-Proto') || 'http' "#{scheme}://#{host}#{path}" end |
#has_exception? ⇒ Boolean
79 80 81 |
# File 'app/models/binocs/request.rb', line 79 def has_exception? exception.present? end |
#http_method ⇒ Object
Alias for ‘method’ to avoid conflict with Object#method
59 60 61 |
# File 'app/models/binocs/request.rb', line 59 def http_method read_attribute(:method) end |
#method_class ⇒ Object
91 92 93 94 95 96 97 98 99 |
# File 'app/models/binocs/request.rb', line 91 def method_class case method.upcase when "GET" then "method-get" when "POST" then "method-post" when "PUT", "PATCH" then "method-put" when "DELETE" then "method-delete" else "method-other" end end |
#redirect? ⇒ Boolean
67 68 69 |
# File 'app/models/binocs/request.rb', line 67 def redirect? status_code.present? && status_code >= 300 && status_code < 400 end |
#server_error? ⇒ Boolean
75 76 77 |
# File 'app/models/binocs/request.rb', line 75 def server_error? status_code.present? && status_code >= 500 end |
#short_path ⇒ Object
125 126 127 128 129 |
# File 'app/models/binocs/request.rb', line 125 def short_path return path if path.length <= 50 "#{path[0, 47]}..." end |
#status_class ⇒ Object
83 84 85 86 87 88 89 |
# File 'app/models/binocs/request.rb', line 83 def status_class return "error" if server_error? || has_exception? return "warning" if client_error? return "redirect" if redirect? "success" end |
#success? ⇒ Boolean
63 64 65 |
# File 'app/models/binocs/request.rb', line 63 def success? status_code.present? && status_code >= 200 && status_code < 300 end |
#swagger_url ⇒ Object
144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/binocs/request.rb', line 144 def swagger_url # Convert path to Swagger UI deep link format: #/{tagName}/{operationId} swagger_path, tag = find_swagger_path_and_tag # Generate operationId: method + path with non-alphanumeric chars replaced # /v1/companies/{company_uuid}/invitations -> v1_companies__company_uuid__invitations operation_id = "#{method.downcase}#{swagger_path.gsub(/[^a-zA-Z0-9]/, '_')}" "/api-docs/index.html#/#{ERB::Util.url_encode(tag)}/#{operation_id}" end |