【问题标题】:How do I log asynchronous thin+sinatra+rack requests?如何记录异步瘦+sinatra+机架请求?
【发布时间】:2011-06-21 14:33:48
【问题描述】:

我正在编写我的第一个基于 Sinatra 的 Web 应用程序作为另一个基于 TCP 的服务的前端,使用 EventMachine 和 async_sinatra 异步处理传入的 HTTP 请求。当我测试我的应用程序时,对同步路由的所有请求都以通用日志格式记录到标准输出,但异步请求不是。

我已经阅读了 async_sinatra、Sinatra、Thin 和 Rack 的源代码,看起来同步请求的日志记录是通过 CommonLogger#call 完成的。但是,我在 async_sinatra 或 Thin 的异步代码中找不到任何似乎通过日志中间件传递异步请求的地方(我正在查看 async_sinatra 中的 Sinatra::Helpers#body 和写入 env['. async_callback'] 在 Thin 的 connection.rb:68 和 request.rb:132)。

我有 C 经验,但对 Ruby 比较陌生,所以如果我使用了一些不正确的术语或符号,请纠正我。提前致谢。

编辑:这也会影响错误处理。如果在异步请求中引发异常,则该请求永远不会完成并且永远不会记录错误。

【问题讨论】:

标签: asynchronous sinatra rack thin


【解决方案1】:

我最终发现将 rack-async 与 async_sinatra 一起使用会导致 404 页面、异常处理和日志记录问题:

!! Unexpected error while processing request: undefined method `bytesize' for nil:NilClass

相反,我使用aroute 周围的以下包装器进行日志记录:

module Sinatra::Async
    alias :oldaroute :aroute
    def aroute verb, path, opts = {}, &block
        # Based on aroute from async_sinatra

        run_method = :"RunA#{verb} #{path} #{opts.hash}"
        define_method run_method, &block

        log_method = :"LogA#{verb} #{path} #{opts.hash}"
        define_method(log_method) { |*a|
            puts "#{request.ip} - #{status} #{verb} #{path}"
        }

        oldaroute verb, path, opts do |*a|
            oldcb = request.env['async.callback']
            request.env['async.callback'] = proc { |*args|
                async_runner(log_method, *a)
                oldcb[*args]
            }
            async_runner(run_method, *a)
        end
    end
end

这适用于我去年问这个问题时使用的相同版本的 async_sinatra、Thin 和 Rack;较新的版本可能允许使用通用 Rack 中间件进行日志记录。

【讨论】:

    【解决方案2】:

    我在sinatra-synchrony 上运行,因此我的核心与你略有不同。 但基本上我解决了同样的问题。 以下是解决方案的摘要:

    • 我没有使用Rack::CommonLogger,我使用自己的Logger
    • 您需要在异步感知存储中缓冲日志输出
    • 缓冲的日志输出必须在请求结束时刷新

    在我的sinatra-synchrony 应用程序中,我正在运行以下中间件进行日志记录:

    # in app.rb I register Logger::Middleware as the first middleware
    use Logger::Middleware
    
    # in logger.rb
    module Logger
      attr_accessor :messages
    
      def log(message)
        stack << message
      end
    
      def stack
        # This is the important async awareness
        # It stores messages for each fiber separately
        messages[Fiber.current.object_id] ||= []
      end
    
      def flush
        STDERR.puts stack.join("\n") unless stack.empty?
        messages.delete Fiber.current.object_id
      end
      extend self
    
      class Middleware
        def initialize(app)
          @app = app
        end
    
        def call(env)
          # before the request
          Logger.log "#{env['REQUEST_METHOD']} #{env['REQUEST_URI']}"
          result = @app.call(env)
          # after the request
          Logger.flush
          result
        end
      end
    end
    Logger.messages = {} # initialize the message storage
    

    在应用程序的任何地方我都可以使用Logger.log("message") 进行日志记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-30
      • 1970-01-01
      • 2012-04-17
      • 2012-08-08
      • 2014-10-31
      • 1970-01-01
      • 2023-04-06
      • 2016-07-18
      相关资源
      最近更新 更多