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

Handle exceptions raised during log message formatting #12

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 40 additions & 10 deletions Mcode/+logger/Logger.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,44 +106,74 @@ function error(this, msg, varargin)
if ~this.jLogger.isErrorEnabled()
return
end
msgStr = formatMessage(msg, varargin{:});
this.jLogger.error(msgStr);
try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.error(msgStr);
catch ME
if logger.raiseExceptions
throw(ME)
end
end
end

function warn(this, msg, varargin)
% Log a message at the WARN level.
if ~this.jLogger.isWarnEnabled()
return
end
msgStr = formatMessage(msg, varargin{:});
this.jLogger.warn(msgStr);
try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.warn(msgStr);
catch ME
if logger.raiseExceptions
throw(ME)
end
end
end

function info(this, msg, varargin)
% Log a message at the INFO level.
if ~this.jLogger.isInfoEnabled()
return
end
msgStr = formatMessage(msg, varargin{:});
this.jLogger.info(msgStr);
try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.info(msgStr);
catch ME
if logger.raiseExceptions
throw(ME)
end
end
end

function debug(this, msg, varargin)
% Log a message at the DEBUG level.
if ~this.jLogger.isDebugEnabled()
return
end
msgStr = formatMessage(msg, varargin{:});
this.jLogger.debug(msgStr);
try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.debug(msgStr);
catch ME
if logger.raiseExceptions
throw(ME)
end
end
end

function trace(this, msg, varargin)
% Log a message at the TRACE level.
if ~this.jLogger.isTraceEnabled()
return
end
msgStr = formatMessage(msg, varargin{:});
this.jLogger.trace(msgStr);
try
msgStr = formatMessage(msg, varargin{:});
this.jLogger.trace(msgStr);
catch ME
if logger.raiseExceptions
throw(ME)
end
end
end

function errorj(this, msg, varargin)
Expand Down
46 changes: 46 additions & 0 deletions Mcode/+logger/raiseExceptions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
function varargout = raiseExceptions(newval)
%RAISEEXCEPTIONS gets/sets a flag for handling exceptions during logging
% RAISEEXCEPTIONS gets or sets a flag that is used by the logic that
% controls the handling of exceptions raised/thrown during logging.
% The default flag value is true.
%
% Usage
% % suppress exceptions during logging framework message handling
% logger.raiseExceptions(false)

%{
This mechanism is inspired by a comment from Martijn Pieters in
https://stackoverflow.com/questions/66587941/what-happens-if-a-python-logging-handler-raises-an-exception

"The Python standard-library handlers have been build with robustness in
mind. If an exception is raised ..., all standard library
implementations catch the exception and ...
By default, ... re-raises the exception. In production systems you want
to set logging.raiseExceptions = False, at which point the exceptions
are silently ignored, and logging continues as if nothing happened.

From the documentation: ... If the module-level 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)."
%}

persistent flag
Copy link
Member

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)


if isempty(flag)
flag = true;
end

switch nargin
case 0 % get
varargout{1} = flag;
case 1 % set
assert(isscalar(newval) & islogical(newval), 'bad usage')
flag = newval;
otherwise
assert(false, 'impossible')
end

end
12 changes: 12 additions & 0 deletions Mcode/+logger/throwExceptions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function varargout = throwExceptions(varargin)
%THROWEXCEPTIONS is an alias for RAISEEXCEPTIONS
% RAISEEXCEPTIONS is inspired by the Python standard library
% framework.
% In MATLAB, exceptions are 'thrown' rather than 'raised', so
% THROWEXCEPTIONS is provided as a more MATLAB-consistent alias.
%
% Usage
% % suppress exceptions during logging framework message handling
% logger.throwExceptions(false)

[varargout{1:nargout}] = logger.raiseExceptions(varargin{:});