marginalia-1.10.1/0000755000004100000410000000000014015440510013720 5ustar www-datawww-datamarginalia-1.10.1/.travis.yml0000644000004100000410000000072014015440510016030 0ustar www-datawww-datalanguage: ruby sudo: false services: - mysql - postgresql rvm: - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - 2.7 services: - mysql - postgresql script: "bundle exec rake db:reset test:all" gemfile: - gemfiles/4.2.gemfile - gemfiles/4.2.api.gemfile - gemfiles/5.0.gemfile - gemfiles/5.1.gemfile - gemfiles/5.2.gemfile matrix: exclude: - rvm: 2.7 gemfile: gemfiles/4.2.gemfile - rvm: 2.7 gemfile: gemfiles/4.2.api.gemfile marginalia-1.10.1/test/0000755000004100000410000000000014015440510014677 5ustar www-datawww-datamarginalia-1.10.1/test/query_comments_test.rb0000644000004100000410000002717214015440510021346 0ustar www-datawww-data# -*- coding: utf-8 -*- require 'rails/version' def using_rails_api? ENV["TEST_RAILS_API"] == true end def request_id_available? Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('3.2') end def active_job_available? Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('4.2') end def adapter_pool_available? Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new('3.2.19') end require "minitest/autorun" require "mocha/minitest" require 'logger' require 'pp' require 'active_record' require 'action_controller' require 'sidekiq' require 'sidekiq/testing' if request_id_available? require 'action_dispatch/middleware/request_id' end if active_job_available? require 'active_job' end if using_rails_api? require 'rails-api/action_controller/api' end # Shim for compatibility with older versions of MiniTest MiniTest::Test = MiniTest::Unit::TestCase unless defined?(MiniTest::Test) # From version 4.1, ActiveRecord expects `Rails.env` to be # defined if `Rails` is defined if defined?(Rails) && !defined?(Rails.env) module Rails def self.env end end end require 'marginalia' RAILS_ROOT = File.expand_path(File.dirname(__FILE__)) ActiveRecord::Base.establish_connection({ :adapter => ENV["DRIVER"] || "mysql", :host => "localhost", :username => ENV["DB_USERNAME"] || "root", :database => "marginalia_test" }) class Post < ActiveRecord::Base end class PostsController < ActionController::Base def driver_only ActiveRecord::Base.connection.execute "select id from posts" if Gem::Version.new(Rails::VERSION::STRING) >= Gem::Version.new('5') render body: nil else render nothing: true end end end module API module V1 class PostsController < ::PostsController end end end if active_job_available? class PostsJob < ActiveJob::Base def perform Post.first end end end class PostsSidekiqJob include Sidekiq::Worker def perform Post.first end end if using_rails_api? class PostsApiController < ActionController::API def driver_only ActiveRecord::Base.connection.execute "select id from posts" head :no_content end end end unless Post.table_exists? ActiveRecord::Schema.define do create_table "posts", :force => true do |t| end end end Marginalia::Railtie.insert class MarginaliaTest < MiniTest::Test def setup @queries = [] ActiveSupport::Notifications.subscribe "sql.active_record" do |*args| @queries << args.last[:sql] end @env = Rack::MockRequest.env_for('/') ActiveJob::Base.queue_adapter = :inline end def test_double_annotate ActiveRecord::Base.connection.expects(:annotate_sql).returns("select id from posts").once ActiveRecord::Base.connection.send(:select, "select id from posts") ensure ActiveRecord::Base.connection.unstub(:annotate_sql) end def test_exists skip if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('3.2') Post.exists? assert_match %r{/\*application:rails\*/$}, @queries.last end def test_query_commenting_on_mysql_driver_with_no_action ActiveRecord::Base.connection.execute "select id from posts" assert_match %r{select id from posts /\*application:rails\*/$}, @queries.first end if ENV["DRIVER"] =~ /^mysql/ def test_query_commenting_on_mysql_driver_with_binary_chars ActiveRecord::Base.connection.execute "select id from posts /* \x81\x80\u0010\ */" assert_equal "select id from posts /* \x81\x80\u0010 */ /*application:rails*/", @queries.first end end if ENV["DRIVER"] =~ /^postgres/ def test_query_commenting_on_postgres_update ActiveRecord::Base.connection.expects(:annotate_sql).returns("update posts set id = 1").once ActiveRecord::Base.connection.send(:exec_update, "update posts set id = 1") ensure ActiveRecord::Base.connection.unstub(:annotate_sql) end def test_query_commenting_on_postgres_delete ActiveRecord::Base.connection.expects(:annotate_sql).returns("delete from posts where id = 1").once ActiveRecord::Base.connection.send(:exec_delete, "delete from posts where id = 1") ensure ActiveRecord::Base.connection.unstub(:annotate_sql) end end def test_query_commenting_on_mysql_driver_with_action PostsController.action(:driver_only).call(@env) assert_match %r{select id from posts /\*application:rails,controller:posts,action:driver_only\*/$}, @queries.first if using_rails_api? PostsApiController.action(:driver_only).call(@env) assert_match %r{select id from posts /\*application:rails,controller:posts_api,action:driver_only\*/$}, @queries.second end end def test_configuring_application Marginalia.application_name = "customapp" PostsController.action(:driver_only).call(@env) assert_match %r{/\*application:customapp,controller:posts,action:driver_only\*/$}, @queries.first if using_rails_api? PostsApiController.action(:driver_only).call(@env) assert_match %r{/\*application:customapp,controller:posts_api,action:driver_only\*/$}, @queries.second end end def test_configuring_query_components Marginalia::Comment.components = [:controller] PostsController.action(:driver_only).call(@env) assert_match %r{/\*controller:posts\*/$}, @queries.first if using_rails_api? PostsApiController.action(:driver_only).call(@env) assert_match %r{/\*controller:posts_api\*/$}, @queries.second end end def test_last_line_component Marginalia::Comment.components = [:line] PostsController.action(:driver_only).call(@env) # Because "lines_to_ignore" by default includes "marginalia" and "gem", the # extracted line line will be from the line in this file that actually # triggers the query. assert_match %r{/\*line:test/query_comments_test.rb:[0-9]+:in `driver_only'\*/$}, @queries.first end def test_last_line_component_with_lines_to_ignore Marginalia::Comment.lines_to_ignore = /foo bar/ Marginalia::Comment.components = [:line] PostsController.action(:driver_only).call(@env) # Because "lines_to_ignore" does not include "marginalia", the extracted # line will be from marginalia/comment.rb. assert_match %r{/\*line:.*lib/marginalia/comment.rb:[0-9]+}, @queries.first end def test_default_lines_to_ignore_regex line = "/gems/a_gem/lib/a_gem.rb:1:in `some_method'" call_stack = [line] + caller assert_match( call_stack.detect { |line| line !~ Marginalia::Comment::DEFAULT_LINES_TO_IGNORE_REGEX }, line ) end def test_hostname_and_pid Marginalia::Comment.components = [:hostname, :pid] PostsController.action(:driver_only).call(@env) assert_match %r{/\*hostname:#{Socket.gethostname},pid:#{Process.pid}\*/$}, @queries.first end def test_controller_with_namespace Marginalia::Comment.components = [:controller_with_namespace] API::V1::PostsController.action(:driver_only).call(@env) assert_match %r{/\*controller_with_namespace:API::V1::PostsController}, @queries.first end if adapter_pool_available? def test_db_host Marginalia::Comment.components = [:db_host] API::V1::PostsController.action(:driver_only).call(@env) assert_match %r{/\*db_host:localhost}, @queries.first end def test_database Marginalia::Comment.components = [:database] API::V1::PostsController.action(:driver_only).call(@env) assert_match %r{/\*database:marginalia_test}, @queries.first end def test_socket # setting socket in configuration would break some connections - mock it instead pool = ActiveRecord::Base.connection_pool pool.spec.stubs(:config).returns({:socket => "marginalia_socket"}) Marginalia::Comment.components = [:socket] API::V1::PostsController.action(:driver_only).call(@env) assert_match %r{/\*socket:marginalia_socket}, @queries.first pool.spec.unstub(:config) end end if request_id_available? def test_request_id @env["action_dispatch.request_id"] = "some-uuid" Marginalia::Comment.components = [:request_id] PostsController.action(:driver_only).call(@env) assert_match %r{/\*request_id:some-uuid.*}, @queries.first if using_rails_api? PostsApiController.action(:driver_only).call(@env) assert_match %r{/\*request_id:some-uuid.*}, @queries.second end end else def test_request_id_is_noop_on_old_rails @env["action_dispatch.request_id"] = "some-uuid" Marginalia::Comment.components = [:request_id] PostsController.action(:driver_only).call(@env) assert_match %r{^select id from posts$}, @queries.first end end if active_job_available? def test_active_job Marginalia::Comment.components = [:job] PostsJob.perform_later assert_match %{job:PostsJob}, @queries.first Post.first refute_match %{job:PostsJob}, @queries.last end def test_active_job_with_sidekiq Marginalia::Comment.components = [:job, :sidekiq_job] PostsJob.perform_later assert_match %{job:PostsJob}, @queries.first Post.first refute_match %{job:PostsJob}, @queries.last end end def test_sidekiq_job Marginalia::Comment.components = [:sidekiq_job] Marginalia::SidekiqInstrumentation.enable! # Test harness does not run Sidekiq middlewares by default so include testing middleware. Sidekiq::Testing.server_middleware do |chain| chain.add Marginalia::SidekiqInstrumentation::Middleware end Sidekiq::Testing.fake! PostsSidekiqJob.perform_async PostsSidekiqJob.drain assert_match %{sidekiq_job:PostsSidekiqJob}, @queries.first Post.first refute_match %{sidekiq_job:PostsSidekiqJob}, @queries.last end def test_good_comment assert_equal Marginalia::Comment.escape_sql_comment('app:foo'), 'app:foo' end def test_bad_comments assert_equal Marginalia::Comment.escape_sql_comment('*/; DROP TABLE USERS;/*'), '; DROP TABLE USERS;' assert_equal Marginalia::Comment.escape_sql_comment('**//; DROP TABLE USERS;/*'), '; DROP TABLE USERS;' end def test_inline_annotations Marginalia.with_annotation("foo") do Post.first end Post.first assert_match %r{/\*foo\*/$}, @queries.first refute_match %r{/\*foo\*/$}, @queries.last # Assert we're not adding an empty comment, either refute_match %r{/\*\s*\*/$}, @queries.last end def test_nested_inline_annotations Marginalia.with_annotation("foo") do Marginalia.with_annotation("bar") do Post.first end end assert_match %r{/\*foobar\*/$}, @queries.first end def test_bad_inline_annotations Marginalia.with_annotation("*/; DROP TABLE USERS;/*") do Post.first end Marginalia.with_annotation("**//; DROP TABLE USERS;//**") do Post.first end assert_match %r{/\*; DROP TABLE USERS;\*/$}, @queries.first assert_match %r{/\*; DROP TABLE USERS;\*/$}, @queries.last end def test_inline_annotations_are_deduped Marginalia.with_annotation("foo") do ActiveRecord::Base.connection.execute "select id from posts /*foo*/" end assert_match %r{select id from posts /\*foo\*/ /\*application:rails\*/$}, @queries.first end def test_add_comments_to_beginning_of_query Marginalia::Comment.prepend_comment = true ActiveRecord::Base.connection.execute "select id from posts" assert_match %r{/\*application:rails\*/ select id from posts$}, @queries.first ensure Marginalia::Comment.prepend_comment = nil end def teardown Marginalia.application_name = nil Marginalia::Comment.lines_to_ignore = nil Marginalia::Comment.components = [:application, :controller, :action] ActiveSupport::Notifications.unsubscribe "sql.active_record" end end marginalia-1.10.1/README.md0000644000004100000410000001225414015440510015203 0ustar www-datawww-data# marginalia [![Build Status](https://travis-ci.org/basecamp/marginalia.svg?branch=master)](https://travis-ci.org/basecamp/marginalia) Attach comments to your ActiveRecord queries. By default, it adds the application, controller, and action names as a comment at the end of each query. This helps when searching log files for queries, and seeing where slow queries came from. For example, once enabled, your logs will look like: Account Load (0.3ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`queenbee_id` = 1234567890 LIMIT 1 /*application:BCX,controller:project_imports,action:show*/ You can also use these query comments along with a tool like [pt-query-digest](http://www.percona.com/doc/percona-toolkit/2.1/pt-query-digest.html#query-reviews) to automate identification of controllers and actions that are hotspots for slow queries. This gem was created at 37signals. You can read more about how we use it [on our blog](http://37signals.com/svn/posts/3130-tech-note-mysql-query-comments-in-rails). This has been tested and used in production with both the mysql and mysql2 gems, tested on Rails 2.3.5 through 4.1.x. It has also been tested for sqlite3 and postgres. Patches are welcome for other database adapters. ## Installation ### For Rails 3.x and 4.x: # Gemfile gem 'marginalia' ### For Rails 2.x: If using cached externals, add to your `config/externals.yml` file. Or, if your prefer using `config.gem`, you can use: config.gem 'marginalia' Finally, if bundled, you'll need to manually run the initialization step in an initializer, e.g.: # Gemfile gem 'marginalia', :require => false #config/initializers/marginalia.rb require 'marginalia' Marginalia::Railtie.insert ### Customization Optionally, you can set the application name shown in the log like so in an initializer (e.g. `config/initializers/marginalia.rb`): Marginalia.application_name = "BCX" For Rails 3 applications, the name will default to your Rails application name. For Rails 2 applications, "rails" is used as the default application name. #### Components You can also configure the components of the comment that will be appended, by setting `Marginalia::Comment.components`. By default, this is set to: Marginalia::Comment.components = [:application, :controller, :action] Which results in a comment of `application:#{application_name},controller:#{controller.name},action:#{action_name}`. You can re-order or remove these components. You can also add additional comment components of your desire by defining new module methods for `Marginalia::Comment` which return a string. For example: module Marginalia module Comment def self.mycommentcomponent "TEST" end end end Marginalia::Comment.components = [:application, :mycommentcomponent] Which will result in a comment like `application:#{application_name},mycommentcomponent:TEST` The calling controller is available to these methods via `@controller`. Marginalia ships with `:application`, `:controller`, and `:action` enabled by default. In addition, implementation is provided for: * `:line` (for file and line number calling query). :line supports a configuration by setting a regexp in `Marginalia::Comment.lines_to_ignore` to exclude parts of the stacktrace from inclusion in the line comment. * `:controller_with_namespace` to include the full classname (including namespace) of the controller. * `:job` to include the classname of the ActiveJob being performed. * `:hostname` to include ```Socket.gethostname```. * `:pid` to include current process id. With ActiveRecord >= 3.2.19: * `:db_host` to include the configured database hostname. * `:socket` to include the configured database socket. * `:database` to include the configured database name. Pull requests for other included comment components are welcome. #### Prepend comments By default marginalia appends the comments at the end of the query. Certain databases, such as MySQL will truncate the query text. This is the case for slow query logs and the results of querying some InnoDB internal tables where the length of the query is more than 1024 bytes. In order to not lose the marginalia comments from your logs, you can prepend the comments using this option: Marginalia::Comment.prepend_comment = true #### Inline query annotations In addition to the request or job-level component-based annotations, Marginalia may be used to add inline annotations to specific queries using a block-based API. For example, the following code: Marginalia.with_annotation("foo") do Account.where(queenbee_id: 1234567890).first end will issue this query: Account Load (0.3ms) SELECT `accounts`.* FROM `accounts` WHERE `accounts`.`queenbee_id` = 1234567890 LIMIT 1 /*application:BCX,controller:project_imports,action:show*/ /*foo*/ Nesting `with_annotation` blocks will concatenate the comment strings. ## Contributing Start by bundling and creating the test database: bundle rake db:mysql:create rake db:postgresql:create Then, running `rake` will run the tests on all the database adapters (`mysql`, `mysql2`, `postgresql` and `sqlite`): rake marginalia-1.10.1/gemfiles/0000755000004100000410000000000014015440510015513 5ustar www-datawww-datamarginalia-1.10.1/gemfiles/5.0.gemfile0000644000004100000410000000023014015440510017342 0ustar www-datawww-datasource "https://rubygems.org" gem "mysql2", "~> 0.3.13" gem "pg", "~> 0.15" gem "sqlite3", "~> 1.3.6" gem "rails", "= 5.0.7.2" gemspec :path => "../" marginalia-1.10.1/gemfiles/4.2.api.gemfile0000644000004100000410000000026514015440510020123 0ustar www-datawww-datasource "https://rubygems.org" gem "mysql2", "~> 0.3.13" gem "pg", "~> 0.15" gem "sqlite3", "~> 1.3.6" gem "rails", "= 4.2.11.1" gem "rails-api", "~> 0.2.1" gemspec :path => "../" marginalia-1.10.1/gemfiles/4.2.gemfile0000644000004100000410000000023114015440510017344 0ustar www-datawww-datasource "https://rubygems.org" gem "mysql2", "~> 0.3.13" gem "pg", "~> 0.15" gem "sqlite3", "~> 1.3.6" gem "rails", "= 4.2.11.1" gemspec :path => "../" marginalia-1.10.1/gemfiles/5.1.gemfile0000644000004100000410000000023014015440510017343 0ustar www-datawww-datasource "https://rubygems.org" gem "mysql2", "~> 0.3.13" gem "pg", "~> 0.15" gem "sqlite3", "~> 1.3.6" gem "rails", "= 5.1.6.2" gemspec :path => "../" marginalia-1.10.1/gemfiles/5.2.gemfile0000644000004100000410000000023014015440510017344 0ustar www-datawww-datasource "https://rubygems.org" gem "mysql2", "~> 0.4.10" gem "pg", "~> 0.15" gem "sqlite3", "~> 1.3.6" gem "rails", "= 5.2.2.1" gemspec :path => "../" marginalia-1.10.1/init.rb0000644000004100000410000000007014015440510015205 0ustar www-datawww-datarequire 'marginalia/railtie' Marginalia::Railtie.insert marginalia-1.10.1/.gitignore0000644000004100000410000000005614015440510015711 0ustar www-datawww-data*.gem .bundle tmp marginalia_test Gemfile.lockmarginalia-1.10.1/LICENSE0000644000004100000410000000210714015440510014725 0ustar www-datawww-data# Copyright (c) 2012 37signals, LLC # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. marginalia-1.10.1/Rakefile0000755000004100000410000000276014015440510015375 0ustar www-datawww-data#!/usr/bin/env rake require "bundler/gem_tasks" task :default => ['test:all'] namespace :test do desc "test all drivers" task :all => [:mysql2, :postgresql, :sqlite] desc "test mysql driver" task :mysql do sh "DRIVER=mysql bundle exec ruby -Ilib -Itest test/*_test.rb" end desc "test mysql2 driver" task :mysql2 do sh "DRIVER=mysql2 bundle exec ruby -Ilib -Itest test/*_test.rb" end desc "test PostgreSQL driver" task :postgresql do sh "DRIVER=postgresql DB_USERNAME=postgres bundle exec ruby -Ilib -Itest test/*_test.rb" end desc "test sqlite3 driver" task :sqlite do sh "DRIVER=sqlite3 bundle exec ruby -Ilib -Itest test/*_test.rb" end end namespace :db do desc "reset all databases" task :reset => [:"mysql:reset", :"postgresql:reset"] namespace :mysql do desc "reset MySQL database" task :reset => [:drop, :create] desc "create MySQL database" task :create do sh 'mysql -u root -e "create database marginalia_test;"' end desc "drop MySQL database" task :drop do sh 'mysql -u root -e "drop database if exists marginalia_test;"' end end namespace :postgresql do desc "reset PostgreSQL database" task :reset => [:drop, :create] desc "create PostgreSQL database" task :create do sh 'createdb -U postgres marginalia_test' end desc "drop PostgreSQL database" task :drop do sh 'psql -d postgres -U postgres -c "DROP DATABASE IF EXISTS marginalia_test"' end end end marginalia-1.10.1/lib/0000755000004100000410000000000014015440510014466 5ustar www-datawww-datamarginalia-1.10.1/lib/marginalia.rb0000644000004100000410000001143414015440510017122 0ustar www-datawww-datarequire 'marginalia/railtie' require 'marginalia/comment' require 'marginalia/sidekiq_instrumentation' module Marginalia mattr_accessor :application_name module ActiveRecordInstrumentation def self.included(instrumented_class) instrumented_class.class_eval do if instrumented_class.method_defined?(:execute) alias_method :execute_without_marginalia, :execute alias_method :execute, :execute_with_marginalia end if instrumented_class.private_method_defined?(:execute_and_clear) alias_method :execute_and_clear_without_marginalia, :execute_and_clear alias_method :execute_and_clear, :execute_and_clear_with_marginalia else is_mysql2 = defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) && ActiveRecord::ConnectionAdapters::Mysql2Adapter == instrumented_class # Dont instrument exec_query on mysql2 and AR 3.2+, as it calls execute internally unless is_mysql2 && ActiveRecord::VERSION::STRING > "3.1" if instrumented_class.method_defined?(:exec_query) alias_method :exec_query_without_marginalia, :exec_query alias_method :exec_query, :exec_query_with_marginalia end end is_postgres = defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) && ActiveRecord::ConnectionAdapters::PostgreSQLAdapter == instrumented_class # Instrument exec_delete and exec_update on AR 3.2+, since they don't # call execute internally if is_postgres && ActiveRecord::VERSION::STRING > "3.1" if instrumented_class.method_defined?(:exec_delete) alias_method :exec_delete_without_marginalia, :exec_delete alias_method :exec_delete, :exec_delete_with_marginalia end if instrumented_class.method_defined?(:exec_update) alias_method :exec_update_without_marginalia, :exec_update alias_method :exec_update, :exec_update_with_marginalia end end end end end def annotate_sql(sql) Marginalia::Comment.update_adapter!(self) comment = Marginalia::Comment.construct_comment if comment.present? && !sql.include?(comment) sql = if Marginalia::Comment.prepend_comment "/*#{comment}*/ #{sql}" else "#{sql} /*#{comment}*/" end end inline_comment = Marginalia::Comment.construct_inline_comment if inline_comment.present? && !sql.include?(inline_comment) sql = if Marginalia::Comment.prepend_comment "/*#{inline_comment}*/ #{sql}" else "#{sql} /*#{inline_comment}*/" end end sql end def execute_with_marginalia(sql, *args) execute_without_marginalia(annotate_sql(sql), *args) end ruby2_keywords :execute_with_marginalia if respond_to?(:ruby2_keywords, true) def exec_query_with_marginalia(sql, *args) exec_query_without_marginalia(annotate_sql(sql), *args) end ruby2_keywords :exec_query_with_marginalia if respond_to?(:ruby2_keywords, true) if ActiveRecord::VERSION::MAJOR >= 5 def exec_query_with_marginalia(sql, *args, **options) options[:prepare] ||= false exec_query_without_marginalia(annotate_sql(sql), *args, **options) end end def exec_delete_with_marginalia(sql, *args) exec_delete_without_marginalia(annotate_sql(sql), *args) end ruby2_keywords :exec_delete_with_marginalia if respond_to?(:ruby2_keywords, true) def exec_update_with_marginalia(sql, *args) exec_update_without_marginalia(annotate_sql(sql), *args) end ruby2_keywords :exec_update_with_marginalia if respond_to?(:ruby2_keywords, true) if ActiveRecord::VERSION::MAJOR >= 5 def execute_and_clear_with_marginalia(sql, *args, &block) execute_and_clear_without_marginalia(annotate_sql(sql), *args, &block) end ruby2_keywords :execute_and_clear_with_marginalia if respond_to?(:ruby2_keywords, true) else def execute_and_clear_with_marginalia(sql, *args, &block) execute_and_clear_without_marginalia(annotate_sql(sql), *args, &block) end end end module ActionControllerInstrumentation def self.included(instrumented_class) instrumented_class.class_eval do if respond_to?(:around_action) around_action :record_query_comment else around_filter :record_query_comment end end end def record_query_comment Marginalia::Comment.update!(self) yield ensure Marginalia::Comment.clear! end end def self.with_annotation(comment, &block) Marginalia::Comment.inline_annotations.push(comment) block.call if block.present? ensure Marginalia::Comment.inline_annotations.pop end end marginalia-1.10.1/lib/marginalia/0000755000004100000410000000000014015440510016572 5ustar www-datawww-datamarginalia-1.10.1/lib/marginalia/comment.rb0000644000004100000410000001061614015440510020565 0ustar www-datawww-data# frozen_string_literal: true require 'socket' module Marginalia module Comment mattr_accessor :components, :lines_to_ignore, :prepend_comment Marginalia::Comment.components ||= [:application, :controller, :action] def self.update!(controller = nil) self.marginalia_controller = controller end def self.update_job!(job) self.marginalia_job = job end def self.update_adapter!(adapter) self.marginalia_adapter = adapter end def self.construct_comment ret = String.new self.components.each do |c| component_value = self.send(c) if component_value.present? ret << "#{c}:#{component_value}," end end ret.chop! ret = self.escape_sql_comment(ret) ret end def self.construct_inline_comment return nil if inline_annotations.none? escape_sql_comment(inline_annotations.join) end def self.escape_sql_comment(str) while str.include?('/*') || str.include?('*/') str = str.gsub('/*', '').gsub('*/', '') end str end def self.clear! self.marginalia_controller = nil end def self.clear_job! self.marginalia_job = nil end private def self.marginalia_controller=(controller) Thread.current[:marginalia_controller] = controller end def self.marginalia_controller Thread.current[:marginalia_controller] end def self.marginalia_job=(job) Thread.current[:marginalia_job] = job end def self.marginalia_job Thread.current[:marginalia_job] end def self.marginalia_adapter=(adapter) Thread.current[:marginalia_adapter] = adapter end def self.marginalia_adapter Thread.current[:marginalia_adapter] end def self.application if defined?(Rails.application) Marginalia.application_name ||= Rails.application.class.name.split("::").first else Marginalia.application_name ||= "rails" end Marginalia.application_name end def self.job marginalia_job.class.name if marginalia_job end def self.controller marginalia_controller.controller_name if marginalia_controller.respond_to? :controller_name end def self.controller_with_namespace marginalia_controller.class.name if marginalia_controller end def self.action marginalia_controller.action_name if marginalia_controller.respond_to? :action_name end def self.sidekiq_job marginalia_job["class"] if marginalia_job && marginalia_job.respond_to?(:[]) end DEFAULT_LINES_TO_IGNORE_REGEX = %r{\.rvm|/ruby/gems/|vendor/|marginalia|rbenv|monitor\.rb.*mon_synchronize} def self.line Marginalia::Comment.lines_to_ignore ||= DEFAULT_LINES_TO_IGNORE_REGEX last_line = caller.detect do |line| line !~ Marginalia::Comment.lines_to_ignore end if last_line root = if defined?(Rails) && Rails.respond_to?(:root) Rails.root.to_s elsif defined?(RAILS_ROOT) RAILS_ROOT else "" end if last_line.starts_with? root last_line = last_line[root.length..-1] end last_line end end def self.hostname @cached_hostname ||= Socket.gethostname end def self.pid Process.pid end def self.request_id if marginalia_controller.respond_to?(:request) && marginalia_controller.request.respond_to?(:uuid) marginalia_controller.request.uuid end end if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new('3.2.19') def self.socket if self.connection_config.present? self.connection_config[:socket] end end def self.db_host if self.connection_config.present? self.connection_config[:host] end end def self.database if self.connection_config.present? self.connection_config[:database] end end def self.connection_config return if marginalia_adapter.pool.nil? marginalia_adapter.pool.spec.config end end def self.inline_annotations Thread.current[:marginalia_inline_annotations] ||= [] end end end marginalia-1.10.1/lib/marginalia/sidekiq_instrumentation.rb0000644000004100000410000000107314015440510024074 0ustar www-datawww-datamodule Marginalia # Alternative to ActiveJob Instrumentation for Sidekiq. # Apt for Instrumenting Sidekiq with Rails version < 4.2. module SidekiqInstrumentation class Middleware def call(worker, msg, queue) Marginalia::Comment.update_job! msg yield ensure Marginalia::Comment.clear_job! end end def self.enable! Sidekiq.configure_server do |config| config.server_middleware do |chain| chain.add Marginalia::SidekiqInstrumentation::Middleware end end end end end marginalia-1.10.1/lib/marginalia/railtie.rb0000644000004100000410000000431514015440510020553 0ustar www-datawww-datarequire 'marginalia' module Marginalia if defined? Rails::Railtie require 'rails/railtie' class Railtie < Rails::Railtie initializer 'marginalia.insert' do ActiveSupport.on_load :active_record do Marginalia::Railtie.insert_into_active_record end ActiveSupport.on_load :action_controller do Marginalia::Railtie.insert_into_action_controller end ActiveSupport.on_load :active_job do Marginalia::Railtie.insert_into_active_job end end end end class Railtie def self.insert insert_into_active_record insert_into_action_controller insert_into_active_job end def self.insert_into_active_job if defined? ActiveJob::Base ActiveJob::Base.class_eval do around_perform do |job, block| begin Marginalia::Comment.update_job! job block.call ensure Marginalia::Comment.clear_job! end end end end end def self.insert_into_action_controller ActionController::Base.send(:include, ActionControllerInstrumentation) if defined? ActionController::API ActionController::API.send(:include, ActionControllerInstrumentation) end end def self.insert_into_active_record if defined? ActiveRecord::ConnectionAdapters::Mysql2Adapter ActiveRecord::ConnectionAdapters::Mysql2Adapter.module_eval do include Marginalia::ActiveRecordInstrumentation end end if defined? ActiveRecord::ConnectionAdapters::MysqlAdapter ActiveRecord::ConnectionAdapters::MysqlAdapter.module_eval do include Marginalia::ActiveRecordInstrumentation end end if defined? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.module_eval do include Marginalia::ActiveRecordInstrumentation end end if defined? ActiveRecord::ConnectionAdapters::SQLite3Adapter ActiveRecord::ConnectionAdapters::SQLite3Adapter.module_eval do include Marginalia::ActiveRecordInstrumentation end end end end end marginalia-1.10.1/Gemfile0000644000004100000410000000071414015440510015215 0ustar www-datawww-datasource "https://rubygems.org" gemspec version = ENV["RAILS_VERSION"] || "4.2.0" if "4.2.5" > version gem 'mysql2', '~> 0.3.13' else gem 'mysql2', '>= 0.3.13', '< 0.5' end gem 'pg', '~> 0.15' gem 'sqlite3', '~> 1.3.6' rails = case version when "master" {:github => "rails/rails"} else "~> #{version}" end gem "rails", rails if ENV["TEST_RAILS_API"] == "true" gem "rails-api", "~> 0.2.1" end if RUBY_VERSION.start_with?('2.3') gem 'mysql' end marginalia-1.10.1/.ruby-version0000644000004100000410000000000614015440510016361 0ustar www-datawww-data2.6.6 marginalia-1.10.1/marginalia.gemspec0000644000004100000410000000177614015440510017404 0ustar www-datawww-dataGem::Specification.new do |gem| gem.authors = ["Noah Lorang", "Nick Quaranto", "Taylor Weibley"] gem.email = ["arthurnn@github.com"] gem.homepage = "https://github.com/basecamp/marginalia" gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.files = `git ls-files`.split("\n") gem.test_files = `git ls-files -- {test}/*`.split("\n") gem.name = "marginalia" gem.require_paths = ["lib"] gem.version = "1.10.1" gem.license = "MIT" gem.add_runtime_dependency "actionpack", ">= 2.3" gem.add_runtime_dependency "activerecord", ">= 2.3" gem.add_development_dependency "rake" gem.add_development_dependency "mysql2" gem.add_development_dependency "pg" gem.add_development_dependency "sqlite3" gem.add_development_dependency "minitest" gem.add_development_dependency "mocha" gem.add_development_dependency "sidekiq" gem.summary = gem.description = %q{Attach comments to your ActiveRecord queries.} end