├── .gitignore ├── .rspec ├── .ruby-version ├── .travis.yml ├── Appraisals ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── docker-compose.yml ├── gemfiles ├── .bundle │ └── config ├── rails_32.gemfile ├── rails_4.gemfile ├── rails_40.gemfile ├── rails_41.gemfile ├── rails_42.gemfile ├── rails_5.gemfile ├── rails_50.gemfile └── rails_51.gemfile ├── lib ├── rails-tracer.rb └── rails │ ├── active_record │ └── tracer.rb │ ├── active_support │ └── cache │ │ ├── core_ext.rb │ │ ├── dalli_tracer.rb │ │ ├── manual_tracer.rb │ │ ├── subscriber.rb │ │ └── tracer.rb │ ├── rack │ └── tracer.rb │ ├── span_helpers.rb │ └── tracer.rb ├── rails-tracer.gemspec └── spec ├── rails ├── active_record │ └── tracer_spec.rb ├── active_suppport │ └── cache │ │ ├── dalli_tracer_spec.rb │ │ └── tracer_spec.rb ├── rack │ └── tracer_spec.rb ├── span_helpers_spec.rb └── tracer_spec.rb ├── rails_helper.rb ├── spec_helper.rb └── test_app ├── .gitignore ├── .rspec ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ └── stylesheets │ │ └── application.css ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ └── concerns │ │ └── .keep ├── helpers │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ ├── article.rb │ └── concerns │ │ └── .keep └── views │ └── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb ├── bin ├── bundle ├── rails ├── rake ├── setup └── update ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb └── secrets.yml ├── db ├── migrate │ └── 20170815215418_create_articles.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── tmp └── .keep └── vendor └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | 11 | # Appraisal 12 | *.gemfile.lock 13 | 14 | # rspec failure tracking 15 | .rspec_status 16 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | language: ruby 3 | rvm: 4 | - 2.2 5 | - 2.3 6 | - 2.4 7 | gemfile: 8 | - gemfiles/rails_32.gemfile 9 | - gemfiles/rails_40.gemfile 10 | - gemfiles/rails_41.gemfile 11 | - gemfiles/rails_42.gemfile 12 | - gemfiles/rails_50.gemfile 13 | - gemfiles/rails_51.gemfile 14 | matrix: 15 | exclude: 16 | - rvm: 2.4 17 | gemfile: gemfiles/rails_32.gemfile 18 | - rvm: 2.4 19 | gemfile: gemfiles/rails_40.gemfile 20 | - rvm: 2.4 21 | gemfile: gemfiles/rails_41.gemfile 22 | before_install: 23 | - gem install bundler -v 1.15.3 24 | services: 25 | - memcached 26 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails-32" do 2 | gem "rails", "~> 3.2.0" 3 | gem 'test-unit', '~> 3.0' 4 | end 5 | 6 | appraise "rails-40" do 7 | gem "rails", "~> 4.0.0" 8 | end 9 | 10 | appraise "rails-41" do 11 | gem "rails", "~> 4.1.0" 12 | end 13 | 14 | appraise "rails-42" do 15 | gem "rails", "~> 4.2.0" 16 | end 17 | 18 | appraise "rails-50" do 19 | gem "rails", "~> 5.0.0" 20 | end 21 | 22 | appraise "rails-51" do 23 | gem "rails", "~> 5.1.0" 24 | end 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v0.5.0 2 | 3 | * Expose global Rails::Tracer which instruments all sub-modules [#26](https://github.com/iaintshine/ruby-rails-tracer/pull/26) 4 | * Allow to disable sub-tracers [#25](https://github.com/iaintshine/ruby-rails-tracer/pull/25) 5 | * Support Evented and Timed subscribers in cache instrumentation [#24](https://github.com/iaintshine/ruby-rails-tracer/pull/24) 6 | * Test and add support for multiple Rails versions - 3.2+ [#23](https://github.com/iaintshine/ruby-rails-tracer/pull/23) 7 | * Development instructions update [#27](https://github.com/iaintshine/ruby-rails-tracer/pull/27) 8 | 9 | ## v0.4.3 10 | 11 | * Handle instrumentation exceptions and mark spans as failed [#21](https://github.com/iaintshine/ruby-rails-tracer/pull/21) 12 | 13 | ## v0.4.2 14 | 15 | * Test the gem with multiple Ruby versions 2.2+ [#20](https://github.com/iaintshine/ruby-rails-tracer/pull/20) 16 | * Set explicit Ruby version requirement in gemspec. 17 | 18 | ## v0.4.1 19 | 20 | * Use tracing matchers in tests [#19](https://github.com/iaintshine/ruby-rails-tracer/pull/19) 21 | 22 | ## v0.4.0 23 | 24 | * Start maintaining CHANGELOG.md [#18](https://github.com/iaintshine/ruby-rails-tracer/pull/18) 25 | * Auto enable tracing-logger when Dalli is auto-instrumented [#17](https://github.com/iaintshine/ruby-rails-tracer/pull/17) 26 | * Introduce Dalli and ActiveSupport::Cache::DalliStore auto-instrumentation [#9](https://github.com/iaintshine/ruby-rails-tracer/pull/9) 27 | * Introduce docker-compose with all required external dependencies. 28 | 29 | ## v0.3.0 30 | 31 | * Introduce ActiveSupport::Cache auto-instrumentation [#4](https://github.com/iaintshine/ruby-rails-tracer/pull/4) 32 | * Add ActiveRecord::Tracer tests for active span propagation 33 | 34 | ## v0.2.0 35 | 36 | * Introduce ActiveRecord auto-instrumentation [#3](https://github.com/iaintshine/ruby-rails-tracer/pull/3) 37 | * Add Rails test application to be used in specs 38 | 39 | ## v0.1.1 40 | 41 | * Replace RecordingTracer with Test::Tracer [#6](https://github.com/iaintshine/ruby-rails-tracer/pull/6) 42 | * README typo fix [#2](https://github.com/iaintshine/ruby-rails-tracer/pull/2) 43 | 44 | ## v0.1.0 45 | 46 | * Initial release 47 | * Introduced a rack middleware, to generate more informative operation names based on information supplied by ActionDispatch. [#1](https://github.com/iaintshine/ruby-rails-tracer/pull/1) 48 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in rails-tracer.gemspec 6 | gemspec 7 | 8 | gem 'pry' 9 | gem 'pry-stack_explorer' 10 | gem 'pry-byebug' 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenTracing Rails Instrumentation 2 | 3 | This gem is an attempt to introduce OpenTracing instrumentation into Rails. It's in an early stage. 4 | 5 | The following instrumentation is supported: 6 | 7 | * ActionDispatch - The library introduces a rack middleware, which is intended to be used together with `rack-tracer`, to generate more informative operation names based on information supplied by ActionDispatch. 8 | * ActiveRecord - The library hooks up into Rails, and instruments all ActiveRecord query. 9 | * ActionSupport::Cache - The library hooks up into Rails, and instruments cache events. 10 | 11 | ## Installation 12 | 13 | Add this line to your application's Gemfile: 14 | 15 | ```ruby 16 | gem 'rails-tracer' 17 | ``` 18 | 19 | And then execute: 20 | 21 | $ bundle 22 | 23 | Or install it yourself as: 24 | 25 | $ gem install rails-tracer 26 | 27 | ## Rails::Tracer 28 | 29 | The library hooks up into Rails using `ActiveSupport::Notifications`, and instruments all previously mentioned modules. 30 | To enable instrumentation, you can either use sub-tracers directly (see sections below) or global `Rails::Tracer` which 31 | will enabled all of them (except for Rack/ActionDispatch instrumentation). 32 | 33 | ### Configuration Options 34 | 35 | * `tracer: OpenTracing::Tracer` an OT compatible tracer. Default `OpenTracing.global_tracer` 36 | * `active_span: boolean` an active span provider. Default: `nil`. 37 | * `active_record: boolean` whether to enable `ActiveRecord` instrumentation. Default: `true`. 38 | * `active_support_cache: boolean` whether to enable `ActionDispatch::Cache` instrumentation. Default: `true`. 39 | * `dalli: boolean` if set to `true` you will hook up into `Dalli` low-level details. Default: `false`. 40 | * `rack: boolean` whether to enable extended `Rack` instrumentation. Default: `false`. 41 | * `middlewares: ActionDispatch::MiddlewareStack` a middlewares stack. Default: `Rails.configuration.middleware`. 42 | 43 | ### Usage 44 | 45 | ```ruby 46 | require 'rails/tracer' 47 | 48 | Rails::Tracer.instrument 49 | ``` 50 | 51 | ## ActionDispatch 52 | 53 | When you use `rack-tracer`, the generated operation name corresponds to the request's http method e.g. GET, POST etc. 54 | It's not perfect. You need to dig into the trace to understand with what url it's related. 55 | 56 | The `rails-tracer` introduces another rack middleware, which is intended to be used together with `rack-tracer`, to generate more informative operation names in the form `ControllerName#action`. 57 | 58 | ### Usage 59 | 60 | ```ruby 61 | require 'rack/tracer' 62 | require 'rails/tracer' 63 | 64 | Rails.configuration.middleware.use(Rack::Tracer) 65 | Rails.configuration.middleware.insert_after(Rack::Tracer, Rails::Rack::Tracer) 66 | ``` 67 | 68 | or simpler 69 | 70 | ```ruby 71 | Rails::Rack::Tracer.instrument 72 | ``` 73 | 74 | optionally you can pass `tracer` argument to `instrument` method. 75 | 76 | ## ActiveRecord 77 | 78 | The library hooks up into Rails using `ActiveSupport::Notifications`, and instruments all `ActiveRecord` query. 79 | 80 | ### Usage 81 | 82 | Auto-instrumentation example. 83 | 84 | ```ruby 85 | require 'rails/tracer' 86 | 87 | ActiveRecord::Tracer.instrument(tracer: OpenTracing.global_tracer, 88 | active_span: -> { OpenTracing.global_tracer.active_span }) 89 | ``` 90 | 91 | There are times when you might want to skip ActiveRecord's magic, and use connection directly. Still the library 92 | can help you with span creation. Instead of auto-instrumenting you can manually call `ActiveRecord::Tracer.start_span` as shown below. 93 | 94 | ```ruby 95 | def q(name, sql) 96 | span = ActiveRecord::Tracer.start_span(name, 97 | tracer: OpenTracing.global_tracer, 98 | active_span: -> { OpenTracing.global_tracer.active_span }, 99 | sql: sql) 100 | ActiveRecord::Base. 101 | connection. 102 | raw_connection. 103 | query(sql). 104 | each(as: :hash) 105 | ensure 106 | span&.finish 107 | end 108 | 109 | q("FirstUser", "SELECT * FROM users LIMIT 1") 110 | ``` 111 | 112 | ## ActiveSupport::Cache 113 | 114 | The library hooks up into Rails using `ActiveSupport::Notifications`, and instruments all `ActiveSupport::Cache` events. 115 | 116 | ### Usage 117 | 118 | Auto-instrumentation example. 119 | 120 | ```ruby 121 | require 'rails/tracer' 122 | 123 | ActiveSupport::Cache::Tracer.instrument(tracer: OpenTracing.global_tracer, 124 | active_span: -> { OpenTracing.global_tracer.active_span }) 125 | ``` 126 | 127 | If you use [Dalli](https://github.com/petergoldstein/dalli/) and `ActiveSupport::Cache::DalliStore` as your application's cache store, you can get low-level details about Memcached calls by setting `dalli` option to `true`. If you want to get even more details, simply require [tracing-logger](https://github.com/iaintshine/ruby-tracing-logger) and Dalli error logs will be attached to the current active span. The library will wrap current `Dalli.logger` into a `Tracing::CompositeLogger` and append additional `Tracing::Logger` with severity level set to `Logger::ERROR`. 128 | 129 | ```ruby 130 | ActiveSupport::Cache::Tracer.instrument(tracer: OpenTracing.global_tracer, 131 | active_span: -> { OpenTracing.global_tracer.active_span }, 132 | dalli: true) 133 | ``` 134 | 135 | If you want to skip the auto-instrumentation, still the library can help you with span creation and setting up proper tags. Instead of auto-instrumenting, as shown above, you can manually call `ActiveSupport::Cache::Tracer.start_span` as shown below. 136 | 137 | ```ruby 138 | def read(key) 139 | span = ActiveSupport::Cache::Tracer.start_span("InMemoryCache#read", 140 | tracer: OpenTracing.global_tracer, 141 | active_span: -> { OpenTracing.global_tracer.active_span }, 142 | key: key) 143 | result = in_memory_cache[key] 144 | span.set_tag('cache.hit', !!result) 145 | result 146 | ensure 147 | span&.finish 148 | end 149 | 150 | read("user-1") 151 | ``` 152 | 153 | ## Development 154 | 155 | After checking out the repo, install dependencies. 156 | 157 | ``` 158 | bundle install 159 | appraisal install 160 | ``` 161 | 162 | The tests depends on having memcached running locally within docker container. It means you need to install docker, and docker-compose first. 163 | Once you're done to run the containers: 164 | 165 | ``` 166 | docker-compose up -d 167 | ``` 168 | 169 | Then, to run tests for all appraisals: 170 | 171 | ``` 172 | appraisal bundle exec rspec spec 173 | ``` 174 | 175 | You can also run `bin/console` for an interactive prompt that will allow you to experiment. 176 | 177 | To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 178 | 179 | ## Contributing 180 | 181 | Bug reports and pull requests are welcome on GitHub at https://github.com/iaintshine/ruby-rails-tracer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 182 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "bundler/setup" 3 | require "bundler/gem_tasks" 4 | require "rspec/core/rake_task" 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "rails/tracer" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | memcached: 2 | image: memcached 3 | ports: 4 | - 11211:11211 5 | -------------------------------------------------------------------------------- /gemfiles/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_RETRY: "1" 3 | -------------------------------------------------------------------------------- /gemfiles/rails_32.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 3.2.0" 9 | gem "test-unit", "~> 3.0" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 5.1.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_40.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 4.0.0" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_41.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 4.1.0" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_42.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 4.2.0" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_5.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 5.1.3" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_50.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 5.0.0" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /gemfiles/rails_51.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "pry" 6 | gem "pry-stack_explorer" 7 | gem "pry-byebug" 8 | gem "rails", "~> 5.1.0" 9 | 10 | gemspec path: "../" 11 | -------------------------------------------------------------------------------- /lib/rails-tracer.rb: -------------------------------------------------------------------------------- 1 | require 'rails/tracer' 2 | -------------------------------------------------------------------------------- /lib/rails/active_record/tracer.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module Tracer 3 | DEFAULT_OPERATION_NAME = "sql.query".freeze 4 | 5 | class << self 6 | def instrument(tracer: OpenTracing.global_tracer, active_span: nil) 7 | clear_subscribers 8 | @subscriber = ::ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| 9 | ActiveRecord::Tracer.sql(tracer: tracer, active_span: active_span, args: args) 10 | end 11 | 12 | self 13 | end 14 | 15 | def disable 16 | if @subscriber 17 | ActiveSupport::Notifications.unsubscribe(@subscriber) 18 | @subscriber = nil 19 | end 20 | 21 | self 22 | end 23 | alias :clear_subscribers :disable 24 | 25 | def sql(tracer: OpenTracing.global_tracer, active_span: nil, args:) 26 | _, start, finish, _, payload = *args 27 | 28 | span = start_span(payload.fetch(:name), 29 | tracer: tracer, 30 | active_span: active_span, 31 | start_time: start, 32 | sql: payload.fetch(:sql), 33 | cached: payload.fetch(:cached, false), 34 | connection_id: payload.fetch(:connection_id)) 35 | 36 | if payload[:exception] 37 | Rails::Tracer::SpanHelpers.set_error(span, payload[:exception_object] || payload[:exception]) 38 | end 39 | 40 | span.finish(end_time: finish) 41 | end 42 | 43 | 44 | def start_span(operation_name, tracer: OpenTracing.global_tracer, active_span: nil, start_time: Time.now, **fields) 45 | connection_config = ::ActiveRecord::Base.connection_config 46 | 47 | span = tracer.start_span(operation_name || DEFAULT_OPERATION_NAME, 48 | child_of: active_span.respond_to?(:call) ? active_span.call : active_span, 49 | start_time: start_time, 50 | tags: { 51 | 'component' => 'ActiveRecord', 52 | 'span.kind' => 'client', 53 | 'db.user' => connection_config.fetch(:username, 'unknown'), 54 | 'db.instance' => connection_config.fetch(:database), 55 | 'db.vendor' => connection_config.fetch(:adapter), 56 | 'db.connection_id' => fields.fetch(:connection_id, 'unknown'), 57 | 'db.cached' => fields.fetch(:cached, false), 58 | 'db.statement' => fields.fetch(:sql), 59 | 'db.type' => 'sql' 60 | }) 61 | span 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/rails/active_support/cache/core_ext.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | module Cache 3 | class Store 4 | # See the PR https://github.com/rails/rails/pull/15943/files 5 | # In order to make the instrumentation to work we need to override the original implementation 6 | def self.instrument 7 | true 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rails/active_support/cache/dalli_tracer.rb: -------------------------------------------------------------------------------- 1 | require "method/tracer" 2 | 3 | module Dalli 4 | class Tracer 5 | class << self 6 | def instrument(tracer: OpenTracing.global_tracer, active_span: nil) 7 | ::Dalli::Server.class_eval do 8 | @tracer = tracer 9 | @active_span = active_span 10 | 11 | class << self 12 | attr_reader :tracer, :active_span 13 | end 14 | 15 | def tracer 16 | self.class.tracer 17 | end 18 | 19 | def active_span 20 | self.class.active_span 21 | end 22 | 23 | alias_method :request_without_instrumentation, :request 24 | 25 | def request(op, *args) 26 | tags = { 27 | 'component' => 'Dalli::Server', 28 | 'span.kind' => 'client', 29 | 'db.statement' => op.to_s, 30 | 'db.type' => 'memcached', 31 | 'peer.hostname' => hostname, 32 | 'peer.port' => port, 33 | 'peer.weight' => weight 34 | } 35 | Method::Tracer.trace("Dalli::Server#request", tracer: tracer, child_of: active_span, tags: tags) do 36 | request_without_instrumentation(op, *args) 37 | end 38 | end 39 | end 40 | 41 | ::Dalli::Client.class_eval do 42 | @tracer = tracer 43 | @active_span = active_span 44 | 45 | class << self 46 | attr_reader :tracer, :active_span 47 | end 48 | 49 | def tracer 50 | self.class.tracer 51 | end 52 | 53 | def active_span 54 | self.class.active_span 55 | end 56 | 57 | alias_method :perform_without_instrumentation, :perform 58 | 59 | def perform(*args) 60 | Method::Tracer.trace("Dalli::Client#perform", tracer: tracer, child_of: active_span) do 61 | perform_without_instrumentation(*args) 62 | end 63 | end 64 | end 65 | end 66 | 67 | def remove_instrumentation 68 | ::Dalli::Server.class_eval do 69 | alias_method :request, :request_without_instrumentation 70 | remove_method :request_without_instrumentation 71 | end 72 | 73 | ::Dalli::Client.class_eval do 74 | alias_method :perform, :perform_without_instrumentation 75 | remove_method :perform_without_instrumentation 76 | end 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/rails/active_support/cache/manual_tracer.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | module Cache 3 | module Tracer 4 | class << self 5 | def start_span(operation_name, tracer: OpenTracing.global_tracer, active_span: nil, start_time: Time.now, event:, **fields) 6 | span = tracer.start_span(operation_name, 7 | child_of: active_span.respond_to?(:call) ? active_span.call : active_span, 8 | start_time: start_time, 9 | tags: { 10 | 'component' => 'ActiveSupport::Cache', 11 | 'span.kind' => 'client', 12 | 'cache.key' => fields.fetch(:key, 'unknown') 13 | }) 14 | 15 | if event == 'read' 16 | span.set_tag('cache.hit', fields.fetch(:hit, false)) 17 | end 18 | 19 | span 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/rails/active_support/cache/subscriber.rb: -------------------------------------------------------------------------------- 1 | module ActiveSupport 2 | module Cache 3 | module Tracer 4 | class Subscriber 5 | attr_reader :tracer, :active_span, :event, :operation_name 6 | 7 | def initialize(tracer: OpenTracing.global_tracer, active_span: nil, event:) 8 | @tracer = tracer 9 | @active_span = active_span 10 | @event = event 11 | @operation_name = "cache.#{event}" 12 | end 13 | 14 | # For compatibility with Rails 3.2 15 | def call(*args) 16 | _, start, finish, _, payload = *args 17 | 18 | span = Tracer.start_span(operation_name, 19 | event: event, 20 | tracer: tracer, 21 | active_span: active_span, 22 | start_time: start, 23 | **payload) 24 | 25 | if payload[:exception] 26 | Rails::Tracer::SpanHelpers.set_error(span, payload[:exception_object] || payload[:exception]) 27 | end 28 | 29 | span.finish(end_time: finish) 30 | end 31 | 32 | def start(name, _, payload) 33 | span = tracer.start_span(operation_name, 34 | child_of: active_span.respond_to?(:call) ? active_span.call : active_span, 35 | tags: { 36 | 'component' => 'ActiveSupport::Cache', 37 | 'span.kind' => 'client' 38 | }) 39 | 40 | payload[:__OT_SPAN__] = span 41 | end 42 | 43 | def finish(name, _, payload) 44 | span = payload[:__OT_SPAN__] 45 | return unless span 46 | 47 | span.set_tag('cache.key', payload.fetch(:key, 'unknown')) 48 | 49 | if event == 'read' 50 | span.set_tag('cache.hit', payload.fetch(:hit, false)) 51 | end 52 | 53 | if payload[:exception] 54 | Rails::Tracer::SpanHelpers.set_error(span, payload[:exception_object] || payload[:exception]) 55 | end 56 | 57 | span.finish 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/rails/active_support/cache/tracer.rb: -------------------------------------------------------------------------------- 1 | require 'rails/active_support/cache/core_ext' 2 | require 'rails/active_support/cache/manual_tracer' 3 | require 'rails/active_support/cache/subscriber' 4 | 5 | module ActiveSupport 6 | module Cache 7 | module Tracer 8 | class << self 9 | def instrument(tracer: OpenTracing.global_tracer, active_span: nil, dalli: false) 10 | clear_subscribers 11 | events = %w(read write generate delete clear) 12 | @subscribers = events.map do |event| 13 | subscriber = ActiveSupport::Cache::Tracer::Subscriber.new(tracer: tracer, 14 | active_span: active_span, 15 | event: event) 16 | ActiveSupport::Notifications.subscribe("cache_#{event}.active_support", subscriber) 17 | end 18 | 19 | instrument_dalli(tracer: tracer, active_span: active_span) if dalli 20 | 21 | self 22 | end 23 | 24 | def disable 25 | if @subscribers 26 | @subscribers.each { |subscriber| ActiveSupport::Notifications.unsubscribe(subscriber) } 27 | @subscribers.clear 28 | end 29 | 30 | self 31 | end 32 | 33 | alias :clear_subscribers :disable 34 | 35 | def instrument_dalli(tracer:, active_span:) 36 | return unless defined?(ActiveSupport::Cache::DalliStore) 37 | require 'rails/active_support/cache/dalli_tracer' 38 | 39 | Dalli::Tracer.instrument(tracer: tracer, active_span: active_span) 40 | instrument_dalli_logger(active_span: active_span) 41 | end 42 | 43 | def instrument_dalli_logger(active_span:, level: Logger::ERROR) 44 | return unless defined?(Tracing::Logger) 45 | return unless active_span 46 | return if [Tracing::Logger, Tracing::CompositeLogger].any? { |t| Dalli.logger.is_a?(t) } 47 | 48 | tracing_logger = Tracing::Logger.new(active_span: active_span, level: level) 49 | loggers = [tracing_logger, Dalli.logger].compact 50 | Dalli.logger = Tracing::CompositeLogger.new(*loggers) 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/rails/rack/tracer.rb: -------------------------------------------------------------------------------- 1 | module Rails 2 | module Rack 3 | class Tracer 4 | class << self 5 | def instrument(tracer: OpenTracing.global_tracer, middlewares: Rails.configuration.middleware) 6 | return unless defined?(::Rack::Tracer) 7 | @owns_all_middlewares = false 8 | unless middlewares.include?(::Rack::Tracer) 9 | middlewares.use(::Rack::Tracer, tracer: tracer) 10 | @owns_all_middlewares = true 11 | end 12 | middlewares.insert_after(::Rack::Tracer, Rails::Rack::Tracer) 13 | end 14 | 15 | def disable(middlewares: Rails.configuration.middleware) 16 | middlewares.delete(Rails::Rack::Tracer) 17 | if @owns_all_middlewares 18 | middlewares.delete(::Rack::Tracer) 19 | @owns_all_middlewares = false 20 | end 21 | rescue 22 | end 23 | end 24 | 25 | def initialize(app) 26 | @app = app 27 | end 28 | 29 | def call(env) 30 | @app.call(env) 31 | ensure 32 | enhance_rack_span(env) 33 | end 34 | 35 | private 36 | 37 | def enhance_rack_span(env) 38 | span = extract_span(env) 39 | if span && route_found?(env) 40 | span.operation_name = operation_name(env) 41 | end 42 | end 43 | 44 | def extract_span(env) 45 | env['rack.span'] 46 | end 47 | 48 | def route_found?(env) 49 | env["action_dispatch.request.path_parameters"] 50 | end 51 | 52 | def operation_name(env) 53 | path_parameters = env["action_dispatch.request.path_parameters"] 54 | action_controller = env["action_controller.instance"] 55 | controller = action_controller ? action_controller.class.to_s : path_parameters[:controller] 56 | action = path_parameters[:action] 57 | "#{controller}##{action}" 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/rails/span_helpers.rb: -------------------------------------------------------------------------------- 1 | module Rails 2 | module Tracer 3 | module SpanHelpers 4 | class << self 5 | def set_error(span, exception) 6 | span.set_tag('error', true) 7 | 8 | case exception 9 | when Array 10 | exception_class, exception_message = exception 11 | span.log(event: 'error', :'error.kind' => exception_class, message: exception_message) 12 | when Exception 13 | span.log(event: 'error', :'error.object' => exception) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/rails/tracer.rb: -------------------------------------------------------------------------------- 1 | require "rails/span_helpers" 2 | require "rails/rack/tracer" 3 | require "rails/active_record/tracer" 4 | require "rails/active_support/cache/tracer" 5 | 6 | module Rails 7 | module Tracer 8 | class << self 9 | def instrument(tracer: OpenTracing.global_tracer, active_span: nil, 10 | rack: false, middlewares: Rails.configuration.middleware, 11 | active_record: true, 12 | active_support_cache: true, dalli: false) 13 | Rails::Rack::Tracer.instrument(tracer: tracer, middlewares: middlewares) if rack 14 | ActiveRecord::Tracer.instrument(tracer: tracer, active_span: active_span) if active_record 15 | ActiveSupport::Cache::Tracer.instrument(tracer: tracer, active_span: active_span, dalli: dalli) if active_support_cache 16 | end 17 | 18 | def disable 19 | ActiveRecord::Tracer.disable 20 | ActiveSupport::Cache::Tracer.disable 21 | Rails::Rack::Tracer.disable 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /rails-tracer.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "rails-tracer" 7 | spec.version = "0.5.0" 8 | spec.authors = ["iaintshine"] 9 | spec.email = ["bodziomista@gmail.com"] 10 | 11 | spec.summary = %q{Rack OpenTracing middleware enhanced for Rails} 12 | spec.description = %q{} 13 | spec.homepage = "https://github.com/iaintshine/ruby-rails-tracer" 14 | spec.license = "Apache-2.0" 15 | 16 | spec.required_ruby_version = ">= 2.2.0" 17 | 18 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 19 | f.match(%r{^(test|spec|features)/}) 20 | end 21 | spec.bindir = "exe" 22 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 23 | spec.require_paths = ["lib"] 24 | 25 | spec.add_dependency 'opentracing', '~> 0.3.1' 26 | spec.add_dependency "rack-tracer", "~> 0.3.0" 27 | spec.add_dependency "method-tracer", "~> 1.1" 28 | 29 | spec.add_development_dependency "sqlite3" 30 | spec.add_development_dependency "puma", "~> 3.7" 31 | spec.add_development_dependency "rspec-rails", "~> 3.6" 32 | spec.add_development_dependency "database_cleaner", "~> 1.6" 33 | spec.add_development_dependency "dalli", "~> 2.7.6" 34 | spec.add_development_dependency "tracing-logger", "~> 1.0" 35 | spec.add_development_dependency "test-tracer", "~> 1.0", ">= 1.2.1" 36 | spec.add_development_dependency "tracing-matchers", "~> 1.0", ">= 1.3.0" 37 | spec.add_development_dependency "spanmanager", "~> 0.3.0" 38 | spec.add_development_dependency "bundler", "~> 1.15" 39 | spec.add_development_dependency "rake", "~> 10.0" 40 | spec.add_development_dependency "rspec", "~> 3.0" 41 | spec.add_development_dependency "simplecov" 42 | spec.add_development_dependency "simplecov-console" 43 | spec.add_development_dependency "appraisal" 44 | end 45 | -------------------------------------------------------------------------------- /spec/rails/active_record/tracer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | RSpec.describe ActiveRecord::Tracer do 4 | let(:tracer) { Test::Tracer.new } 5 | 6 | describe "active span propagation" do 7 | let(:root_span) { tracer.start_span("root") } 8 | 9 | before do 10 | ActiveRecord::Tracer.instrument(tracer: tracer, active_span: -> { root_span }) 11 | Article.first 12 | end 13 | 14 | after do 15 | ActiveRecord::Tracer.disable 16 | end 17 | 18 | it "creates the new span with active span trace_id" do 19 | expect(tracer).to have_traces(1) 20 | end 21 | 22 | it "creates the new span with active span as a parent" do 23 | cache_span = tracer.finished_spans.last 24 | expect(cache_span).to be_child_of(root_span) 25 | end 26 | end 27 | 28 | describe "auto-instrumentation" do 29 | before do 30 | ActiveRecord::Tracer.instrument(tracer: tracer) 31 | Article.count 32 | end 33 | 34 | after do 35 | ActiveRecord::Tracer.disable 36 | end 37 | 38 | it "creates a new span" do 39 | expect(tracer).to have_spans 40 | end 41 | 42 | it "sets operation_name to event's name" do 43 | expect(tracer).to have_span("sql.query") 44 | end 45 | 46 | it "sets standard OT tags" do 47 | [ 48 | ['component', 'ActiveRecord'], 49 | ['span.kind', 'client'] 50 | ].each do |key, value| 51 | expect(tracer).to have_span.with_tag(key, value) 52 | end 53 | end 54 | 55 | it "sets database specific OT tags" do 56 | sql = /SELECT COUNT\(\*\) FROM \"articles\"/ 57 | [ 58 | ['db.type', 'sql'], 59 | ['db.vendor', 'sqlite3'], 60 | ['db.statement', sql], 61 | ].each do |key, value| 62 | expect(tracer).to have_span.with_tag(key, value) 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/rails/active_suppport/cache/dalli_tracer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "dalli" 3 | require "active_support/cache/dalli_store" 4 | require "rails/active_support/cache/dalli_tracer" 5 | 6 | RSpec.describe Dalli::Tracer do 7 | let(:tracer) { Test::Tracer.new } 8 | let(:root_span) { tracer.start_span("root") } 9 | 10 | let(:hostname) { "localhost" } 11 | let(:port) { 11211 } 12 | let(:servers) { ["#{hostname}:#{port}"] } 13 | let(:cache) { ActiveSupport::Cache::DalliStore.new(servers) } 14 | let(:test_key) { "test-key" } 15 | 16 | describe "active span propagation" do 17 | let(:root_span) { tracer.start_span("root") } 18 | 19 | before do 20 | Dalli::Tracer.instrument(tracer: tracer, active_span: -> { root_span }) 21 | cache.read(test_key) 22 | root_span.finish 23 | end 24 | 25 | after do 26 | Dalli::Tracer.remove_instrumentation 27 | end 28 | 29 | it "creates spans for each part of the chain" do 30 | expect(tracer).to have_spans(3) 31 | end 32 | 33 | it "all spans contains the same trace_id" do 34 | expect(tracer).to have_traces(1) 35 | end 36 | 37 | it "propagates parent child relationship properly" do 38 | server_span = tracer.finished_spans[0] 39 | client_span = tracer.finished_spans[1] 40 | expect(server_span).to be_child_of(root_span) 41 | expect(client_span).to be_child_of(root_span) 42 | end 43 | end 44 | 45 | describe "auto-instrumentation" do 46 | before do 47 | Dalli::Tracer.instrument(tracer: tracer) 48 | cache.read(test_key) 49 | end 50 | 51 | after do 52 | Dalli::Tracer.remove_instrumentation 53 | end 54 | 55 | it "creates a new span" do 56 | expect(tracer).to have_spans.finished 57 | end 58 | 59 | it "creates 2 spans, one for a server, and second for client" do 60 | expect(tracer).to have_spans(2).finished 61 | end 62 | 63 | describe "server span" do 64 | it "sets operation_name to ClassName#method" do 65 | expect(tracer).to have_span("Dalli::Server#request") 66 | end 67 | 68 | it "sets standard OT tags" do 69 | [ 70 | ['component', 'Dalli::Server'], 71 | ['span.kind', 'client'] 72 | ].each do |key, value| 73 | expect(tracer).to have_span.with_tag(key, value) 74 | end 75 | end 76 | 77 | it "sets cache specific OT tags" do 78 | [ 79 | ['db.statement', 'get'], 80 | ['db.type', 'memcached'], 81 | ['peer.hostname', hostname], 82 | ['peer.port', port], 83 | ['peer.weight', 1], 84 | ].each do |key, value| 85 | expect(tracer).to have_span.with_tag(key, value) 86 | end 87 | end 88 | end 89 | 90 | describe "client span" do 91 | it "sets operation_name to ClassName#method" do 92 | expect(tracer).to have_span("Dalli::Client#perform") 93 | end 94 | end 95 | end 96 | end 97 | -------------------------------------------------------------------------------- /spec/rails/active_suppport/cache/tracer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "spanmanager" 3 | 4 | RSpec.describe ActiveSupport::Cache::Tracer do 5 | let(:tracer) { Test::Tracer.new } 6 | let(:test_key) { "test-key" } 7 | 8 | describe "active span propagation" do 9 | let(:root_span) { tracer.start_span("root") } 10 | 11 | before do 12 | ActiveSupport::Cache::Tracer.instrument(tracer: tracer, active_span: -> { root_span }) 13 | Rails.cache.read(test_key) 14 | end 15 | 16 | after do 17 | ActiveSupport::Cache::Tracer.disable 18 | Rails.cache.clear 19 | end 20 | 21 | it "creates the new span with active span trace_id" do 22 | expect(tracer).to have_traces(1) 23 | end 24 | 25 | it "creates the new span with active span as a parent" do 26 | cache_span = tracer.finished_spans.last 27 | expect(cache_span).to be_child_of(root_span) 28 | end 29 | end 30 | 31 | describe "nested context" do 32 | let(:test_tracer) { Test::Tracer.new } 33 | let(:tracer) { SpanManager::Tracer.new(test_tracer) } 34 | 35 | before do 36 | ActiveSupport::Cache::Tracer.instrument(tracer: tracer, active_span: -> { tracer.active_span }) 37 | 38 | root = tracer.start_span("root") 39 | Rails.cache.fetch(test_key) do 40 | tracer.start_span("nested").finish 41 | "test-value" 42 | end 43 | root.finish 44 | end 45 | 46 | after do 47 | ActiveSupport::Cache::Tracer.disable 48 | Rails.cache.clear 49 | end 50 | 51 | it "creates a single trace" do 52 | expect(test_tracer).to have_traces(1) 53 | end 54 | 55 | it "creates 5 spans" do 56 | expect(test_tracer).to have_spans(5) 57 | end 58 | 59 | if Gem::Version.new(Rails.version) >= Gem::Version.new("4.0.0") 60 | it "creates nested spans tree" do 61 | expect(test_tracer).to have_span("root") 62 | expect(test_tracer).to have_span("cache.read").with_parent("root") 63 | expect(test_tracer).to have_span("cache.generate").with_parent("root") 64 | expect(test_tracer).to have_span("nested").with_parent("cache.generate") 65 | expect(test_tracer).to have_span("cache.write").with_parent("root") 66 | end 67 | else 68 | it "creates flat spans tree" do 69 | expect(test_tracer).to have_span("root") 70 | expect(test_tracer).to have_span("cache.read").with_parent("root") 71 | expect(test_tracer).to have_span("cache.generate").with_parent("root") 72 | expect(test_tracer).to have_span("nested").with_parent("root") 73 | expect(test_tracer).to have_span("cache.write").with_parent("root") 74 | end 75 | end 76 | end 77 | 78 | describe "auto-instrumentation" do 79 | before do 80 | ActiveSupport::Cache::Tracer.instrument(tracer: tracer) 81 | Rails.cache.read(test_key) 82 | end 83 | 84 | after do 85 | ActiveSupport::Cache::Tracer.disable 86 | Rails.cache.clear 87 | end 88 | 89 | it "creates a new span" do 90 | expect(tracer).to have_spans 91 | end 92 | 93 | it "sets operation_name to event's name" do 94 | expect(tracer).to have_span("cache.read") 95 | end 96 | 97 | it "sets standard OT tags" do 98 | [ 99 | ['component', 'ActiveSupport::Cache'], 100 | ['span.kind', 'client'] 101 | ].each do |key, value| 102 | expect(tracer).to have_span.with_tag(key, value) 103 | end 104 | end 105 | 106 | it "sets cache specific OT tags" do 107 | [ 108 | ['cache.key', test_key], 109 | ].each do |key, value| 110 | expect(tracer).to have_span.with_tag(key, value) 111 | end 112 | end 113 | 114 | context "cache entry not found during read" do 115 | it "sets cache.hit tag to false" do 116 | Rails.cache.read(test_key) 117 | expect(tracer).to have_span.with_tag('cache.hit', false) 118 | end 119 | end 120 | 121 | context "cache entry found during read" do 122 | it "sets cache.hit tag to true" do 123 | Rails.cache.write(test_key, "a value") 124 | Rails.cache.read(test_key) 125 | expect(tracer).to have_span.with_tag('cache.hit', false) 126 | end 127 | end 128 | 129 | context "exception thrown during cache operation" do 130 | it "sets error on span" do 131 | exception = Timeout::Error.new("couldn't reach cache server") 132 | expect { Rails.cache.fetch(test_key) { raise exception } }.to raise_error(exception) 133 | if Gem::Version.new(Rails.version) >= Gem::Version.new("5.0.0") 134 | # exception_object was introduced in Rails version 5+ 135 | expect(tracer).to have_span 136 | .with_tag('error', true) 137 | .with_log(event: 'error', :'error.object' => exception) 138 | else 139 | expect(tracer).to have_span 140 | .with_tag('error', true) 141 | .with_log(event: 'error', :'error.kind' => "Timeout::Error", message: "couldn't reach cache server") 142 | end 143 | end 144 | end 145 | end 146 | 147 | describe "dalli store auto-instrumentation option" do 148 | def instrument(dalli:) 149 | ActiveSupport::Cache::Tracer.instrument(tracer: tracer, active_span: -> { }, dalli: dalli).disable 150 | end 151 | 152 | context "Dalli wasn't required" do 153 | context "dalli: false" do 154 | let(:enabled) { false } 155 | 156 | it "doesn't enable dalli auto-instrumentation" do 157 | expect(Dalli::Tracer).not_to receive(:instrument) 158 | instrument(dalli: enabled) 159 | end 160 | end 161 | 162 | context "dalli: true" do 163 | let(:enabled) { true } 164 | 165 | before do 166 | HiddenDalliStore = ActiveSupport::Cache::DalliStore 167 | ActiveSupport::Cache.send(:remove_const, "DalliStore") 168 | end 169 | 170 | after do 171 | ActiveSupport::Cache::DalliStore = HiddenDalliStore 172 | end 173 | 174 | it "doesn't enable dalli auto-instrumentation" do 175 | expect(Dalli::Tracer).not_to receive(:instrument) 176 | instrument(dalli: enabled) 177 | end 178 | end 179 | end 180 | 181 | context "Dalli was required" do 182 | context "dalli: false" do 183 | let(:enabled) { false } 184 | 185 | it "doesn't enable dalli auto-instrumentation" do 186 | expect(Dalli::Tracer).not_to receive(:instrument) 187 | instrument(dalli: enabled) 188 | end 189 | end 190 | 191 | context "dalli: true" do 192 | let(:enabled) { true } 193 | 194 | it "enables dalli auto-instrumentation" do 195 | expect(Dalli::Tracer).to receive(:instrument) 196 | instrument(dalli: enabled) 197 | end 198 | 199 | describe "Dalli tracing logger" do 200 | context "logger already instrumented" do 201 | it "keeps current logger intact" do 202 | logger = Tracing::Logger.new(active_span: -> { }) 203 | Dalli.logger = logger 204 | instrument(dalli: enabled) 205 | expect(Dalli.logger).to eq(logger) 206 | 207 | logger = Tracing::CompositeLogger.new(logger) 208 | Dalli.logger = logger 209 | instrument(dalli: enabled) 210 | expect(Dalli.logger).to eq(logger) 211 | end 212 | end 213 | 214 | context "logger wasn't instrumented" do 215 | let(:stdout_logger) { Logger.new(STDOUT) } 216 | 217 | before do 218 | Dalli.logger = stdout_logger 219 | instrument(dalli: enabled) 220 | end 221 | 222 | it "creates composite logger" do 223 | expect(Dalli.logger).to be_instance_of(Tracing::CompositeLogger) 224 | end 225 | 226 | it "wraps existing logger" do 227 | expect(Dalli.logger.destinations).to include(stdout_logger) 228 | end 229 | 230 | it "wraps tracing logger with ERROR severity level" do 231 | logger = Dalli.logger.destinations.find { |d| d.is_a?(Tracing::Logger) } 232 | expect(logger).not_to be_nil 233 | expect(logger.level).to eq(Logger::ERROR) 234 | end 235 | end 236 | end 237 | end 238 | end 239 | end 240 | end 241 | -------------------------------------------------------------------------------- /spec/rails/rack/tracer_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | require "rack/tracer" 3 | require "rack/mock" 4 | 5 | RSpec.describe Rails::Rack::Tracer do 6 | let(:tracer) { Test::Tracer.new } 7 | let(:env) { Rack::MockRequest.env_for('/api/user', method: 'GET') } 8 | let(:response) { [200, {'Content-Type' => 'application/json'}, ['{"users": []}']] } 9 | 10 | describe "Class Methods" do 11 | subject { described_class } 12 | 13 | it { should respond_to :instrument } 14 | it { should respond_to :disable } 15 | end 16 | 17 | describe :instrument do 18 | let(:stack) { ActionDispatch::MiddlewareStack.new } 19 | 20 | context "Rack::Tracer already present" do 21 | before do 22 | stack.use(::Rack::Tracer) 23 | Rails::Rack::Tracer.instrument(middlewares: stack) 24 | end 25 | 26 | it "does not insert additional Rack::Tracer" do 27 | rack_tracers = stack.middlewares.select { |m| m == ::Rack::Tracer } 28 | expect(rack_tracers.size).to eq(1) 29 | end 30 | 31 | it "inserts the tracer after Rack::Tracer" do 32 | rack_tracer = stack.middlewares.find_index(::Rack::Tracer) 33 | rails_tracer = stack.middlewares.find_index(Rails::Rack::Tracer) 34 | 35 | expect(rack_tracer).not_to be_nil 36 | expect(rails_tracer).not_to be_nil 37 | 38 | expect(rails_tracer).to be > rack_tracer 39 | end 40 | end 41 | 42 | context "Rack::Tracer wasn't present" do 43 | before do 44 | Rails::Rack::Tracer.instrument(middlewares: stack) 45 | end 46 | 47 | it "inserts additional Rack::Tracer" do 48 | rack_tracers = stack.middlewares.select { |m| m == ::Rack::Tracer } 49 | expect(rack_tracers.size).to eq(1) 50 | end 51 | 52 | it "inserts the tracer after Rack::Tracer" do 53 | rack_tracer = stack.middlewares.find_index(::Rack::Tracer) 54 | rails_tracer = stack.middlewares.find_index(Rails::Rack::Tracer) 55 | 56 | expect(rack_tracer).not_to be_nil 57 | expect(rails_tracer).not_to be_nil 58 | 59 | expect(rails_tracer).to be > rack_tracer 60 | end 61 | end 62 | end 63 | 64 | describe :disable do 65 | let(:stack) { ActionDispatch::MiddlewareStack.new } 66 | 67 | context "Rack::Tracer already present" do 68 | before do 69 | stack.use(::Rack::Tracer) 70 | Rails::Rack::Tracer.instrument(middlewares: stack) 71 | Rails::Rack::Tracer.disable(middlewares: stack) 72 | end 73 | 74 | it "leaves Rack::Tracer intact" do 75 | expect(stack.middlewares).to include(::Rack::Tracer) 76 | end 77 | 78 | it "removes rails tracer" do 79 | expect(stack.middlewares).not_to include(Rails::Rack::Tracer) 80 | end 81 | end 82 | 83 | context "Rack::Tracer wasn't present" do 84 | before do 85 | Rails::Rack::Tracer.instrument(middlewares: stack) 86 | Rails::Rack::Tracer.disable(middlewares: stack) 87 | end 88 | 89 | it "removes Rack::Tracer" do 90 | expect(stack.middlewares).not_to include(::Rack::Tracer) 91 | end 92 | 93 | it "removes rails tracer" do 94 | expect(stack.middlewares).not_to include(Rails::Rack::Tracer) 95 | end 96 | end 97 | end 98 | 99 | def respond_with(&app) 100 | enhance_middleware = Rails::Rack::Tracer.new(app) 101 | middleware = Rack::Tracer.new(enhance_middleware, tracer: tracer) 102 | middleware.call(env) 103 | end 104 | 105 | context 'when path was not found' do 106 | it 'leaves the operation_name as it was' do 107 | respond_with { response } 108 | 109 | expect(tracer).to have_spans(1) 110 | expect(tracer).to have_span('GET').finished 111 | end 112 | end 113 | 114 | context 'when path was found' do 115 | it 'enhances the operation_name to Controller#action' do 116 | respond_with do |env| 117 | env["action_dispatch.request.path_parameters"] = {controller: "/api/users", action: "index"} 118 | env["action_controller.instance"] = Api::UsersController.new 119 | response 120 | end 121 | 122 | expect(tracer).to have_spans(1) 123 | expect(tracer).to have_span('Api::UsersController#index').finished 124 | end 125 | end 126 | 127 | module Api 128 | class UsersController 129 | end 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /spec/rails/span_helpers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Rails::Tracer::SpanHelpers do 4 | describe "Class Methods" do 5 | it { should respond_to :set_error } 6 | end 7 | 8 | describe :set_error do 9 | let(:tracer) { Test::Tracer.new } 10 | let(:span) { tracer.start_span("failed span") } 11 | 12 | context "exception object passed" do 13 | let(:exception) { Exception.new("test exception") } 14 | 15 | before do 16 | Rails::Tracer::SpanHelpers.set_error(span, exception) 17 | end 18 | 19 | it 'marks the span as failed' do 20 | expect(span).to have_tag('error', true) 21 | end 22 | 23 | it 'logs the error' do 24 | expect(span).to have_log(event: 'error', :'error.object' => exception) 25 | end 26 | end 27 | 28 | context "array passed" do 29 | let(:exception) { ["Exception", "test exception"] } 30 | 31 | before do 32 | Rails::Tracer::SpanHelpers.set_error(span, exception) 33 | end 34 | 35 | it 'marks the span as failed' do 36 | expect(span).to have_tag('error', true) 37 | end 38 | 39 | it 'logs the error' do 40 | expect(span).to have_log(event: 'error', :'error.kind' => "Exception", message: "test exception") 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/rails/tracer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Rails::Tracer do 4 | describe "Class Methods" do 5 | it { should respond_to :instrument } 6 | it { should respond_to :disable } 7 | end 8 | 9 | describe :instrument do 10 | let(:tracer) { Test::Tracer.new } 11 | 12 | before do 13 | OpenTracing.global_tracer = tracer 14 | end 15 | 16 | context "default arguments" do 17 | it "doesn't instrument Rack" do 18 | expect(Rails::Rack::Tracer).not_to receive(:instrument) 19 | 20 | Rails::Tracer.instrument 21 | end 22 | 23 | it "instruments ActiveRecord" do 24 | expect(ActiveRecord::Tracer).to receive(:instrument) 25 | .with(tracer: tracer, 26 | active_span: nil) 27 | 28 | Rails::Tracer.instrument 29 | end 30 | 31 | it "instruments ActiveSupport::Cache" do 32 | expect(ActiveSupport::Cache::Tracer).to receive(:instrument) 33 | .with(tracer: tracer, 34 | active_span: nil, 35 | dalli: false) 36 | 37 | Rails::Tracer.instrument 38 | end 39 | end 40 | 41 | context "sub-tracers explicitly disabled" do 42 | it "doesn't instrument Rack" do 43 | expect(Rails::Rack::Tracer).not_to receive(:instrument) 44 | 45 | Rails::Tracer.instrument(rack: false) 46 | end 47 | 48 | it "doesn't instrument ActiveRecord" do 49 | expect(ActiveRecord::Tracer).not_to receive(:instrument) 50 | 51 | Rails::Tracer.instrument(active_record: false) 52 | end 53 | 54 | it "doesn't instrument ActiveSupport::Cache" do 55 | expect(ActiveSupport::Cache::Tracer).not_to receive(:instrument) 56 | 57 | Rails::Tracer.instrument(active_support_cache: false) 58 | end 59 | end 60 | 61 | context "sub-tracers explicitly enabled" do 62 | it "instruments Rack" do 63 | expect(Rails::Rack::Tracer).to receive(:instrument) 64 | .with(tracer: tracer, 65 | middlewares: Rails.configuration.middleware) 66 | 67 | Rails::Tracer.instrument(rack: true) 68 | end 69 | end 70 | 71 | describe "passing arguments to sub-tracers" do 72 | it "pass middlewares argument to rack tracer" do 73 | stack = double 74 | expect(Rails::Rack::Tracer).to receive(:instrument) 75 | .with(tracer: tracer, 76 | middlewares: stack) 77 | 78 | Rails::Tracer.instrument(rack: true, middlewares: stack) 79 | end 80 | 81 | it "pass dalli argument to cache tracer" do 82 | [true, false].each do |enabled| 83 | expect(ActiveSupport::Cache::Tracer).to receive(:instrument) 84 | .with(tracer: tracer, 85 | active_span: nil, 86 | dalli: enabled) 87 | 88 | Rails::Tracer.instrument(dalli: enabled) 89 | end 90 | end 91 | end 92 | end 93 | 94 | describe :disable do 95 | it "disables all submodules" do 96 | [ 97 | Rails::Rack::Tracer, 98 | ActiveRecord::Tracer, 99 | ActiveSupport::Cache::Tracer 100 | ].each do |tracer_class| 101 | expect(tracer_class).to receive(:disable) 102 | end 103 | 104 | Rails::Tracer.disable 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require "test_app/config/environment" 3 | # Prevent database truncation if the environment is production 4 | abort("The Rails environment is running in production mode!") if Rails.env.production? 5 | require 'rspec/rails' 6 | require 'database_cleaner' 7 | 8 | # Checks for pending migration and applies them before tests are run. 9 | # ActiveRecord::Migration.maintain_test_schema! 10 | system("cd spec/test_app; bin/rake db:create db:migrate db:test:prepare") 11 | 12 | RSpec.configure do |config| 13 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 14 | 15 | config.use_transactional_fixtures = true 16 | 17 | config.infer_spec_type_from_file_location! 18 | config.filter_rails_from_backtrace! 19 | end 20 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | require "simplecov" 3 | require "simplecov-console" 4 | 5 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ 6 | SimpleCov::Formatter::HTMLFormatter, 7 | SimpleCov::Formatter::Console, 8 | ]) 9 | SimpleCov.start 10 | 11 | RSpec.configure do |config| 12 | # Enable flags like --only-failures and --next-failure 13 | config.example_status_persistence_file_path = ".rspec_status" 14 | 15 | # Disable RSpec exposing methods globally on `Module` and `main` 16 | config.disable_monkey_patching! 17 | 18 | config.expect_with :rspec do |c| 19 | c.syntax = :expect 20 | end 21 | end 22 | 23 | 24 | require "rails_helper" 25 | require "test/tracer" 26 | require "tracing/matchers" 27 | require "tracing/logger" 28 | require "rails-tracer" 29 | -------------------------------------------------------------------------------- /spec/test_app/.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle 2 | 3 | # Ignore the default SQLite database. 4 | /db/*.sqlite3 5 | /db/*.sqlite3-journal 6 | 7 | # Ignore all logfiles and tempfiles. 8 | /log/* 9 | /tmp/* 10 | !/log/.keep 11 | !/tmp/.keep 12 | -------------------------------------------------------------------------------- /spec/test_app/.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /spec/test_app/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/test_app/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /spec/test_app/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/test_app/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/test_app/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | if Gem::Version.new(Rails.version) >= Gem::Version.new("5.0.0") 2 | module ApplicationCable 3 | class Channel < ActionCable::Channel::Base 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/test_app/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | if Gem::Version.new(Rails.version) >= Gem::Version.new("5.0.0") 2 | module ApplicationCable 3 | class Connection < ActionCable::Connection::Base 4 | end 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/test_app/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /spec/test_app/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/test_app/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/test_app/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | if Gem::Version.new(Rails.version) >= Gem::Version.new("4.2.0") 2 | class ApplicationJob < ActiveJob::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/test_app/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /spec/test_app/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /spec/test_app/app/models/article.rb: -------------------------------------------------------------------------------- 1 | class Article < ApplicationRecord 2 | end 3 | -------------------------------------------------------------------------------- /spec/test_app/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/test_app/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TestApp 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all' %> 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/test_app/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/test_app/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /spec/test_app/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/test_app/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /spec/test_app/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /spec/test_app/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | 22 | # puts "\n== Copying sample files ==" 23 | # unless File.exist?('config/database.yml') 24 | # cp 'config/database.yml.sample', 'config/database.yml' 25 | # end 26 | 27 | puts "\n== Preparing database ==" 28 | system! 'bin/rails db:setup' 29 | 30 | puts "\n== Removing old logs and tempfiles ==" 31 | system! 'bin/rails log:clear tmp:clear' 32 | 33 | puts "\n== Restarting application server ==" 34 | system! 'bin/rails restart' 35 | end 36 | -------------------------------------------------------------------------------- /spec/test_app/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /spec/test_app/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /spec/test_app/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_record/railtie" 7 | require "action_controller/railtie" 8 | require "action_mailer/railtie" 9 | require "action_view/railtie" 10 | 11 | if Gem::Version.new(Rails.version) >= Gem::Version.new("4.2.0") 12 | require "active_job/railtie" 13 | end 14 | 15 | 16 | if Gem::Version.new(Rails.version) >= Gem::Version.new("5.0.0") 17 | require "action_cable/engine" 18 | end 19 | # require "sprockets/railtie" 20 | # require "rails/test_unit/railtie" 21 | 22 | # Require the gems listed in Gemfile, including any gems 23 | # you've limited to :test, :development, or :production. 24 | Bundler.require(*Rails.groups) 25 | 26 | module TestApp 27 | class Application < Rails::Application 28 | # Initialize configuration defaults for originally generated Rails version. 29 | if Gem::Version.new(Rails.version) >= Gem::Version.new("5.1.0") 30 | config.load_defaults Rails.version[/(\d\.\d)/, 0] 31 | end 32 | 33 | # Settings in config/environments/* take precedence over those specified here. 34 | # Application configuration should go into files in config/initializers 35 | # -- all .rb files in that directory are automatically loaded. 36 | 37 | # Don't generate system test files. 38 | config.generators.system_tests = nil 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/test_app/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /spec/test_app/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: test_app_production 11 | -------------------------------------------------------------------------------- /spec/test_app/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /spec/test_app/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Test::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | config.action_controller.perform_caching = false 17 | 18 | config.cache_store = :null_store 19 | 20 | # Don't care if the mailer can't send. 21 | config.action_mailer.raise_delivery_errors = false 22 | 23 | config.action_mailer.perform_caching = false 24 | 25 | # Print deprecation notices to the Rails logger. 26 | config.active_support.deprecation = :log 27 | 28 | # Raise an error on page load if there are pending migrations. 29 | config.active_record.migration_error = :page_load 30 | 31 | 32 | # Raises error for missing translations 33 | # config.action_view.raise_on_missing_translations = true 34 | 35 | # Use an evented file watcher to asynchronously detect changes in source code, 36 | # routes, locales, etc. This feature depends on the listen gem. 37 | # config.file_watcher = ActiveSupport::EventedFileUpdateChecker 38 | end 39 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | 27 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 28 | # config.action_controller.asset_host = 'http://assets.example.com' 29 | 30 | # Specifies the header that your server uses for sending files. 31 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 32 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 33 | 34 | # Mount Action Cable outside main process or domain 35 | # config.action_cable.mount_path = nil 36 | # config.action_cable.url = 'wss://example.com/cable' 37 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 38 | 39 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 40 | # config.force_ssl = true 41 | 42 | # Use the lowest log level to ensure availability of diagnostic information 43 | # when problems arise. 44 | config.log_level = :debug 45 | 46 | # Prepend all log lines with the following tags. 47 | config.log_tags = [ :request_id ] 48 | 49 | # Use a different cache store in production. 50 | # config.cache_store = :mem_cache_store 51 | 52 | # Use a real queuing backend for Active Job (and separate queues per environment) 53 | # config.active_job.queue_adapter = :resque 54 | # config.active_job.queue_name_prefix = "test_app_#{Rails.env}" 55 | config.action_mailer.perform_caching = false 56 | 57 | # Ignore bad email addresses and do not raise email delivery errors. 58 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 59 | # config.action_mailer.raise_delivery_errors = false 60 | 61 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 62 | # the I18n.default_locale when a translation cannot be found). 63 | config.i18n.fallbacks = true 64 | 65 | # Send deprecation notices to registered listeners. 66 | config.active_support.deprecation = :notify 67 | 68 | # Use default logging formatter so that PID and timestamp are not suppressed. 69 | config.log_formatter = ::Logger::Formatter.new 70 | 71 | # Use a different logger for distributed setups. 72 | # require 'syslog/logger' 73 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 74 | 75 | if ENV["RAILS_LOG_TO_STDOUT"].present? 76 | logger = ActiveSupport::Logger.new(STDOUT) 77 | logger.formatter = config.log_formatter 78 | config.logger = ActiveSupport::TaggedLogging.new(logger) 79 | end 80 | 81 | # Do not dump schema after migrations. 82 | config.active_record.dump_schema_after_migration = false 83 | end 84 | -------------------------------------------------------------------------------- /spec/test_app/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | TestApp::Application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Show full error reports and disable caching. 16 | config.consider_all_requests_local = true 17 | config.action_controller.perform_caching = false 18 | 19 | # Raise exceptions instead of rendering exception templates. 20 | config.action_dispatch.show_exceptions = false 21 | 22 | # Disable request forgery protection in test environment. 23 | config.action_controller.allow_forgery_protection = false 24 | 25 | # Tell Action Mailer not to deliver emails to the real world. 26 | # The :test delivery method accumulates sent emails in the 27 | # ActionMailer::Base.deliveries array. 28 | config.action_mailer.delivery_method = :test 29 | 30 | # Print deprecation notices to the stderr. 31 | config.active_support.deprecation = :stderr 32 | 33 | # Raises error for missing translations 34 | # config.action_view.raise_on_missing_translations = true 35 | 36 | config.cache_store = :memory_store 37 | end 38 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /spec/test_app/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /spec/test_app/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /spec/test_app/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /spec/test_app/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 3 | end 4 | -------------------------------------------------------------------------------- /spec/test_app/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: aee32486b316eb6471b2d031607927170fa23896323cf7abaee3fc62cab30853605980b4021fbb2864ed0982984f724ca4b32a97c403242dc77e1a91d60d1cbc 22 | 23 | test: 24 | secret_key_base: a6111b1050049f420ed4672328bd5d128e1f1ab4bb4ec5afde9ff043d59f143c9cd72c18b2b3b7c009e2ed85079e953358ab522ee9f866227aba73a4e98b19cb 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /spec/test_app/db/migrate/20170815215418_create_articles.rb: -------------------------------------------------------------------------------- 1 | superclass = if Gem::Version.new(Rails.version) >= Gem::Version.new(5) 2 | ActiveRecord::Migration[5.0] 3 | else 4 | ActiveRecord::Migration 5 | end 6 | 7 | class CreateArticles < superclass 8 | def change 9 | create_table :articles do |t| 10 | t.string :title 11 | t.text :text 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/test_app/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170815215418) do 14 | 15 | create_table "articles", force: :cascade do |t| 16 | t.string "title", limit: 255 17 | t.text "text" 18 | t.datetime "created_at", null: false 19 | t.datetime "updated_at", null: false 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /spec/test_app/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /spec/test_app/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/lib/assets/.keep -------------------------------------------------------------------------------- /spec/test_app/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/lib/tasks/.keep -------------------------------------------------------------------------------- /spec/test_app/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/log/.keep -------------------------------------------------------------------------------- /spec/test_app/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/test_app/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /spec/test_app/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /spec/test_app/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /spec/test_app/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/public/apple-touch-icon.png -------------------------------------------------------------------------------- /spec/test_app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/public/favicon.ico -------------------------------------------------------------------------------- /spec/test_app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /spec/test_app/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/tmp/.keep -------------------------------------------------------------------------------- /spec/test_app/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iaintshine/ruby-rails-tracer/7d7173e3b03078d9525b65e9febf38adfe59e8ac/spec/test_app/vendor/.keep --------------------------------------------------------------------------------