### Install Dependencies and Start Development Server Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Run these commands to install gem dependencies for the website and start a local Sinatra server for previewing changes. This is part of the development setup for contributing to the project. ```bash $ BUNDLE_GEMFILE=website/Gemfile bundle install $ bundle exec rake client_dev ``` -------------------------------- ### Rails Installation Generator Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Run this command to set up rack-mini-profiler in a Rails development environment. ```bash bundle exec rails g rack_mini_profiler:install ``` -------------------------------- ### Rack Builder Integration Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Integrate Rack Mini Profiler into a Rack application using Rack::Builder. This example maps the root path to a simple hello world response. ```ruby require 'rack-mini-profiler' home = lambda { |env| [200, {'Content-Type' => 'text/html'}, ["hello!"]] } builder = Rack::Builder.new do use Rack::MiniProfiler map('/') { run home } end run builder ``` -------------------------------- ### Install rack-mini-profiler Gem Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Add the rack-mini-profiler gem to your Gemfile for Ruby 3.2+ to enable basic profiling features. ```ruby gem 'rack-mini-profiler' ``` -------------------------------- ### Configure RedisStore for Production Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Configures RedisStore for production environments, using a URL from an environment variable. Requires the 'redis' gem. This store is suitable for multi-process and multi-machine setups and has a 24-hour data retention policy. ```ruby # set RedisStore if Rails.env.production? Rack::MiniProfiler.config.storage_options = { url: ENV["REDIS_SERVER_URL"] } Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore end ``` -------------------------------- ### Convert Monotonic Start Time to Milliseconds Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Calculates the elapsed time in milliseconds from a monotonic start time. Useful for precise performance measurements. ```ruby def mp_elapsed_ms(start_mono) ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_mono) * 1000.0).round(1) end ``` -------------------------------- ### Generate Async Flamegraphs Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use '?pp=async-flamegraph' to get the regular response while storing flamegraph data asynchronously. The flamegraph link will appear in the MiniProfiler UI or in the X-MiniProfiler-Flamegraph-Path header. ```bash ?pp=async-flamegraph ``` -------------------------------- ### Patching ORM Gems Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Declare ORM gems before rack-mini-profiler in the Gemfile to enable automatic query statistics collection. This example shows common ORM gems. ```ruby gem 'pg' gem 'mongoid' gem 'rack-mini-profiler' ``` -------------------------------- ### Set Custom User Provider Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Overrides the default IP-based user provider with a custom Proc. This is crucial for multi-machine production setups to ensure unique user identification. ```ruby Rack::MiniProfiler.config.user_provider = Proc.new{ |env| CurrentUser.get(env) } ``` -------------------------------- ### Resolve Net::HTTP Stack Level Too Deep Errors Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If you encounter stack level too deep errors with Net::HTTP after installing Mini Profiler, use this gem line to prepend the patch instead of aliasing. ```ruby gem 'rack-mini-profiler', require: ['prepend_net_http_patch', 'rack-mini-profiler'] ``` -------------------------------- ### Inject MiniProfiler Speed Badge into SPAs Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Include this script tag in your Single Page Application to load the MiniProfiler speed badge. The version GUID and asset URLs may change with new releases. ```html ``` -------------------------------- ### Set Configuration Options Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Configure Rack Mini Profiler settings like display position and initial visibility. These options can be set using the configuration accessor. ```ruby Rack::MiniProfiler.config.position = 'bottom-right' Rack::MiniProfiler.config.start_hidden = true ``` -------------------------------- ### Build and Run Specs Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Execute these rake tasks to build the project and run the associated specifications. Ensure Memcached and Redis services are running before executing these commands. ```bash $ bundle exec rake build $ bundle exec rake spec ``` -------------------------------- ### Hanami Integration and Partial Profiling Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Integrate Rack Mini Profiler with Hanami applications and profile Hanami::View::Rendering::Partial#render method. This requires adding the profiler to config.ru. ```ruby # config.ru require 'rack-mini-profiler' Rack::MiniProfiler.profile_method(Hanami::View::Rendering::Partial, :render) { "Render partial #{@options[:partial]}" } use Rack::MiniProfiler ``` -------------------------------- ### Generate Flamegraphs Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Append '?pp=flamegraph' to the URL or set the 'X-Rack-Mini-Profiler' header to 'flamegraph' to generate flamegraphs. Note that SQL timings are not recorded when requesting a flamegraph. ```bash ?pp=flamegraph ``` ```bash X-Rack-Mini-Profiler: flamegraph ``` -------------------------------- ### Profile Instance Method Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Measures the performance of a specific instance method. The block provided should return a string describing the execution. ```ruby Rails.application.config.to_prepare do ::Rack::MiniProfiler.profile_method(User, :favorite_post) { |a| "executing favorite_post" } end ``` -------------------------------- ### Instrument OpenSearch Client Calls Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Use this initializer to instrument OpenSearch::Client calls so they appear in Rack::MiniProfiler's SQL Summary. This is useful for non-ActiveRecord applications. ```ruby # config/initializers/opensearch_profiler.rb # Instrument OpenSearch::Client calls so they appear in Rack::MiniProfiler's SQL Summary. ``` -------------------------------- ### Rails Gem Initialization Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Add this to your Gemfile to ensure rack-mini-profiler is initialized after other gems. Use `require: false` to prevent immediate loading. ```ruby gem 'rack-mini-profiler', require: false ``` -------------------------------- ### Generate Memory Profile Report Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Add '?pp=profile-memory' to the URL to generate a memory allocation report. This report breaks down allocations by gem, file, and class, and highlights String allocations. ```bash ?pp=profile-memory ``` -------------------------------- ### Require Rack Mini Profiler and OpenSearch Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Ensures that Rack::MiniProfiler and OpenSearch are available before proceeding. If either is not present, the code will gracefully continue without profiling. ```ruby begin require 'rack-mini-profiler' rescue LoadError # Rack::MiniProfiler not present; no-op. end begin require 'opensearch' rescue LoadError # OpenSearch not present; no-op. end require 'json' ``` -------------------------------- ### Profile Singleton Method Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Measures the performance of a specific singleton method. The block provided should return a string describing the execution. ```ruby Rails.application.config.to_prepare do ::Rack::MiniProfiler.profile_singleton_method(User, :non_admins) { |a| "executing all_non_admins" } end ``` -------------------------------- ### Prepend OpenSearch Client Instrumentation Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Prepends the RackMiniProfilerOpenSearch::Instrumentation module to OpenSearch::Client to capture all requests made through this client type. ```ruby unless ::OpenSearch::Client.ancestors.include?(RackMiniProfilerOpenSearch::Instrumentation) ::OpenSearch::Client.prepend(RackMiniProfilerOpenSearch::Instrumentation) end ``` -------------------------------- ### Garbage Collection Statistics Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use the '?pp=profile-gc' query parameter to report on Garbage Collection statistics without requiring the memory_profiler gem. ```bash ?pp=profile-gc ``` -------------------------------- ### Configure Selective Net::HTTP Profiling Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-more-insightful-Net:HTTP-Request-Profiling This initializer enhances Net::HTTP profiling by skipping DynamoDB and OpenSearch requests to avoid duplicate profiling. It requires Rack::MiniProfiler to be present. Ensure that the `after_initialize` callback is used to correctly hook into Rails' initialization process. ```ruby # config/initializers/net_http_profiler.rb # Enhanced Net::HTTP profiling that skips DynamoDB and OpenSearch requests # to avoid duplicate profiling, but shows all other HTTP requests with details begin require 'rack-mini-profiler' rescue LoadError # Rack::MiniProfiler not present; no-op. end require 'net/http' if defined?(::Rack::MiniProfiler) && defined?(::Net::HTTP) Rails.application.config.after_initialize do # First unprofile the default Net::HTTP profiling to replace it with our own if defined?(Net::HTTP) Rack::MiniProfiler.unprofile_method(Net::HTTP, :request) rescue nil end # Now add our custom profiling module NetHttpSelectiveProfiler def request(request, *args, &block) # Check if MiniProfiler is active if ::Rack::MiniProfiler.current&.measure # Check if we're already inside a service-specific profiler timer = ::Rack::MiniProfiler.current&.current_timer timer_name = timer&.name || '' # Skip if we're inside DynamoDB or OpenSearch profiler if timer_name.start_with?('SQL: ') || timer_name.start_with?('OpenSearch:') # We're inside a service-specific profiler, don't double-profile super else # Profile this HTTP request with detailed information host = @address || 'unknown' port = @port || 'unknown' method = request.method || 'unknown' path = request.path || '/' # Identify the service for better labeling service = identify_service(host, port, path) if service == :dynamodb || service == :opensearch # Skip profiling for these as they have their own profilers super else # Build a descriptive label label = if service.is_a?(String) "#{service}: #{method} #{path}" else # Show full details for unknown services if (host == 'localhost' || host == '127.0.0.1') && port != 80 && port != 443 "HTTP #{method} localhost:#{port}#{path}" elsif port == 443 || port == 80 "HTTP #{method} #{host}#{path}" else "HTTP #{method} #{host}:#{port}#{path}" end end ::Rack::MiniProfiler.step(label) do super end end end else super end end private def identify_service(host, port, path) # DynamoDB return :dynamodb if host&.include?('dynamodb') || (host&.include?('amazonaws.com') && path == '/') # OpenSearch/Elasticsearch return :opensearch if port.to_s == '9200' || host&.include?('opensearch') || host&.include?('elasticsearch') # AWS Services return 'S3' if host&.include?('.s3.') || host&.include?('s3.amazonaws.com') return 'SQS' if host&.include?('sqs') && host&.include?('amazonaws.com') return 'SES' if host&.include?('email') && host&.include?('amazonaws.com') return 'SNS' if host&.include?('sns') && host&.include?('amazonaws.com') return 'Lambda' if host&.include?('lambda') && host&.include?('amazonaws.com') return 'CloudWatch' if host&.include?('monitoring') && host&.include?('amazonaws.com') # Third-party APIs return 'OpenAI' if host&.include?('api.openai.com') return 'Google' if host&.include?('googleapis.com') return 'Stripe' if host&.include?('stripe.com') return 'Twilio' if host&.include?('twilio.com') return 'SendGrid' if host&.include?('sendgrid') return 'GitHub' if host&.include?('github.com') || host&.include?('api.github.com') return 'Slack' if host&.include?('slack.com') return 'Webhook' if host&.include?('webhook') # No specific service identified nil end end # Prepend our module to Net::HTTP ::Net::HTTP.prepend(NetHttpSelectiveProfiler) Rails.logger.info 'Rack::MiniProfiler Net::HTTP selective profiling enabled' end end ``` -------------------------------- ### Require Rack Mini Profiler and AWS SDK Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling These blocks ensure that Rack::MiniProfiler and Aws::DynamoDB::Client are available. If they are not present, the code will not raise an error, allowing the application to run without them. ```ruby begin require 'rack-mini-profiler' rescue LoadError # Rack::MiniProfiler not present; no-op. end ``` ```ruby begin require 'aws-sdk-dynamodb' rescue LoadError # AWS SDK not present; no-op. The patch only applies when Aws::DynamoDB::Client exists. end ``` -------------------------------- ### Prepend Rack Mini Profiler DynamoDB Instrumentation Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Prepends the `RackMiniProfilerDynamoDB::Instrumentation` module to `Aws::DynamoDB::Client` if it's not already included. This ensures that DynamoDB client calls are profiled by Rack Mini Profiler. ```ruby unless ::Aws::DynamoDB::Client.ancestors.include?(RackMiniProfilerDynamoDB::Instrumentation) ::Aws::DynamoDB::Client.prepend(RackMiniProfilerDynamoDB::Instrumentation) end ``` -------------------------------- ### Configure MemoryStore Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Sets the storage to MemoryStore, suitable for single-process environments. This is the default when not in a Rails environment. ```ruby # set MemoryStore Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore ``` -------------------------------- ### ObjectSpace Statistics Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use the '?pp=analyze-memory' query parameter to report on ObjectSpace statistics without requiring the memory_profiler gem. ```bash ?pp=analyze-memory ``` -------------------------------- ### Configure Rack MiniProfiler with Heroku Redis Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Add this configuration to `config/initializers/mini_profiler.rb` when using Heroku Redis to ensure Mini Profiler functions correctly. This sets up storage options and uses Redis as the storage backend. ```ruby if Rails.env.production? Rack::MiniProfiler.config.storage_options = { url: ENV["REDIS_URL"], ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } } Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore end ``` -------------------------------- ### Prepend OpenSearch Transport Client Instrumentation Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Prepends the RackMiniProfilerOpenSearch::Instrumentation module to OpenSearch::Transport::Client if it is defined, ensuring requests made via the transport client are also profiled. ```ruby if defined?(::OpenSearch::Transport::Client) && !::OpenSearch::Transport::Client.ancestors.include?(RackMiniProfilerOpenSearch::Instrumentation) ::OpenSearch::Transport::Client.prepend(RackMiniProfilerOpenSearch::Instrumentation) end ``` -------------------------------- ### Sinatra Integration Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use Rack Mini Profiler within a Sinatra application by including it as middleware. ```ruby require 'rack-mini-profiler' class MyApp < Sinatra::Base use Rack::MiniProfiler end ``` -------------------------------- ### Instrument OpenSearch perform_request with Rack Mini Profiler Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Intercepts OpenSearch requests to profile operations. It builds a descriptive label for each operation, measures execution time, and records the details using `Rack::MiniProfiler.record_sql` for display in the profiler's SQL Summary. ```ruby if defined?(::Rack::MiniProfiler) && defined?(::OpenSearch) Rails.application.config.after_initialize do # Avoid double-prepend on reload module RackMiniProfilerOpenSearch module Instrumentation # Intercept perform_request to profile OpenSearch operations # # @param method [String] HTTP method (GET, POST, PUT, DELETE) # @param path [String] Request path (e.g., "/_search", "/index/_doc/123") # @param params [Hash] Query parameters # @param body [Hash, String, nil] Request body # @param headers [Hash, nil] Request headers # @return [OpenSearch::Transport::Transport::Response] def perform_request(method, path, params = {}, body = nil, headers = nil) return super unless mp_should_measure? # Parse operation type and details from path and body operation_label = build_opensearch_label(method, path, params, body) # Wrap in a step for the timeline view AND record SQL timing ::Rack::MiniProfiler.step("OpenSearch: #{operation_label}") do start_mono = Process.clock_gettime(Process::CLOCK_MONOTONIC) # Execute the actual OpenSearch request response = super elapsed_ms = mp_elapsed_ms(start_mono) # Extract response metrics response_info = extract_response_info(response, path) # Build the final label with response metrics sql_text = "#{operation_label} #{response_info}".strip # Record as SQL timing (shows in SQL Summary) begin ::Rack::MiniProfiler.record_sql(sql_text, elapsed_ms, nil) rescue => e Rails.logger.debug { "OpenSearch profiling record_sql failed: #{e.class}: #{e.message}" } end response end rescue => e # Preserve original exceptions from OpenSearch raise e end private # True if MiniProfiler is currently measuring this request. # # @return [Boolean] def mp_should_measure? c = ::Rack::MiniProfiler.current !!(c && c.measure && c.current_timer) end # Converts a monotonic start time to elapsed milliseconds. # # @param start_mono [Float] Monotonic clock reading captured before the operation. # @return [Float] Elapsed time in milliseconds with 0.1ms precision. def mp_elapsed_ms(start_mono) ((Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_mono) * 1000.0).round(1) end # Builds a concise, informative label for the OpenSearch operation. # # @param method [String] HTTP method # @param path [String] Request path # @param params [Hash] Query parameters # @param body [Hash, String, nil] Request body # @return [String] def build_opensearch_label(method, path, params, body) # Extract index name from path index_name = extract_index_name(path) # Determine operation type operation = detect_operation(method, path) # Build base label label = operation.to_s label += " [#{index_name}]" if index_name && index_name != '_all' # Add operation-specific details case operation when :search label += extract_search_details(body, params) when :multi_search, :msearch label += extract_multi_search_details(body) when :bulk label += extract_bulk_details(body) when :index, :create, :update doc_id = extract_doc_id(path) label += " id:#{truncate_value(doc_id, 20)}" if doc_id when :delete_by_query label += extract_delete_by_query_details(body) end label rescue => e Rails.logger.debug { "OpenSearch profiling label build failed: #{e.class}: #{e.message}" } "#{method} #{path}" end # Extract index name from the request path # # @param path [String] # @return [String, nil] def extract_index_name(path) # Common patterns: # /index_name/_search # /index_name/_doc/id # /index_name/_bulk # /_search (all indices) return nil if path.start_with?('/_') parts = path.split('/') parts[1] if parts[1] && !parts[1].start_with?('_') end # Detect the operation type from method and path # # @param method [String] # @param path [String] # @return [Symbol] ``` -------------------------------- ### Instrument DynamoDB Client Calls Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Add this initializer to instrument Aws::DynamoDB::Client calls so they appear in Rack::MiniProfiler's SQL Summary. This is useful for non-ActiveRecord applications. ```ruby # config/initializers/dynamodb_profiler.rb # Instrument Aws::DynamoDB::Client calls so they appear in Rack::MiniProfiler's SQL Summary. ``` -------------------------------- ### Include Optional Profiling Gems Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Add these gems to your Gemfile to enable memory profiling and call-stack profiling (flamegraphs). ```ruby # For memory profiling gem 'memory_profiler' # For call-stack profiling flamegraphs gem 'stackprof' ``` -------------------------------- ### DynamoDB Operation Profiling Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling This section describes the instrumentation applied to various AWS DynamoDB operations when Rack Mini Profiler and the AWS SDK are present. These methods are patched to record performance metrics and display them in the Rack Mini Profiler's SQL panel. ```APIDOC ## DynamoDB Operation Profiling ### Description This section details the instrumentation applied to various AWS DynamoDB operations when Rack Mini Profiler and the AWS SDK are present. These methods are patched to record performance metrics and display them in the Rack Mini Profiler's SQL panel. ### Methods - **get_item** - **put_item** - **update_item** - **delete_item** - **query** - **scan** - **batch_get_item** - **batch_write_item** ### Parameters #### Common Parameters for all profiled methods: - **params** (Hash) - Required/Optional - Request parameters for the specific DynamoDB operation. - **options** (Hash) - Required/Optional - Additional AWS SDK options for the call. ### Request Body Not applicable directly as these are method calls with parameters. ### Response #### Success Response (200) - **Seahorse::Client::Response** - The response object from the AWS SDK. ### Internal Profiling Logic #### `profile_dynamodb(operation, params = {}, &blk)` ##### Description Profiles a DynamoDB operation so it shows up in Rack::MiniProfiler's SQL panel. It gates off active MiniProfiler requests, measures elapsed time, records the SQL via `Rack::MiniProfiler.record_sql`, and wraps the operation in a `step` for the timeline view. ##### Parameters - **operation** (String) - DynamoDB operation name (e.g., 'query', 'get_item'). - **params** (Hash) - The exact params hash sent to AWS SDK (symbols or strings). - **blk** (Block) - The original DynamoDB operation call. ##### Returns - **Seahorse::Client::Response** - The AWS SDK response (unchanged). #### `build_ddb_label(operation, params)` ##### Description Builds a label for the DynamoDB operation, including the operation name and relevant parameters for identification. ##### Parameters - **operation** (String) - The name of the DynamoDB operation. - **params** (Hash) - The parameters sent with the operation. ##### Returns - **String** - A formatted string representing the operation and its key parameters. #### `ddb_response_size_bytes(response)` ##### Description Calculates the size of the DynamoDB response in bytes. ##### Parameters - **response** (Seahorse::Client::Response) - The response object from the AWS SDK. ##### Returns - **Integer** - The size of the response in bytes. ``` -------------------------------- ### Upgrade Speedscope with Rake Task Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/lib/html/speedscope/README.md Run this rake task to download the latest Speedscope release, replace existing files, and update the index.html for local asset loading. This ensures Speedscope works offline. ```ruby bundle exec rake speedscope_upgrade ``` -------------------------------- ### SPA Page Transition Handling Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md In Single Page Applications, call `window.MiniProfiler.pageTransition()` on route changes to clear profiling data from previous pages and reset aggregate statistics. ```javascript if (window.MiniProfiler !== undefined) { window.MiniProfiler.pageTransition(); } ``` -------------------------------- ### DynamoDB Operation Profiling Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling These methods instrument common DynamoDB operations to be profiled by Rack Mini Profiler. They wrap the original SDK calls, measure execution time, and record the operation details. ```ruby def get_item(params = {}, options = {}) profile_dynamodb('get_item', params) { super } end ``` ```ruby def put_item(params = {}, options = {}) profile_dynamodb('put_item', params) { super } end ``` ```ruby def update_item(params = {}, options = {}) profile_dynamodb('update_item', params) { super } end ``` ```ruby def delete_item(params = {}, options = {}) profile_dynamodb('delete_item', params) { super } end ``` ```ruby def query(params = {}, options = {}) profile_dynamodb('query', params) { super } end ``` ```ruby def scan(params = {}, options = {}) profile_dynamodb('scan', params) { super } end ``` ```ruby def batch_get_item(params = {}, options = {}) profile_dynamodb('batch_get_item', params) { super } end ``` ```ruby def batch_write_item(params = {}, options = {}) profile_dynamodb('batch_write_item', params) { super } end ``` -------------------------------- ### Authorize Requests in Production Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use this snippet in your ApplicationController's before_action to authorize profiling for specific users in production environments. Ensure that Rack::MiniProfiler.authorize_request is called only when profiling is permitted. ```ruby # inside your ApplicationController before_action do if current_user && current_user.is_admin? Rack::MiniProfiler.authorize_request end end ``` -------------------------------- ### DynamoDB Profiling Helper Method Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling This private method, `profile_dynamodb`, is the core of the instrumentation. It checks if profiling is active, measures the time taken for the DynamoDB operation, and records it using `Rack::MiniProfiler.record_sql`. ```ruby def profile_dynamodb(operation, params = {}, &blk) return yield unless mp_should_measure? base_label = build_ddb_label(operation, params) ::Rack::MiniProfiler.step("SQL: #{base_label}") do start_mono = Process.clock_gettime(Process::CLOCK_MONOTONIC) response = blk.call elapsed_ms = mp_elapsed_ms(start_mono) size = ddb_response_size_bytes(response) sql_text = "#{base_label} [#{size} bytes]" begin ::Rack::MiniProfiler.record_sql(sql_text, elapsed_ms, nil) rescue => e Rails.logger.debug { "DynamoDB profiling record_sql failed: #{e.class}: #{e.message}" } end end end ``` -------------------------------- ### Build DynamoDB Operation Label Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Constructs a descriptive label for DynamoDB operations, including operation type, table name, key information, and expression details. ```ruby def build_ddb_label(operation, params) # Support symbol and string keys (SDK accepts both). label = "#{operation}" case operation when 'query', 'scan' names = params[:expression_attribute_names] || params['ExpressionAttributeNames'] values = params[:expression_attribute_values] || params['ExpressionAttributeValues'] key_cond = params[:key_condition_expression] || params['KeyConditionExpression'] filter = params[:filter_expression] || params['FilterExpression'] parts = [] parts << "#{substitute_expressions(key_cond, names, values)}" if key_cond parts << "#{substitute_expressions(filter, names, values)}" if filter label += " (#{parts.join('; ')})" if parts.any? idx = params[:index_name] || params['IndexName'] label += " [idx: #{idx}]" if idx when 'get_item', 'update_item', 'delete_item' key = params[:key] || params['Key'] if key && key.respond_to?(:map) key_info = key.map do |k, v| raw = extract_attr_value(v) "#{k}=#{truncate_value(raw)}" end.join(', ') label += " (#{key_info})" unless key_info.empty? end when 'batch_get_item' reqs = params[:request_items] || params['RequestItems'] if reqs && reqs.respond_to?(:values) all_keys = [] reqs.each do |_table, config| keys = Array((config[:keys] || config['Keys'])) keys.each do |key| # Extract the hash key value (usually the first key) key_str = key.map { |k, v| "#{k}=#{truncate_value(extract_attr_value(v))}" }.join(',') all_keys << key_str end end total = all_keys.size if total <= 10 && total.positive? # Show actual keys if 10 or fewer label += " [#{all_keys.join(', ')}]" elsif total.positive? # Show count if more than 10 label += " [#{total} keys]" end end when 'batch_write_item' reqs = params[:request_items] || params['RequestItems'] if reqs && reqs.respond_to?(:values) all_ops = [] reqs.each do |_table, operations| Array(operations).each do |op| if op[:put_request] || op['PutRequest'] item = (op[:put_request] || op['PutRequest'])[:item] || (op[:put_request] || op['PutRequest'])['Item'] if item && (key = item[:hash_key] || item['hash_key']) all_ops << "put:#{truncate_value(extract_attr_value(key))}" else all_ops << 'put' end elsif op[:delete_request] || op['DeleteRequest'] key = (op[:delete_request] || op['DeleteRequest'])[:key] || (op[:delete_request] || op['DeleteRequest'])['Key'] if key && (hash_key = key[:hash_key] || key['hash_key']) all_ops << "del:#{truncate_value(extract_attr_value(hash_key))}" else all_ops << 'del' end end end end total = all_ops.size if total <= 10 && total.positive? # Show actual operations if 10 or fewer label += " [#{all_ops.join(', ')}]" elsif total.positive? # Show count if more than 10 label += " [#{total} operations]" end end end label rescue => e # Handle potential errors during label building "#{operation} [error]" end ``` -------------------------------- ### Configure Rails Assets for MiniProfiler Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Register MiniProfiler's assets in the Rails asset pipeline by providing a lambda to the `assets_url` configuration. This callback returns the URL for loading assets. ```ruby Rack::MiniProfiler.config.assets_url = ->(name, version, env) { ActionController::Base.helpers.asset_path(name) } ``` -------------------------------- ### Resolve Net::HTTP Stack Level Too Deep Errors (require: false) Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If you previously had 'require: false' for rack-mini-profiler and are experiencing Net::HTTP stack level too deep errors, use this modified gem line. ```ruby gem 'rack-mini-profiler', require: ['prepend_net_http_patch'] ``` -------------------------------- ### Extract Response Information in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Gathers key information from an OpenSearch response, including 'took' time, hit count, created/updated/deleted counts, and response size. Formats the size into human-readable units (bytes, KB, MB). ```ruby def extract_response_info(response, path) return '' unless response && response.body body = parse_body(response.body) return '' unless body.is_a?(Hash) info = [] # Extract took time (search operations) if took = body['took'] || body[:took] info << "took:#{took}ms" end # Extract hit count (search operations) if hits = body['hits'] || body[:hits] total = hits['total'] || hits[:total] if total.is_a?(Hash) total = total['value'] || total[:value] end info << "#{total} hits" if total end # Extract created/updated/deleted counts (bulk/delete_by_query) if body['created'] || body[:created] info << "created:#{body['created'] || body[:created]}" end if body['updated'] || body[:updated] info << "updated:#{body['updated'] || body[:updated]}" end if body['deleted'] || body[:deleted] info << "deleted:#{body['deleted'] || body[:deleted]}" end # Add response size size = response_size_bytes(response) if size > 0 size_str = if size < 1024 "#{size} bytes" elsif size < 1024 * 1024 "#{(size / 1024.0).round(1)}KB" else "#{(size / (1024.0 * 1024.0)).round(1)}MB" end info << "[#{size_str}]" end info.empty? ? '' : "(#{info.join(', ')})" rescue => e Rails.logger.debug { "OpenSearch profiling response info failed: #{e.class}: #{e.message}" } '' end ``` -------------------------------- ### Profile Arbitrary Code Block Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Profiles any block of code by passing a name and the block to `Rack::MiniProfiler.step`. Useful for measuring specific operations. ```ruby Rack::MiniProfiler.step('Adding two elements') do result = 1 + 2 end ``` -------------------------------- ### Calculate Response Size in Bytes Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Calculates the size of a response body in bytes. Handles String and Hash bodies, returning 0 for other types or on error. ```ruby def response_size_bytes(response) if response.respond_to?(:body) body = response.body case body when String body.bytesize when Hash JSON.generate(body).bytesize else 0 end else 0 end rescue 0 end ``` -------------------------------- ### Filter Memory Profile Report Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Use query parameters like `memory_profiler_allow_files` and `memory_profiler_ignore_files` to filter memory profiling results. `memory_profiler_top` controls the number of results per section. ```bash ?pp=profile-memory&memory_profiler_allow_files=active_record|app ``` -------------------------------- ### Resolve PG Stack Level Too Deep Errors (require: false) Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If you previously had 'require: false' for rack-mini-profiler and are experiencing PG stack level too deep errors, use this modified gem line. ```ruby gem 'rack-mini-profiler', require: ['prepend_pg_patch'] ``` -------------------------------- ### Describe Query Structure Concisely in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Provides a concise string representation of different Elasticsearch query types (match, term, bool, query_string, range). Handles nested structures and truncates long values for brevity. Returns nil for invalid input. ```ruby # Describe a query structure concisely # # @param query [Hash] # @return [String, nil] def describe_query(query) return nil unless query.is_a?(Hash) # Handle common query types if query['match'] || query[:match] field_value = (query['match'] || query[:match]).to_a.first return "{match:{#{field_value[0]}:#{truncate_value(field_value[1].to_s, 20)}}}" if field_value end if query['term'] || query[:term] field_value = (query['term'] || query[:term]).to_a.first return "{term:{#{field_value[0]}:#{truncate_value(field_value[1].to_s, 20)}}}" if field_value end if query['bool'] || query[:bool] bool_query = query['bool'] || query[:bool] clauses = [] clauses << 'must' if bool_query['must'] || bool_query[:must] clauses << 'should' if bool_query['should'] || bool_query[:should] clauses << 'filter' if bool_query['filter'] || bool_query[:filter] clauses << 'must_not' if bool_query['must_not'] || bool_query[:must_not] return "{bool:[#{clauses.join(',')}]}" unless clauses.empty? end if query['query_string'] || query[:query_string] qs = query['query_string'] || query[:query_string] q = qs['query'] || qs[:query] return "{query_string:#{truncate_value(q.to_s, 30)}}" if q end if query['range'] || query[:range] field = (query['range'] || query[:range]).keys.first return "{range:#{field}}" if field end # For other query types, just show the type query_type = query.keys.first "{#{query_type}}" rescue nil end ``` -------------------------------- ### Environment Variable for Net::HTTP Patching Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Disable the patch applied to Net::HTTP by setting the RACK_MINI_PROFILER_PATCH_NET_HTTP environment variable to 'false'. ```bash export RACK_MINI_PROFILER_PATCH_NET_HTTP="false" ``` -------------------------------- ### Extract Delete By Query Details in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Extracts query details from delete-by-query requests. It parses the body and attempts to describe the query if present. ```ruby def extract_delete_by_query_details(body) return '' unless body body_hash = parse_body(body) return '' unless body_hash.is_a?(Hash) if query = body_hash['query'] || body_hash[:query] query_desc = describe_query(query) return " query:#{query_desc}" if query_desc end '' end ``` -------------------------------- ### Extract Search Details from Body and Params in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Parses the request body and parameters to extract details relevant to a search operation, such as query clauses, aggregations, size, and from parameters. Returns a formatted string of details or an empty string. ```ruby # Extract search query details # # @param body [Hash, String, nil] # @param params [Hash] # @return [String] def extract_search_details(body, params) return '' unless body body_hash = parse_body(body) return '' unless body_hash.is_a?(Hash) details = [] # Extract query details if query = body_hash['query'] || body_hash[:query] query_desc = describe_query(query) details << "query:#{query_desc}" if query_desc end # Extract aggregations if aggs = body_hash['aggs'] || body_hash[:aggs] || body_hash['aggregations'] || body_hash[:aggregations] agg_desc = describe_aggregations(aggs) details << "aggs:#{agg_desc}" if agg_desc end # Extract size and from size = body_hash['size'] || body_hash[:size] from = body_hash['from'] || body_hash[:from] details << "size:#{size}" if size details << "from:#{from}" if from && from > 0 details.empty? ? '' : " (#{details.join(', ')})" end ``` -------------------------------- ### Truncate String Value Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Performs allocation-light truncation of a string value to a maximum length, appending '...' if truncation occurs. Avoids ActiveSupport dependencies. ```ruby def truncate_value(val, max = 60) s = val.to_s return s if s.length <= max s[0, max - 3] + '...' end ``` -------------------------------- ### Extract Multi-Search Details in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Parses request bodies to count queries in multi-search requests. Handles both String and Array formats for the body. ```ruby def extract_multi_search_details(body) return '' unless body # Multi-search body is newline-delimited JSON if body.is_a?(String) lines = body.split("\n").reject(&:empty?) # Every pair of lines is a search (header + body) query_count = lines.size / 2 return " [#{query_count} queries]" elsif body.is_a?(Array) # Sometimes it might be an array of requests return " [#{body.size} queries]" end '' end ``` -------------------------------- ### Extract Bulk Operation Details in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Analyzes bulk request bodies to summarize the counts of index, create, update, and delete operations. Supports both newline-delimited JSON strings and arrays of hashes. ```ruby def extract_bulk_details(body) return '' unless body operations = { index: 0, create: 0, update: 0, delete: 0 } if body.is_a?(String) # Bulk body is newline-delimited JSON body.split("\n").each do |line| next if line.empty? begin doc = JSON.parse(line) operations[:index] += 1 if doc['index'] || doc[:index] operations[:create] += 1 if doc['create'] || doc[:create] operations[:update] += 1 if doc['update'] || doc[:update] operations[:delete] += 1 if doc['delete'] || doc[:delete] rescue # Skip malformed lines end end elsif body.is_a?(Array) body.each do |item| next unless item.is_a?(Hash) operations[:index] += 1 if item['index'] || item[:index] operations[:create] += 1 if item['create'] || item[:create] operations[:update] += 1 if item['update'] || item[:update] operations[:delete] += 1 if item['delete'] || item[:delete] end end ops = [] operations.each { |type, count| ops << "#{count} #{type}" if count > 0 } ops.empty? ? '' : " [#{ops.join(', ')}]" end ``` -------------------------------- ### Enable Rails Patches for Mini Profiler 2.0+ Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If you want Mini Profiler to continue patching Rails methods for information gathering in version 2.0.0 and later, use this gem line in your Gemfile. ```ruby gem 'rack-mini-profiler', require: ['enable_rails_patches'] ``` -------------------------------- ### Set Authorization Mode Manually Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If automatic authorization mode detection fails (e.g., running outside Rails, using require: false), manually set the authorization mode to :allow_authorized for production environments. This ensures that only explicitly authorized requests are profiled. ```ruby Rack::MiniProfiler.config.authorization_mode = :allow_authorized ``` -------------------------------- ### Manually Require Rails Patches for Mini Profiler 2.0+ Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md If you prefer to manually require Mini Profiler and enable Rails patches in version 2.0.0 and later, use this configuration. ```ruby gem 'rack-mini-profiler', require: ['enable_rails_patches', 'rack-mini-profiler'] ``` -------------------------------- ### Environment Variable for ORM Patching Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md Override the default ORM patching behavior using the RACK_MINI_PROFILER_PATCH environment variable. Set to 'false' to disable all ORM patching. ```bash export RACK_MINI_PROFILER_PATCH="pg,mongoid" ``` ```bash # or export RACK_MINI_PROFILER_PATCH="false" ``` ```ruby # initializers/rack_profiler.rb: SqlPatches.patch %w(mongo) ``` -------------------------------- ### Substitute DynamoDB Expressions Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Replaces ExpressionAttributeNames and ExpressionAttributeValues in a DynamoDB expression string for clearer profiling output. Handles potential errors during substitution by logging and returning the original expression. ```ruby def substitute_expressions(expr, names, values) return '' unless expr s = expr.to_s.dup if names.respond_to?(:each) names.each { |placeholder, real| s.gsub!(placeholder.to_s, real.to_s) } end if values.respond_to?(:each) values.each do |placeholder, typed| raw = extract_attr_value(typed) s.gsub!(placeholder.to_s, raw.inspect) end end s rescue => e Rails.logger.debug { "DynamoDB profiling expression substitution failed: #{e.class}: #{e.message}" } expr.to_s end ``` -------------------------------- ### Parse Request Body in Ruby Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-OpenSearch-Profiling Safely parses a request body, which can be a Hash, a JSON string, or nil. Returns a Hash if parsing is successful, otherwise nil. Includes basic error handling for JSON parsing. ```ruby def parse_body(body) case body when Hash body when String JSON.parse(body) else nil end rescue nil end ``` -------------------------------- ### Calculate DynamoDB Response Size Source: https://github.com/miniprofiler/rack-mini-profiler/wiki/Rails-Integration-for-DynamoDB-Profiling Computes the approximate response size in bytes by JSON-encoding the response's hash representation. Returns 0 if an error occurs during calculation, logging the error. ```ruby def ddb_response_size_bytes(response) h = response.respond_to?(:to_h) ? response.to_h : {} JSON.generate(h).bytesize rescue => e Rails.logger.debug { "DynamoDB profiling size calc failed: #{e.class}: #{e.message}" } 0 end ``` -------------------------------- ### Resolve Peek-MySQL2 Stack Level Too Deep Errors Source: https://github.com/miniprofiler/rack-mini-profiler/blob/master/README.md For Rails versions 5 and above, use this gem spec to resolve stack level too deep errors when using peek-mysql2 with rack-mini-profiler. ```ruby gem 'rack-mini-profiler', require: ['prepend_mysql2_patch', 'rack-mini-profiler'] ```