yhara.jp

Recent Posts
Edit

How to use file logger in Sinatra

2016-09-28
Tech

When you do google search with "sinatra logging file", you may find a solution with Rack::CommonLogger. However I was looking for a different approach because it does not respect interface of the logger stdlib. Instead of calling the usual logger.info, Rack::CommonLogger forcefully inject output with logger.write.

As a result, your log file will look like this. The first line is written by Rack::CommonLogger, which has completely different format with the usual logger output on the second line. This may be a problem when you write a program to parse and analyze the logfile.

::1 - - [27/Sep/2016:02:44:42 +0900] "GET / HTTP/1.1" 200 2 0.0077
I, [2016-09-27T02:44:42.416430 #49814]  INFO -- : hello world

Using your own logger

You can specify a logger by setting env["rack.logger"] in the before hook. You can also log request information like Rack::CommonLogger here (if you want).

require 'logger'
require 'sinatra/base'

class SomeApp < Sinatra::Base
  set :mylogger, Logger.new(#{settings.root}/log/#{settings.environment}.log")

  before do
    env["rack.logger"] = settings.mylogger

    query = env["QUERY_STRING"]
    msg = format("%s %s for %s",
                 env["REQUEST_METHOD"],
                 env["PATH_INFO"] + (query.empty? ? "" : "?#{query}"),
                 (env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"))
    logger.info(msg)
  end

  get '/' do
    logger.info("greet")
    "hi"
  end
end

Output of the logger:

I, [2016-09-27T02:50:07.824032 #50002]  INFO -- : GET / for ::1
I, [2016-09-27T02:50:07.824511 #50002]  INFO -- : greet

Note that this does not log status code of the response because it has not been decided in the before hook. You can log response information by using after do ... end hook, where you can use repsonse.status.

Alternative solution

Another solution is overriding the logger method to return your own logger. Actually this is mostly the same as the code above unless there is a library which writes something to env['rack.logger'] (I've never used such libraries though).

require 'logger'
require 'sinatra/base'

class SomeApp < Sinatra::Base
  # Override the default 'logger' method
  def logger
    @logger ||= Logger.new(#{settings.root}/log/#{settings.environment}.log")
  end

  before do
    query = env["QUERY_STRING"]
    msg = format("%s %s for %s",
                 env["REQUEST_METHOD"],
                 env["PATH_INFO"] + (query.empty? ? "" : "?#{query}"),
                 (env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"))
    logger.info(msg)
  end

  get '/' do
    logger.info("greet")
    "hi"
  end
end

More posts