【问题标题】:Use Rack::CommonLogger in Sinatra在 Sinatra 中使用 Rack::CommonLogger
【发布时间】:2011-01-15 09:42:12
【问题描述】:

我有一个使用 Sinatra 编写的小型 Web 服务器。我希望能够将消息记录到日志文件中。我已经阅读了http://www.sinatrarb.com/api/index.html 和 www.sinatrarb.com/intro.html,我看到 Rack 有一个叫做 Rack::CommonLogger 的东西,但我找不到任何关于如何访问和用于记录的示例消息。我的应用程序很简单,因此我将其编写为顶级 DSL,但如果需要,我可以切换到从 SinatraBase 对其进行子类化。

【问题讨论】:

    标签: ruby logging sinatra rack error-logging


    【解决方案1】:

    Rack::CommonLogger 不会向您的主应用程序提供记录器,它只会像 Apache 那样记录请求。

    自行查看代码:https://github.com/rack/rack/blob/master/lib/rack/common_logger.rb

    所有Rack 应用程序都有使用 HTTP 请求 env 调用的 call 方法,如果您检查此中间件的 call 方法,会发生这种情况:

    def call(env)
      began_at = Time.now
      status, header, body = @app.call(env)
      header = Utils::HeaderHash.new(header)
      log(env, status, header, began_at)
      [status, header, body]
    end
    

    在这种情况下@app 是主应用程序,中间件只是注册请求开始的时间,然后它对你的中间件进行分类,获取 [status, header, body] 三元组,然后调用私有日志方法使用这些参数,返回与您的应用最初返回的相同的三元组。

    logger 方法如下:

    def log(env, status, header, began_at)
      now = Time.now
      length = extract_content_length(header)
    
      logger = @logger || env['rack.errors']
      logger.write FORMAT % [
        env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
        env["REMOTE_USER"] || "-",
        now.strftime("%d/%b/%Y %H:%M:%S"),
        env["REQUEST_METHOD"],
        env["PATH_INFO"],
        env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
        env["HTTP_VERSION"],
        status.to_s[0..3],
        length,
        now - began_at ]
    end
    

    如您所知,log 方法只是从请求环境中获取一些信息,并登录到构造函数调用中指定的记录器,如果没有记录器实例,则它转到rack.errors logger(好像默认有一个)

    使用方法(在你的config.ru):

    logger = Logger.new('log/app.log')
    
    use Rack::CommonLogger, logger
    run YourApp
    

    如果你想在你的所有应用中都有一个通用的记录器,你可以创建一个简单的记录器中间件:

    class MyLoggerMiddleware
    
      def initialize(app, logger)
        @app, @logger = app, logger
      end
    
      def call(env)
        env['mylogger'] = @logger
        @app.call(env)
      end
    
    end
    

    要使用它,请在您的config.ru

    logger = Logger.new('log/app.log')
    use Rack::CommonLogger, logger
    use MyLoggerMiddleware, logger
    run MyApp
    

    希望这会有所帮助。

    【讨论】:

    • MyLoggerMiddleware#call(env) 的第一行不应该是:env['rack.errors'] = @logger 吗?
    • 另外,我不想记录每个请求,只记录警告和错误消息。但我希望它是可配置的,以便我可以设置日志记录级别,如“调试”、“信息”、“警告”、“错误”……顺便说一句 - 我的应用程序不是 Rails 应用程序。没有 config.ru 文件。这是一个简单的 Sinatra 应用程序。我希望使用现有的标准,但不知道那是什么。也许我必须拿你给我看的 CommonLogger 自己动手?
    • config.ru 是 Rack 的配置文件,而不是 Rails 的配置文件。 Sinatra 和 Rails 都是基于 Rack 的,因此您也可以在 Sinatra 应用程序中使用 config.ru
    • 这里首先发布的文件链接已更改。目前是github.com/rack/rack/blob/master/lib/rack/common_logger.rb
    • 这对我不起作用。它对 Sinatra 或 Rack 日志记录没有影响。
    【解决方案2】:

    在你的config.ru

    root = ::File.dirname(__FILE__)
    logfile = ::File.join(root,'logs','requests.log')
    
    require 'logger'
    class ::Logger; alias_method :write, :<<; end
    logger  = ::Logger.new(logfile,'weekly')
    
    use Rack::CommonLogger, logger
    
    require ::File.join(root,'myapp')
    run MySinatraApp.new # Subclassed from Sinatra::Application
    

    【讨论】:

    • 我尝试了您的解决方案,它确实有效。我想知道为什么它必须在 config.ru 中?
    • 一个文件我不是 100% 在那里,你介意评论一下什么/为什么有些行吗?
    【解决方案3】:

    我关注了我在 blog 帖子中找到的内容 - 摘录如下

    require 'rubygems'
    require 'sinatra'
    
    disable :run
    set :env, :production
    set :raise_errors, true
    set :views, File.dirname(__FILE__) + '/views'
    set :public, File.dirname(__FILE__) + '/public'
    set :app_file, __FILE__
    
    log = File.new("log/sinatra.log", "a")
    STDOUT.reopen(log)
    STDERR.reopen(log)
    
    require 'app'
    run Sinatra.application
    

    然后使用putsprint。它对我有用。

    【讨论】:

    • 这行得通,但我真的很想知道如何使用 Rack::CommonLogger 发送带有时间戳格式的消息。
    【解决方案4】:
    class ErrorLogger
      def initialize(file)
        @file = ::File.new(file, "a+")
        @file.sync = true
      end
    
      def puts(msg)
        @file.puts
        @file.write("-- ERROR -- #{Time.now.strftime("%d %b %Y %H:%M:%S %z")}: ")
        @file.puts(msg)
      end
    end
    
    
    class App < Sinatra::Base
    
      if production?
        error_logger = ErrorLogger.new('log/error.log')
    
        before {
          env["rack.errors"] =  error_logger
        }
      end
    
      ...
    
    end
    

    【讨论】:

      【解决方案5】:

      如果您使用Passenger,重新打开STDOUT 并将其重定向到文件不是一个好主意。在我的情况下,它导致乘客没有启动。请阅读https://github.com/phusion/passenger/wiki/Debugging-application-startup-problems#stdout-redirection 了解此问题。

      这将是正确的方法:

      logger = ::File.open('log/sinatra.log', 'a+')
      Sinatra::Application.use Rack::CommonLogger, logger
      

      【讨论】:

        猜你喜欢
        • 2010-10-31
        • 2012-11-02
        • 2014-04-08
        • 1970-01-01
        • 1970-01-01
        • 2011-12-03
        • 1970-01-01
        • 2017-04-26
        • 2013-08-05
        相关资源
        最近更新 更多