Sinatra: Logging from Classes and Modules doesn't work as expected.

Created on 29 Jan 2017  路  8Comments  路  Source: sinatra/sinatra

Starting first with a classic application, and splitting the models/libs in to folders that are included/loaded.

When specifying the logger in the main file:

configure do
  logfile = File.new("#{settings.root}/log/#{settings.environment}.log", 'a+')
  logfile.sync = true
  use Rack::CommonLogger, logfile
  set :logger, Logger.new(logfile)
end

Calling logger.debug from a module or class returns NameError: undefined local variable or method `logger' for <Object or Module>

Most helpful comment

Rack loggers are really only available in the request scope of the application, and Sinatra's logger helper method is only available within the Sinatra application class where logging was configured.

Sinatra's DSL and method delegation make it look like magic, but you have to remember that there is always a class (Sinatra::Application, or a subclass of Sinatra::Application or Sinatra::Base) behind the scenes holding the methods and behavior that make up a Sinatra application. Consider this simple example:

class Foo
  def one; 1 end
end

class Bar
  def two
    one + one
  end
end

Bar.new.two

Would you expect this to work? No, I hope not, because we haven't told Bar how to access Foo#one. The two classes and their instance methods are completely isolated from one another. This is a core tenant of Ruby and many other object-oriented programming languages. So you shouldn't expect Artefact#return_error_via_log to be able to access Sinatra::Application#logger, either!

You have a few options on how to proceed here. Probably the easiest and most common solution would be to create a separate logger to use in your models and wherever else. You can keep a handle to the logging object in a global or a constant, or use some dependency injection scheme. Then you have the option of either using that logger in your Sinatra application for requests and/or other messages or accepting Sinatra's default behavior.

All 8 comments

Can you show more code that clarifies what's in your modules and how you're including them in the main application?

There's a lot of code here, would it be possible for you to distill it into a smaller test case?

The essence is this:

  • notesapp_sinatra.rb brings in models with require_relative or load (tried both, and am open to suggestions)
  • Models are not subclassed
  • Calling the logger from notesapp_sinatra.rb works
  • Calling the logger from any model does not work (gives the NameError above)

Rack loggers are really only available in the request scope of the application, and Sinatra's logger helper method is only available within the Sinatra application class where logging was configured.

Sinatra's DSL and method delegation make it look like magic, but you have to remember that there is always a class (Sinatra::Application, or a subclass of Sinatra::Application or Sinatra::Base) behind the scenes holding the methods and behavior that make up a Sinatra application. Consider this simple example:

class Foo
  def one; 1 end
end

class Bar
  def two
    one + one
  end
end

Bar.new.two

Would you expect this to work? No, I hope not, because we haven't told Bar how to access Foo#one. The two classes and their instance methods are completely isolated from one another. This is a core tenant of Ruby and many other object-oriented programming languages. So you shouldn't expect Artefact#return_error_via_log to be able to access Sinatra::Application#logger, either!

You have a few options on how to proceed here. Probably the easiest and most common solution would be to create a separate logger to use in your models and wherever else. You can keep a handle to the logging object in a global or a constant, or use some dependency injection scheme. Then you have the option of either using that logger in your Sinatra application for requests and/or other messages or accepting Sinatra's default behavior.

Thank you for this comprehensive response.
This actually fills in a lot of other small gaps as I had assumed that Sinatra was the superclass of everything within classic applications.

Are there any straightforward examples that flesh out the different levels of Sinatra's scopes? Having whipped up a number of simple applications without any show-stoppers, it's just a matter of wrapping my head around this. The documentation looks good, but packed.

None that I know of. The documentation is the best resource. Yes, it is packed, but your understanding will deepen each time you read through it as you learn Sinatra.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aren55555 picture aren55555  路  7Comments

fullofcaffeine picture fullofcaffeine  路  7Comments

paulmenzel picture paulmenzel  路  4Comments

za3k picture za3k  路  5Comments

namusyaka picture namusyaka  路  3Comments