Ruby production code coverage collection and reporting (line of code usage)
Key Features • Installation • Coverband Web UI • Advanced Config • Newer Features • License • Change Log / Roadmap • Code of Conduct
A gem to measure production code usage, showing a counter for the number of times each line of code that is executed. Coverband allows easy configuration to collect and report on production code usage. It reports in the background via a thread or can be used as Rack middleware, or manually configured to meet any need.
Note: Coverband is not intended for test code coverage, for that we recommended using SimpleCov.
The primary goal of Coverband is giving deep insight into your production runtime usage of your application code, while having the least impact on performance possible.
Coverband stores coverage data in Redis. The Redis endpoint is looked for in this order:
ENV['COVERBAND_REDIS_URL']
ENV['REDIS_URL']
localhost:6379
The redis store can also be explicitly defined within the coverband.rb. See advanced config.
Add this line to your application's
Gemfile, remember to
bundle installafter updating:
gem 'coverband'
With older versions of coverband, projects would report to redis using rack or sidekiq middleware. After coverband 4.0, this should no longer be required and could cause performance issues. Reporting to redis is now automatically done within a background thread with no custom code needed.
See changelog.
The Railtie integration means you shouldn't need to do anything else other than ensure Coverband is required after Rails within your Gemfile.
For the best coverage you want this loaded as early as possible. We recommend requiring cover band directly in the
config.ru. Requiring coverband within an initializer could also work, but you may end up missing some boot up coverage. To start collection require Coverband as early as possible.
require 'coverband' require File.dirname(__FILE__) + '/config/environment'use Coverband::BackgroundMiddleware run ActionController::Dispatcher.new
The web index as available on the Coverband Demo site
View overall coverage information
Drill into individual file coverage
View individual file details
Clear Coverage - disabled by default as it could be considered a dangerous operation in production. Enable with
config.web_enable_clearor leave off and clear from rake task.
This will clear the coverage data. This wipes out all collected data. - Clear individual file coverage
This will clear the details of the file you are looking at. This is helpful if you don't want to lose all Coverage data but made a change that you expect would impact a particular file.
Force coverage collection
This triggers coverage collection on the current webserver process. Useful in development but confusing in production environments where many ruby processes are usually running.
Coverband comes with a mountable rack app for viewing reports. For Rails this can be done in
config/routes.rbwith:
Rails.application.routes.draw do mount Coverband::Reporters::Web.new, at: '/coverage' end
But don't forget to protect your source code with proper authentication. Something like this when using devise:
Rails.application.routes.draw do authenticate :user, lambda { |u| u.admin? } do mount Coverband::Reporters::Web.new, at: '/coverage' end end
or you can enable basic auth by setting
ENV['COVERBAND_PASSWORD']or via your configuration
config.password =
The coverage server can also be started standalone with a rake task:
bundle exec rake coverband:coverage_server
The web UI should then be available here: http://localhost:9022/
If you want to run on an alternative port:
COVERBAND_COVERAGE_PORT=8086 bundle exec rake coverband:coverage_server
This is especially useful for projects that are api only and cannot support the mounted rack app. To get production coverage, point coverband at your production redis server and ensure to checkout the production version of your project code locally.
COVERBAND_REDIS_URL=redis://username:password:redis_production_server:2322 bundle exec rake coverband:coverage_server
Take Coverband for a spin on the live Heroku deployed Coverband Demo. The full source code for the demo is available to help with installation, configuration, and understanding of basic usage.
If you need to configure coverband, this can be done by creating a
config/coverband.rbfile relative to your project root.
ENV['COVERBAND_REDIS_URL'],
ENV['REDIS_URL'], or
localhost
Below is an example config file for a Rails 5 app:
#config/coverband.rb Coverband.configure do |config| config.store = Coverband::Adapters::RedisStore.new(Redis.new(url: ENV['MY_REDIS_URL'])) config.logger = Rails.loggerconfig options false, true. (defaults to false)
true and debug can give helpful and interesting code usage information
and is safe to use if one is investigating issues in production, but it will slightly
hit perf.
config.verbose = false
default false. button at the top of the web interface which clears all data
config.web_enable_clear = true
default false. Experimental support for tracking view layer tracking.
Does not track line-level usage, only indicates if an entire file
is used or not.
config.track_views = true end
Do you use figaro, mc-settings, dotenv or something else to inject environment variables into your app? If so ensure you have that done BEFORE coverband is required.
For example if you use dotenv, you need to do this, see https://github.com/bkeepers/dotenv#note-on-load-order
gem 'dotenv-rails', require: 'dotenv/rails-now' gem 'coverband' gem 'other-gem-that-requires-env-variables'
Sometimes you have files that are known to be valuable perhaps in other environments or something that is just run very infrequently. Opposed to having to mentally filter them out of the report, you can just have them ignored in the Coverband reporting by using
config.ignoreas shown below. Ignore takes a string but can also match with regex rules see how below ignores all rake tasks as an example.
config.ignore += ['config/application.rb', 'config/boot.rb', 'config/puma.rb', 'config/schedule.rb', 'bin/*', 'config/environments/*', 'lib/tasks/*']
Ignoring Custom Gem Locations: Note, if you have your gems in a custom location under your app folder you likely want to add them to
config.ignore. For example, if you have your gems not in a default ignored location of
app/vendorbut have them in
app/gemsyou would need to add
gems/*to your ignore list.
Coverband allows an optional feature to track all view files that are used by an application.
To opt-in to this feature... enable the feature in your Coverband config.
config.track_views = true
If all your coverage is being counted as loading or eagerloading coverage, and nothing is showing as runtime Coverage the initialization hook failed for some reason. The most likely reason for this issue is manually calling `eagerload!
on some Plugin/Gem. If you or a plugin is altering the Rails initialization process, you can manually flip Coverband to runtime coverage by calling these two lines, in anafter_initialize
block, inapplication.rb`.
config.after_initialize do unless Coverband.tasks_to_ignore? Coverband.report_coverage # record the last of the loading coverage Coverband.runtime_coverage! # set all future coverage to runtime end end
or if you know you are manually calling eager load anywhere in your initialization process immediately adfter call those two lines. A user reported an issue after calling
ResqueWeb::Engine.eager_load!for example.
Rails.application.routes.draw do ResqueWeb::Engine.eager_load! Coverband.report_coverage Coverband.runtime_coverage! end
If you have many servers and they all hit Redis at the same time you can see spikes in your Redis CPU, and memory. This is do to a concept called cache stampede. It is better to spread out the reporting across your servers. A simple way to do this is to add a random wiggle on your background reporting. This configuration option allows a wiggle. The right amount of wiggle depends on the numbers of servers you have and how willing you are to have delays in your coverage reporting. I would recommend at least 1 second per server. Note, the default wiggle is set to 30 seconds.
Add a wiggle (in seconds) to the background thread to avoid all your servers reporting at the same time:
config.reporting_wiggle = 30
Coverband on very high volume sites with many server processes reporting can have a race condition which can cause hit counts to be inaccurate. To resolve the race condition and reduce Ruby memory overhead we have introduced a new Redis storage option. This moves the some of the work from the Ruby processes to Redis. It is worth noting because of this, it has a larger demands on the Redis server. So adjust your Redis instance accordingly. To help reduce the extra redis load you can also change the background reporting frequency.
config.store = Coverband::Adapters::HashRedisStore.new(Redis.new(url: redis_url))
config.background_reporting_sleep_seconds = 120
See more discussion here.
Now that Coverband uses MD5 hashes there should be no reason to manually clear coverage unless one is testing, changing versions, possibly debugging Coverband itself.
rake coverband:clear
This can also be done through the web if
config.web_enable_clearis enabled.
Between the release of 4.0 and 4.1 our data format changed. This resets all your coverage data. If you want to restore your previous coverage data, feel free to migrate.
rake coverband:migrate
Rails apps should automaticallly include the tasks via the Railtie.
For non Rails apps, either add the below to your
Rakefileor to a file included in your
Rakefilesuch as
lib/tasks/coverband.rakeif you want to break it up that way.
require 'coverband' Coverband.configure require 'coverband/utils/tasks'
Verify it works
rake -T coverband rake coverband:clear # reset coverband coverage data rake coverband:coverage # report runtime coverband code coverage
Coverband starts on require of the the library which is usually done within the Gemfile. This can be disabled by setting the
COVERBAND_DISABLE_AUTO_STARTenvironment variable. This environment variable can be useful to toggle coverband on and off in certain environments.
NOTE: That any value set for
COVERBAND_DISABLE_AUTO_STARTis considered true, it does not match the string content but only checks the presence of the ENV variable.
In order to start coverband manually yourself when this flag is enabled, call
Coverband.configurefollowed by
Coverband.start.
Coverband.configure Coverband.start
Note: To debug issues getting Coverband working. I recommend running in development mode, by turning verbose logging on
config.verbose = trueand passing in the Rails.logger
config.logger = Rails.loggerto the Coverband config. We respect the log level, and I would recommend log level info generally, but if you are investigating a problem Coverband logs additional data at the
debuglevel. This makes it easy to follow in development mode. Be careful to not leave these on in production as they will affect performance.
If you are trying to debug locally wondering what code is being run during a request. The verbose modes
config.verbose = true&&
Rails.logger.level = :debug. With true set it will output the number of lines executed per file, to the passed in log.
If you start seeing SystemStackError: stack level too deep errors from background jobs after installing Coverband, this means there is another patch for ResqueWorker that conflicts with Coverband's patch in your application. To fix this, change coverband gem line in your Gemfile to the following:
gem 'coverband', require: ['alternative_coverband_patch', 'coverband']
If you currently have require: false, remove the 'coverband' string from the require array above so the gem line becomes like this:
gem 'coverband', require: ['alternative_coverband_patch']
This conflict happens when a ruby method is patched twice, once using module prepend, and once using method aliasing. See this ruby issue for details. The fix is to apply all patches the same way. Coverband by default will apply its patch using prepend, but you can change that to method aliasing by adding require: ['alternativecoverbandpatch'] to the gem line as shown above.
Rake task that outputs dead methods based on current coverage data:
bundle exec rake coverband:dead_methods
Outputs:
--------------------------------------------------------------------------------------------------- | file | class | method | line_number | | ./config/routes.rb | AdminConstraint | matches? | 20 | | ./app/controllers/home_controller.rb | HomeController | trigger_jobs | 8 | | ./app/controllers/home_controller.rb | HomeController | data_tracer | 14 | | ./app/controllers/posts_controller.rb | PostsController | edit | 22 | | ./app/controllers/posts_controller.rb | PostsController | destroy_bad_dangerously | 73 | ---------------------------------------------------------------------------------------------------
We will match Heroku & Ruby's support lifetime, supporting the last 3 major Ruby releases. For details see supported runtimes.
For Rails, we will follow the policy of the Rails team maintenance policy. We officially support the last two major release versions, while providing minimal support (major bugs / security fixes) for an additional version. This means at the moment we primaryly target Rails 6.x, 5.x, and will try to keep current functionality working for Rails 4.x but may release new features that do not work on that target.
Coverband is compatible with JRuby. If you want to run on JRuby note that I haven't benchmarked and I believe the perf impact on older versions of JRuby could be significant. Improved Coverage support is in JRuby master, and will be in the next release.
--debugoption
.jrubyrcthe
debug.fullTrace=truesetting
oneshot_linesis recommended, and in the latest releases should have very low overhead
If you are working on adding features, PRs, or bugfixes to Coverband this section should help get you going.
git checkout -b my-new-feature)
git commit -am 'Add some feature')
git push origin my-new-feature)
bundle install, make sure Redis is running, and then execute
rake test)
If you submit a change please make sure the tests and benchmarks are passing.
bundle exec rake
BUNDLE_GEMFILE=Gemfile.rails6 bundle exec rake(Same tests using rails 6 instead of 5)
open coverage/index.html
rake benchmarks
bundle exec m test/coverband/reporters/html_test.rb:29
erb,
slim, and the like.
ERBfiles are often off and aren't considered useful. I recommend filtering out .erb using the
config.ignoreoption. The default configuration excludes these files
AUTO_INSTRUMENT=true(usually production or staging) it stops reporting any coverage, it will show one or two files that have been loaded at the start but everything else will show up as having 0% coverage
What files have been synced to Redis?
Coverband.configuration.store.covered_files
What is the coverage data in Redis?
Coverband.configuration.store.coverage
The Coverband logo was created by Dave Woodall. Thanks Dave!
This is a MIT License project... See the file license.txt for copying permission.