Dead simple, zero-dependency exception tracking for Redmine.
Errmine automatically creates and updates Redmine issues from Ruby/Rails exceptions. When an error occurs, it creates a new issue. When the same error occurs again, it increments a counter and adds a journal note instead of creating duplicates.
[a1b2c3d4][47] NoMethodError: undefined method 'foo' for nil...
If you already use Redmine for project management, Errmine lets you track production errors without adding another service to your stack.
vs. Sentry, Honeybadger, Airbrake, etc.
- No external service, no monthly fees, no data leaving your infrastructure
- Errors live alongside your tasks and documentation in Redmine
- No new UI to learn - just Redmine issues
vs. Exception Notification gem
- Automatic deduplication - same error updates existing issue instead of creating duplicates
- Rate limiting prevents inbox/Redmine flooding during error storms
- Occurrence counting shows error frequency at a glance
vs. Rolling your own
- Zero dependencies - uses only Ruby stdlib
- Handles edge cases: rate limiting, timeouts, thread safety, fail-safe error handling
- Works out of the box with Rails 7+ Error Reporting API
- Zero dependencies - Uses only Ruby stdlib (net/http, json, digest, uri)
- Automatic deduplication - Same errors update existing issues instead of creating duplicates
- Rate limiting - Prevents flooding Redmine during error loops
- Rails integration - Works with Rails 7+ Error Reporting API or as Rack middleware
- Thread-safe - Safe to use in multi-threaded environments
- Fail-safe - Never crashes your application, logs errors to stderr
Add to your Gemfile:
gem 'errmine'Then run:
bundle installErrmine reads these environment variables as defaults:
ERRMINE_REDMINE_URL- Redmine server URLERRMINE_API_KEY- API key for authenticationERRMINE_PROJECT- Project identifier (default:'bug-tracker')ERRMINE_APP_NAME- Application name (default:'unknown')
Create an initializer:
# config/initializers/errmine.rb
Errmine.configure do |config|
config.redmine_url = ENV.fetch('REDMINE_URL')
config.api_key = ENV.fetch('REDMINE_API_KEY')
config.project_id = 'my-project'
config.tracker_id = 1 # Default: 1 (Bug)
config.app_name = Rails.application.class.module_parent_name
config.cooldown = 300 # Default: 300 seconds
endrequire 'errmine'
Errmine.configure do |config|
config.redmine_url = 'https://redmine.example.com'
config.api_key = 'your-redmine-api-key'
config.project_id = 'my-project'
end| Option | Default | Description |
|---|---|---|
redmine_url |
ENV['ERRMINE_REDMINE_URL'] |
Redmine server URL (required) |
api_key |
ENV['ERRMINE_API_KEY'] |
API key for authentication (required) |
project_id |
ENV['ERRMINE_PROJECT'] or 'bug-tracker' |
Redmine project identifier |
tracker_id |
1 |
Tracker ID (usually 1 = Bug) |
app_name |
ENV['ERRMINE_APP_NAME'] or 'unknown' |
Application name shown in issues |
enabled |
true |
Enable/disable notifications |
cooldown |
300 |
Seconds between same-error notifications |
Errmine automatically subscribes to Rails' error reporting API via a Railtie. Unhandled exceptions are reported automatically. No additional setup required.
For all Rails versions or any Rack-based application:
# config/application.rb (Rails)
config.middleware.use Errmine::Middleware
# or config.ru (Sinatra, etc.)
use Errmine::MiddlewareThe middleware captures request context (URL, HTTP method, user via Warden).
Report exceptions manually with custom context:
begin
risky_operation
rescue => e
Errmine.notify(e, {
url: request.url,
user: current_user&.email,
custom_field: 'any value'
})
raise
endclass ApplicationController < ActionController::Base
rescue_from StandardError do |e|
Errmine.notify(e, {
url: request.url,
user: current_user&.email
})
raise e
end
endErrmine.configure do |config|
config.enabled = Rails.env.production?
endEach exception gets an 8-character MD5 checksum based on:
- Exception class name
- Exception message
- First application backtrace line (containing
/app/)
MD5("NoMethodError:undefined method 'foo':app/controllers/users_controller.rb:45")[0..7]
# => "a1b2c3d4"
- Search Redmine for open issues containing
[{checksum}]in the subject - If found: increment counter, add journal note with timestamp and backtrace
- If not found: create new issue
To prevent flooding Redmine during error loops:
- Each checksum is cached with its last occurrence time
- Same error won't hit Redmine more than once per cooldown period (default: 5 minutes)
- Cache is automatically cleaned when it exceeds 500 entries
- Enable REST API: Administration > Settings > API > Enable REST web service
- Create an API key: My Account > API access key > Show/Reset
- Create a project for error tracking (or use existing one)
- Permissions: The API user needs:
- View issues
- Add issues
- Edit issues
- Add notes
[{checksum}][{count}] {ExceptionClass}: {truncated message}
Example: [a1b2c3d4][47] NoMethodError: undefined method 'foo' for nil...
**Exception:** @NoMethodError@
**Message:** undefined method 'foo' for nil:NilClass
**App:** my-app
**First seen:** 2025-01-15 10:30:00
**URL:** /users/123
**User:** user@example.com
h3. Backtrace
<pre>
app/controllers/users_controller.rb:45:in `show'
app/controllers/application_controller.rb:12:in `authenticate'
</pre>
Occurred again (*47x*) at 2025-01-15 10:35:00
URL: /users/456
<pre>
app/controllers/users_controller.rb:45:in `show'
</pre>
MIT License. See LICENSE for details.
