Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add levels to enabledLevels? #10

Open
ruck94301 opened this issue Aug 15, 2022 · 6 comments
Open

How to add levels to enabledLevels? #10

ruck94301 opened this issue Aug 15, 2022 · 6 comments
Assignees
Labels
documentation question Further information is requested

Comments

@ruck94301
Copy link

Suppose I'm using the functions...

logger.info('Something happened')
logger.debug('Something happened')

The info message is output, and the debug message is dropped, as expected.

What statement would I make to change the logging level so that logger.debug() was output instead of dropped?

Likewise, suppose

mylogger = logger.Logger.getLogger('foo.bar.baz.MyThing');
mylogger.debug('Something happened')

How to add 'debug' to enabledLevels?

@apjanke
Copy link
Member

apjanke commented Aug 15, 2022

Use the Configurator for the logging back-end! That's what sets which levels are enabled for which loggers in your session. The level settings are stored in a central location mapped to logger names (like 'foo.bar.baz.MyThing') instead of logger objects per se. The enabled-levels/isenabled() info on the Logger objects themselves is a read-only mechanism that just reflects that central state. This is for complicated technical reasons, mostly about how the logging facade/front-end interface (SLF4J) is decoupled from the logging backend (log4j/logback/etc) that does the actual logging.

The only logging back-end currently supported is log4j (because that's what Matlab ships with), so use the Log4JConfigurator class. Its setLevels method is what you want.

https://github.com/janklab/SLF4M/blob/master/Mcode/%2Blogger/Log4jConfigurator.m

In your case, I think it would be:

logger.Log4jConfigurator.setLevels({'foo.bar.baz.MyThing','DEBUG'});

and after that, the debug-level messages should be visible in your Matlab session.

@apjanke apjanke self-assigned this Aug 15, 2022
@apjanke apjanke added the question Further information is requested label Aug 15, 2022
@ruck94301
Copy link
Author

Thanks for the response; I see that this, with a logger object, works (*) just like you said.

           loggerobj = logger.Logger.getLogger('foo.bar.baz.MyThing');
           logger.Log4jConfigurator.setLevels({loggerobj.name,'INFO'});

           loggerobj.info('Something happened')
           loggerobj.debug('Something happened')  % suppressed
           loggerobj.trace('Something happened')  % suppressed

           logger.Log4jConfigurator.setLevels({loggerobj.name,'DEBUG'});

           loggerobj.info('Something happened again')
           loggerobj.debug('Something happened again')
           loggerobj.trace('Something happened again')  % suppressed

But if you're using the package functions like logger.info() & logger.info(), you don't have a good way to get the loggerobj.name or callerId that loggerCallImpl.m would have used... so if you want to manipulate levels that will affect package functions, I'm not seeing a good way to do that from inside matlab code. That's okay though, I'm okay with using the above instead of package functions.

(*) after a minor patch to Log4jConfigurator.m

old
   117                  logger = org.apache.log4j.LogManager.getLogger(logName);
   118                  level = logger.Log4jConfigurator.getLog4jLevel(levelName);
   119                  logger.setLevel(level);

new
   117                  loggerobj = org.apache.log4j.LogManager.getLogger(logName);
   118                  level = logger.Log4jConfigurator.getLog4jLevel(levelName);
   119                  loggerobj.setLevel(level);

@apjanke
Copy link
Member

apjanke commented Aug 26, 2022

That's intentional! The point of the high-level logging functions is that the code that is calling them logs to a logger with the same name as their class/package. And it defers all runtime logging output configuration to the application or consumer that is mixing all this code together. Logging output by libraries and reusable code is good. But logging configuration is supposed to be left to the final implementer, and SLF4M is designed to actively discourage or prevent log message sources from trying to muddle with logging output configuration.

This is a whole long conversation and philosophical argument.

BUT! Your patch in your last comment where you change a local variable name from logger to loggerobj to avoid masking the package name: Yep, that's an oopsie on my part. A while back I renamed the top-level SLF4M package, and there were somename collisions with existing variables, and this was one of them.

So, maybe this is a quick fix? Or maybe it's a big long architectural discussion. I'm not sure.

@ruck94301
Copy link
Author

ruck94301 commented Aug 30, 2022

Your comment is making me think...
There's (a) "mylib", the sw library that I'm authoring, (b) "bigsw", the matlab sw that uses mylib, & (c) SLF4M.
I agree the right thing is for bigsw ("final implementer") to configure logging, probably by slurping in a configfile and applying it.
But isn't it typical that in mylib I would also provide a default that I like, like
logger.Log4jConfigurator.setLevels({loggerObj.name,'DEBUG'})

Maybe not. I'm asking here, in case you're interested :)
https://stackoverflow.com/questions/73545376/in-python-logging-is-it-accepted-practice-to-setlevel-in-a-library-to-establi

