Как мне регистрировать асинхронные запросы thin + sinatra + rack? - PullRequest
4 голосов
/ 21 июня 2011

Я пишу свое первое веб-приложение на основе Sinatra в качестве внешнего интерфейса для другой службы на основе TCP, используя EventMachine и async_sinatra для асинхронной обработки входящих HTTP-запросов.Когда я тестирую свое приложение, все запросы к синхронным маршрутам записываются на стандартный вывод в обычном формате журнала, но асинхронные запросы - нет.

Я прочитал биты исходного кода в async_sinatra, Sinatra, Thinи Rack, и, похоже, регистрация синхронных запросов осуществляется через вызов CommonLogger #.Тем не менее, я не могу найти нигде в асинхронном коде в async_sinatra или Thin, который, кажется, передает асинхронные запросы через промежуточное программное обеспечение регистрации (я смотрю на Sinatra :: Helpers # body в async_sinatra и на Thin :: Connection.post_process , который записан в env ['. Async_callback'] в Thin's connection.rb: 68 и request.rb: 132).

У меня есть опыт работы с C, но относительноплохо знаком с Ruby, поэтому, если я неправильно использовал некоторые термины или обозначения, поправьте меня.Заранее спасибо.

Редактировать: это также влияет на обработку ошибок.Если исключение возникает в асинхронном запросе, запрос никогда не завершается и ошибка никогда не регистрируется.

Ответы [ 2 ]

2 голосов
/ 11 ноября 2012

В конце концов я обнаружил, что использование 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 для регистрации.

1 голос
/ 20 декабря 2011

Я работаю на 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") для ведения журнала.

...