2015-03-02 11 views
6

Wir möchten eine Sammlung von Controllern haben, wo wir Logger-Ausgabe von allen Aktionen und Downstream-Methoden in eine separate Protokolldatei leiten. Dies ist ein Rails 3-Projekt. In Rails 2 haben wir dies getan, indem wir die "Logger" -Methode neu definiert haben, aber in Rails 3 verwenden wir "Rails.logger". Ich habe versucht,Redirector Logger-Ausgabe für eine bestimmte Steuerung in Rails 3

Rails::logger = Logger.new(File.join(Rails.root, 'log', "reports_controller.log"), 10, 1000000) 

an der Oberseite des Controllers setzen, sondern nur die spezifischen Fälle, in denen Rails.logger speziell in der Aktion verwendet wird, wird immer auf die angegebene Protokolldatei gesendet, alle Standardprotokollausgabe für den Controller leitet immer noch zur Hauptprotokolldatei.

Wie können wir die gesamte Protokollausgabe für einen bestimmten Controller in eine bestimmte Protokolldatei umleiten, um alle Standard-Controller-Ausgaben einzubeziehen?

von Standard-Controller-Ausgang, ich beziehe mich auf alle Nachrichten, die

Started POST "/api/v1/reports.json" for 10.XXX.XX.XX at 2015-03-07 01:30:22 +0000 
Processing by Api::V1::ReportsController#create as JSON 
    Parameters: {"report"=>{"cloud_file_path"=>"report.zip", "company_id"=>nil, "created_at"=>"2015-03-07T01:30:17Z", "type_id"=>"2", "updated_at"=>"2015-03-07T01:30:17Z", "master"=>"1"}} 

und alle Lügen-Ausgabe mit dem Beginn der eingehenden Anfrage starten, die von der eingehenden Anfrage und Outbound-Reaktion in der folgen könnte Controller

Grundsätzlich möchte ich alle Protokollierung für die Berichte Controller in der reports_controller.log und ich möchte keine Nachrichten für den Datenverkehr zum Berichtscontroller im Hauptprotokoll angezeigt werden (dh production.log wenn in Produktion)

Update:

Dank @ mudasobwa Hilfe mit seiner Antwort und plaudern, ich war in der Lage, dies mit Middleware als seine Antwort zeigt zu lösen (obwohl ich vor Rails :: Rack sein musste mein insert_before ändern :: Logger)

die überarbeitete Antwort von ihm, dass es für mich gelöst ist unten und es lebt in config/initializers/logger_middleware.rb

module MyApp 
    class LoggerMiddleware 

    REPORTS_API_CONTROLLER_PATH = %r|\A/api/v.*/reports/.*| 
    REPORTS_API_CONTROLLER_LOGFILE = "reports_controller.log" 

    def initialize(app) 
     @app, @logger = app, Rails::logger.instance_variable_get(:@logger).instance_variable_get(:@log) 
     @reports_api_controller_logger = Logger.new(Rails.root.join('log', REPORTS_API_CONTROLLER_LOGFILE), 10, 1000000) 
    end 

    def call(env) 
     Rails::logger 
      .instance_variable_get(:@logger) 
      .instance_variable_set(:@log, 
       case env['PATH_INFO'] 
       when REPORTS_API_CONTROLLER_PATH then 
       @reports_api_controller_logger 
       else 
       @logger 
       end 
      ) 
     @app.call(env) 
    end 
    end 
end 

Rails.application.middleware.insert_before Rails::Rack::Logger, MyApp::LoggerMiddleware 

Antwort

2

Der Grund, dass nicht die ganze Zeug von Controller Filter umgeleitet wird, ist, dass diese „Gestartet ...“ usw. von rack middleware geschrieben werden, dass vor die Steuerung ausgeführt wurde sogar instanziiert.

Also, zu greifen und umleiten alles im Zusammenhang mit einigen Bedingungen, sollte man tiefer einmischen. Unten ist ein [möglicherweise unvollständiges] Beispiel, wie man in die Pipeline hackt.

Definieren Sie die Middleware-Logger wechseln

module MyApp 
    class MyMiddleware 

    def initialize(app) 
     @app, @logger = app, Rails.logger 
           .instance_variable_get(:@logger) 
           .instance_variable_get(:@log) 
     @my_logger = Logger.new('reports_controller.log', ...) 
    end 

    def call(env) 
     # env['action_dispatch.logger'].inspect 
     #⇒ <TaggedLogging... @logger=#<BufferedLogger...> @log_dest=...> 

     # here we do not have a controller name 
     Rails.logger 
      .instance_variable_get(:@logger) 
      .instance_variable_set(:@log, 
       case env['PATH_INFO'] # or PATH_INFO, or whatever 
       when %r|\A/api/v1/| then @my_logger 
       else @logger 
       end 
      ) 

     @app.call(env) 
    end 
    end 
end 

einen Initialisierer irgendwo in config/initializers/my_logger.rb

Rails.application.middleware.insert_before \ 
    Rails::Rack::Logger, MyApp::MyMiddleware 

Bitte beachten Sie hinzufügen, dass Rails' Logger ein verschachteltes Tier ist:

