0

Possible Duplicate:
Get client’s IP address in Sinatra?

I'm using Sinatra with Apache and Passenger.

I'm currently using the following logger in my config.ru:

LOGGER = Logger.new("logs/sinatra.log")

I can log things in my app using:

LOGGER.error "Log msg"

An example log entry looks like this:

E, [2013-01-18T19:43:41.857146 #19412] ERROR -- : Log msg

How can I put the IP of the current user into the log, so it might look like this:

E, [2013-01-18T19:43:41.857146 #19412] ERROR -- : <127.0.0.1> Log msg

I could write:

LOGGER.error "<#{request.ip}> Log msg"

But, I want to have it on every log message, so always prepending the IP manually seems to be the wrong way. How can the LOGGER be configured to parse the request.ip automatically and to put it into the log?


I tried it the following way as suggested in some answers:

configure.ru:

configure do
    LOGGER = Logger.new("logs/sinatra.log")
    original_formatter = Logger::Formatter.new
    LOGGER.formatter = proc { |severity, datetime, progname, msg|
        original_formatter.call(severity, datetime, progname, "#{ip} #{msg.dump}")
    }
end

app.rb:

helpers do
    def ip
        request.ip
    end
end

get '/' do
    LOGGER.info 'test'
end

This is the result:

NameError: undefined local variable or method `ip' for #<Rack::Builder:0x00000001821c60>

Update Based on theTinMan's answer I came up with the following helper:

helpers do
    def log(call,msg = '')
        severity = Logger.const_get(call.upcase)
        return if LOGGER.level > severity

        msg = yield if block_given?
        LOGGER.send(call, "<#{request.ip}> #{msg}")
    end
end

So I can do this:

log :info, 'msg'
log(:debug) { very.expensive.operation }

This looks like the solution I wanted. Is there room for improvement?

Community
  • 1
  • 1
Markus
  • 5,667
  • 4
  • 48
  • 64
  • There is no guarantee that the IP available is valid, because of proxies, NAT, etc. – the Tin Man Jan 19 '13 at 05:45
  • possible duplicate of [Simple and Ideal Logging in Sinatra](http://stackoverflow.com/questions/9644753/simple-and-ideal-logging-in-sinatra), [Sinatra Logger for Web Service Errors](http://stackoverflow.com/q/8716595/128421), [Logging in Sinatra](http://stackoverflow.com/q/5995854/128421) and [Get client's IP address in Sinatra?](http://stackoverflow.com/questions/1319331/get-clients-ip-address-in-sinatra) – the Tin Man Jan 19 '13 at 06:51
  • @theTinMan but some ip address must be there, since something triggered a request. that should be logged. And in the threads you linked I see no way to have the ip outputted by default. – Markus Jan 19 '13 at 09:57
  • The threads linked are a combination of various ways to use LOGGER, and how to get `request.ip`. From those you can easily put together a solution. You can't expect a custom-tailored answer. – the Tin Man Jan 19 '13 at 18:52
  • Additional useful answers are: [Use Rack::CommonLogger in Sinatra](http://stackoverflow.com/questions/2239240/use-rackcommonlogger-in-sinatra) and [Where does RACK log to?](http://stackoverflow.com/questions/2366352/where-does-rack-log-to). – the Tin Man Jan 19 '13 at 18:56
  • Change `def log(call,msg = '')` to `def log(call=:info, msg = '')` to default to `:info` level. I'd consider reversing the order of the parameters, because you *ALWAYS* want a message and defaulting to `:info` is reasonable. I see the value in using a block to return the message though, so it's a mixed-bag. – the Tin Man Jan 19 '13 at 19:00
  • Some address would be there, but it could be wildly different than the address the request actually came from, especially when people are deliberately trying to hide their IP. – the Tin Man Jan 19 '13 at 19:04
  • I don't see this being a duplicate, since the question is not about how to get the ip address, but how to always log it. – Markus Jan 24 '13 at 10:42

2 Answers2

1

See "Simple and Ideal Logging in Sinatra":

I tend to use something like:

require 'sinatra'
use Rack::Logger

def log(msg)
  logger.info('%s: %s' % [request.ip, msg])
end

get '/' do
  log("Request for '/'")
  '<html><body>psych</body></html>'
end

Which outputs:

I, [2013-01-18T23:39:42.049072 #81011]  INFO -- : 127.0.0.1: Request for '/'
127.0.0.1 - - [18/Jan/2013 23:39:42] "GET / HTTP/1.1" 200 31 0.0034

I could sub-class Logger, or to patch it, but I prefer wrapping the logger call in a local method. We use that in several apps at work, and it works nicely.

Community
  • 1
  • 1
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • where do these logs go to? Can this be a seperate logfile (`logs/sinatra.log`) besides of the regular output? and I would have to define a method for each severity (`info error warn` etc) or to make the method accept two arguments (severity + msg), so something like `log(:info,'msg')` would work ... okey, is an idea. – Markus Jan 19 '13 at 09:54
  • added a helper based on your answer to my question. do you see room for improvement? – Markus Jan 19 '13 at 10:29
-1

You can override the formatter for Logger. See http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html for an example. You can create a sinatra helper method to return the request IP, so that it can be called from the formatter proc.

aagdbl
  • 99
  • 4