こしごぇ(B)

旧:http://d.hatena.ne.jp/koshigoeb/

Rails.logger.error(msg) したら Bugsnag にも通知しちゃうやつ

注意: quiet_assets など、broadcast 後に Rails.logger.level= する様な実装をしているケースには未対応。

こんな感じでやってみた。

  • lib/ext と lib/bugsnag に分けたのは、Bugsnag クライアントが lib/bugsnag にマッチしたバックトレース行を読み飛ばしてくれる実装になっているから
  • Bugsnag.configure の config.logger をセットしてるのは、デフォルトの Rails.logger を Bugsnag クライアント内部で使っているため(再帰回避)
  • broadcast するタイミングはどこが適切かよくわかってない
diff --git a/config/application.rb b/config/application.rb
index 3ddf16f..e10572f 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -17,6 +17,8 @@ Bundler.require(*Rails.groups)
 
 module RailsExampleActivesupportLoggerBroadcast
   class Application < Rails::Application
+    config.autoload_paths += %W(#{config.root}/lib/ext #{config.root}/lib/bugsnag)
+
     # Settings in config/environments/* take precedence over those specified here.
     # Application configuration should go into files in config/initializers
     # -- all .rb files in that directory are automatically loaded.
@@ -31,5 +33,11 @@ module RailsExampleActivesupportLoggerBroadcast
 
     # Do not swallow errors in after_commit/after_rollback callbacks.
     config.active_record.raise_in_transactional_callbacks = true
+
+    config.after_initialize do
+      error_logger = ErrorMonitor::Logger.new(:bugsnag)
+      error_logger.level = Logger::WARN
+      Rails.logger.extend ActiveSupport::Logger.broadcast(error_logger)
+    end
   end
 end
diff --git a/config/initializers/bugsnag.rb b/config/initializers/bugsnag.rb
index 3b6d7ae..2bff2a2 100644
--- a/config/initializers/bugsnag.rb
+++ b/config/initializers/bugsnag.rb
@@ -1,3 +1,4 @@
 Bugsnag.configure do |config|
   config.api_key = ENV['BUGSNAG_API_KEY']
+  config.logger = ActiveSupport::Logger.new(Rails.root.join('log/bugsnag.log'))
 end
diff --git a/lib/bugsnag/error_monitor/bugsnag.rb b/lib/bugsnag/error_monitor/bugsnag.rb
new file mode 100644
index 0000000..620e0ea
--- /dev/null
+++ b/lib/bugsnag/error_monitor/bugsnag.rb
@@ -0,0 +1,15 @@
+module ErrorMonitor
+  class Bugsnag
+    def write(message)
+      if message[:options][:severity] >= Logger::ERROR
+        message[:options][:severity] = 'error'
+      elsif message[:options][:severity] >= Logger::WARN
+        message[:options][:severity] = 'warning'
+      else
+        message[:options][:severity] = 'info'
+      end
+
+      ::Bugsnag.notify(message[:exception], message[:options])
+    end
+  end
+end
diff --git a/lib/ext/error_monitor/logger.rb b/lib/ext/error_monitor/logger.rb
new file mode 100644
index 0000000..cd4db68
--- /dev/null
+++ b/lib/ext/error_monitor/logger.rb
@@ -0,0 +1,20 @@
+require 'active_support/logger'
+
+module ErrorMonitor
+  class Logger < ActiveSupport::Logger
+    def initialize(*args)
+      @progname = nil
+      @level = ERROR
+      @default_formatter = proc do |severity, datetime, progname, msg|
+        {
+          exception: RuntimeError.new(msg),
+          options: {
+            severity: SEV_LABEL.index(severity)
+          }
+        }
+      end
+      @formatter = nil
+      @logdev = "ErrorMonitor::#{args.first.to_s.camelize}".constantize.new
+    end
+  end
+end

追記

取り急ぎ Gem にしてみた。