-
Notifications
You must be signed in to change notification settings - Fork 6
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
Handle exceptions raised during log message formatting #12
base: main
Are you sure you want to change the base?
Conversation
If the package attribute raiseExceptions is False, exceptions get silently ignored. This is what is mostly wanted for a logging system - most users will not care about errors in the logging system, they are more interested in application errors. ... (The default value of raiseExceptions is True, as that is more useful during development).
Ah, interesting! I had actually overlooked this possibility of formatMessage and the actual jLogger calls raising errors during the lazy message evaluation. Doing something to avoid that sounds like a good idea. Wrapping all these sections in try/catches that can quash the errors like you're doing here sounds like a good way to handle it. Though maybe that should be the only behavior SLF4M supports, and we shouldn't offer a raiseExceptions control at all. My concern is that would be a package-global setting that affects control flow. And as a library, SLF4M could be used by multiple client packages in the same contexts, which might have different expectations about the library behavior. Let's say you've got libraries Foo and Bar, each of which takes SLF4M as a dependency, and there's an application Banana which uses both Foo and Bar in the same Matlab session. Library Foo enables raiseExceptions, but Bar has some code like this: try
% ... do some work that writes data in a transaction ...
catch err
logging.error('Oops: %s %s %s', x, y, z);
rollback_transaction(); % required for data integrity!
rethrow(err);
end Now if Foo or Banana has enabled logging.raiseExceptions, Bar is going to corrupt some of its data. The raiseExceptions control would be useful for debugging, but not necessary I think: A developer could get similar behavior to But if raiseExceptions is something the Python logging library offers, they probably know what they're doing, and can hopefully rely on client libraries to just not do that. Let me do a bit of reading on how this works in the Python and Java logging frameworks. If we do provide raiseExceptions, its value should probably be false, since that's what users will generally want, especially in a production context. This is a behavior change from how SLF4M works now, but I think that's fine. We probably have a major-version release coming up soon anyway. There's a bit of concern about performance: try/catch introduces overhead, and the logging framework tries to be low overhead. But that's probably fine here: this try/catch happens after the If we do provide a raiseExceptions control, its state should probably be stored in a Matlab appdata(0) item instead of a persistent variable, so that its setting survives a Regardless, this lazy-eval error suppression sounds to me like something SLF4M should do. Thanks! |
(Even though raiseExceptions defaults to true in Python because that's useful for development, IMHO Matlab users tend to be less sophisticated at the software-development side of things, so I think a Matlab logging library should default to, or only provide, the "safe/best for production use" behavior.) |
Oh, and conversely: if raiseExceptions defaults to true, but is settable to false, some library or code using SLF4M might accidentally rely on that behavior for its control flow, and not realize that it might be turned off in a production scenario. Hmmm. |
Hmm. On the other other hand, maybe some client application will be using logging to output important information that they'll use in their business processes, and if a log message output fails in production code execution, they want that to raise an error so that job errors out and it sends an alert to their ops team. That's not something you can do with debugger breakpoints, so maybe SLF4M should provide a raiseExceptions control to support that scenario. And just put a big warning message on it that library code should not enable raiseExceptions itself; only end applications, developers during development, and deployers should enable raiseExceptions, and rely on developers to do the right thing. |
Also, I dunno about silently ignoring errors during message formatting. I think that error is something users would want to know about, so they know why log messages they were expecting didn't appear, or why something is failing mysteriously but there's no diagnostic log output. Especially because this isn't providing a generic error-hander hook like Python's logging module does. I need to read more about this before making a decision. But in the mean time, what do you think about this as the general form of the try/catch code in the logging methods? try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.info(msgStr);
catch err
errLogMsg = sprintf('ERROR: Caught an error during log message formatting. Error: %s', err.message);
this.jLogger.error(errLogMsg);
if logger.raiseExceptions
rethrow(err)
end
end That message formatting is pretty unlikely to error, since This ignores the case of the jLogger.error(...) call itself raising an exception, like if the log destination was unreachable due to an IO or permissions error. I don't even know if that's a thing that happens. And I think using Maybe this should instead throw a fresh exception, with a message that explicitly indicates that the error happened during SLF4M log message construction, and includes the original exception as a chained "cause" in the new exception. But I don't think we can actually do that in SLF4M, or at least would need to be clever about it, because the exception cause feature was introduced in a newer version of Matlab, and SLF4M wants to support Matlab releases going back to like R2015b. (I know "ME" is the name that MathWorks likes to use for exception variables, but to my ears that sounds like the word "me", which is like "this" or "self" or "obj", which are very different roles for a variable. I prefer "err" here.) |
more useful during development)." | ||
%} | ||
|
||
persistent flag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's back the flag value with appdata, so that its state survives a clear classes
or other action that clears the raiseException function definition. Something like this:
function out = raiseExceptions(newval)
persistent flag
if isempty(flag)
flag = getappdata(0, 'SLF4M_raiseExceptions');
if isempty(flag)
flag = true;
end
end
if nargin == 0
out = flag;
else
if ~(islogical(newval) && isscalar(newval))
error('The raiseExceptions value must be a scalar logical; got a %s %s', mat2str(size(newval)), class(newval));
end
flag = newval;
setappdata(0, 'SLF4M_raiseExceptions', flag);
end
end
(Oops: I was going to say that SLF4M's code base uses 2-space indents, but it looks like my code actually has a mix of 2-space or 4-space indents, on a per-file basis. And the .editorconfig
file says it's 4 spaces. I need to pick one and make it all uniform. #13)
4 responses :-)
(2)
(3)
(4)
|
But what if Bar has code like this:
This code relies on the logging framework's raiseExceptions being true for control flow, to ensure correctness. If Foo or Banana turns off raiseExceptions to "protect production", now this code sequence is going to end up writing incorrect data to the prod database. |
Motivation -- see https://stackoverflow.com/questions/66587941/what-happens-if-a-python-logging-handler-raises-an-exception
Offering a way to de-risk the lazy eval could increase acceptance.
If the package attribute raiseExceptions is False, exceptions get silently ignored. This is what is mostly wanted for a logging system - most users will not care about errors in the logging system, they are more interested in application errors. ... (The default value of raiseExceptions is True, as that is more useful during development).
This is implemented by