Rails::logger 
#⇒ #<ActiveSupport::TaggedLogging:0x000000020f5ad0 @logger=... 
Rails::logger.instance_variable_get(:@logger) 
#⇒ #<ActiveSupport::BufferedLogger:0x000000020f6188 @log_dest=... 
Rails::logger.instance_variable_get(:@logger) 
      .instance_variable_get(:@log) 
#⇒ #<Logger:0x000000020f6138 @progname=nil, @level=0, @default_formatter=... 

Man könnte eine spezifische formatter auf dem Logger einstellen möchten, oder sogar Filter Nachrichten mit regulären Ausdrücken gibt (obwohl es keine gute Praxis. In Betracht gezogen werden sollte)

+0

So ist dieser Ansatz, indem Sie die Rails.Logger in der Middleware basierend Bewertung der REQUEST_URI gehen zu Auswirkungen auf alle Protokollierung ab diesem Zeitpunkt für diesen einen Anruf weiterleiten? Oder ist das nur ein zusätzlicher Ort, um mehr zu fangen? Zum Beispiel nach der anderen Antwort bemerkte ich, dass ich ActiveRecord :: Base.logger separat umleiten musste, wenn ich wollte, db Protokollierung auch geroutet. Überschreibt die Rack-Middleware den Logger für diese und andere Orte, an denen auch Logger definiert ist, oder muss ich diesen erweitern, um mehr Logger an diesem Punkt (oder weiter stromaufwärts?) Einzubeziehen? – Streamline

+0

Die Einstellung 'Rails :: logger.instance_variable_get (: @ logger) .instance_variable_get (: @ log)' beeinflusst alles (ich benutze dies in der Produktion). Das Einstellen von 'Rails :: logger' hängt von etwas ab, in das ich nicht vertieft bin (Kurz gesagt: Ich schlage vor, den zugrunde liegenden 'Logger' zu setzen.) – mudasobwa

+0

Wenn Sie sagen, dass Rails :: logger.instance_variable_get (: @ logger) .instance_variable_get (: @ log) jetzt in Produktion ist, tun Sie das in der gleichen wie Sie über die Middleware gepostet haben und diese Zeile nur ersetzen, wenn% r | \ A/api/v1/| dann Rails :: logger = @ my_logger' oben mit diesem 'wenn% r | \ A/api/v1/| dann Rails :: logger.instance_variable_get (: @ logger) .instance_variable_set (: @ log, @ my_logger) '? – Streamline

4

Haben auch Sie eine around_filter versucht, vorangestellt?

class MyController < ApplicationController 
    prepend_around_filter :set_logger 

    private 

    def set_logger 
    old_logger = Rails::logger 
    Rails::logger = Logger.new(Rails.root.join('log', "reports_controller.log"), 10, 1000000) 
    yield 
    Rails.logger = old_logger 
    end 
end 
+0

Das Ergebnis ist das gleiche wie in meinem Codebeispiel, die expliziten Logger-Aufrufe werden jedoch an die neue Controller-Protokolldatei weitergeleitet. Alle Standard-Logger-Meldungen für die Controller-Aktion werden jedoch weiterhin an die Umgebungsstandardprotokolldatei weitergeleitet. Wiederum meine ich standardmäßig Nachrichten, die Rails selbst schreibt, wie "Gestartet ...", "Verarbeitung durch ..." und einschließlich Datenbank (dh wenn Umgebung Entwicklung ist), Nachrichten usw. – Streamline

+1

Hmm - diese Antwort hat mich inspiriert basteln mehr und ich erweiterte die Methode, um 'ActiveRecord :: Base.logger = new_logger' (und das Zurücksetzen nach Ertrag) einzuschließen, und dies routet die Aufzeichnung der aktiven Datensatzdatenbank auf den neuen spezifischen Logger - aber ich kann nicht scheinen, das' zu bekommen Starte ... 'und' Processing by ... 'und' Completed ... 'Nachrichten zum Weiterleiten an die spezifische Logdatei – Streamline

+0

@Streamline - hast du jemals herausgefunden, wie man auch die" Started ... "und "Processing by ..." in der anderen Log-Datei? – Mattherick

0

hinzufügen oben auf @ messanjah Antwort Sie können diese Logger überschreiben: Rails::logger, ActionView::Base.logger, ActionController::Base.logger, ActiveRecord::Base.logger.

Durch die Unterdrückung dieser Logger werden Sie die meisten Nachrichten los. Aber wie @mudasobwa wies darauf hin Gestartet ... Nachrichten werden vor dem Controller geschrieben.

Wenn Sie keine Middleware verwenden möchten, können Sie immer den Hammer und die Monkeypatch Rails::Rack::Logger verwenden, um Ihre Loggeranwendung weit zu verwenden. Oder Sie können nach dem Pfad suchen und bestimmte Nachrichten dort ablehnen.

+0

Wenn Sie sagen monkeypatch Rails :: Rack :: Logger Anwendung breit, wie würde das passieren 1) anders als die Art und Weise @mudasobwa schlägt vor, mit Middleware und 2) wie selektiv zu sein Controller-Aufruf, obwohl es vor dem Controller ist anders als Parsing der REQUEST_URI aufgerufen? – Streamline

Verwandte Themen