@ruck94301
Copy link
Author

ruck94301 commented Sep 6, 2022

It seems my original position is untenable. It's been a week, and I'm not seeing any response to the stackoverflow posting that supports this position:

But isn't it typical that in mylib I would also provide a default that I favor, such as
logger.Log4jConfigurator.setLevels({loggerObj.name,'DEBUG'})

So now, I am reformed, and I no longer seek to set library default with setLevel.
And maybe the same principle applies to providing a customized message format?

(edit) P.S. I like apjanke's idea (below) that mylib can simply provide examples to assist the author of bigsw in configuring mylib use of SLF4M.

@apjanke
Copy link
Member

apjanke commented Sep 6, 2022

But isn't it typical that in mylib I would also provide a default that I like, like
logger.Log4jConfigurator.setLevels({loggerObj.name,'DEBUG'})

I don't know that it is. And my view is that it probably shouldn't be, at least by default as a result of loading and initializing the library. IMHO, "configuration" of logging like this - including setting levels, appenders, and so on - should be reserved for the "final implementer", whether that is the offer of "bigsw", or the IT staff of an organization that is deploying bigsw in their environment. It might be nice for mylib to provide some useful configuration settings via examples or tools - like, as examples in doco, or XML configuration files, or as a configureTerseLogging() or configureVerboseLoggingWithFullSqlTraces() method or something - but I think actually invoking those configurations should be left to the end user/implementer. Like, SLF4M provides a couple convenience methods like that do this (like Log4jConfigurator.configureBasicLogging here), but I think I'm not calling them by default, and it's up to the end-app author/deployer to invoke them. (Or if I'm doing so, that's a compromise in the name of user-friendliness to Matlab developers who aren't familiar with logging frameworks at all.)

I think that's how I've seen it work with other logging frameworks and the libraries that use them, except for the logging framework or intermediate library defaulting to a super-simple "output log to console, INFO level for everything" arrangement if no other configuration is supplied, as a "reasonable default".

BTW, sorry for my slow response here. It's been a week. Again.

The logger "facade" design

Oh, one other thing to consider is this: When you get a logger object like this using SLF4J or a similar logging "facade" framework (and SLF4M is one of those):

mylogger = logger.Logger.getLogger('foo.bar.baz.MyThing');

That mylogger is a simple "facade" that is a layer of indirection in front of the actual logging "back-end" library which controls the messages and provides the run-time configurability for setting which loggers are at what levels, and where output goes and in what format. That mylogger object doesn't even have an interface for modifying those "knobs" that control the levels. It serves as a "drop-off" point where you can pass in log messages and say what the logger name and message level that message is. But it intentionally doesn't provide controls for the client code here to control or configure what that logger and its delegates are actually going to do with that log message.

SLF4M is a little weird in that it provides a logging facade API like this for log-emitting code to send log messages with. And it also provides an interface for configuring the log4j back-end to tell it what to do with those messages (by setting output levels and destinations). But that's because Matlab doesn't have either of those facilities in the first place, and it was simpler to combine them in one library. Those two aspects of SLF4M are intended to be used by different audiences: the message-sending stuff is for library and application code to use to send/emit log messages, and the "Configurator" stuff is for the final deployer/end-applicat-writer to use to control where those messages end up.

I should improve the SLF4M documentation on this front.

@apjanke apjanke changed the title how to add levels to enabledLevels? How to add levels to enabledLevels? Sep 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants