ISEPOS-2280 Added base loggers scripts #1

Open
ymlesni wants to merge 8 commits from ymlesni/shared-snippets:feature/ISEPOS-2280-mechanizm-raportowania-informacji-z-aplikacji-do-pliku-z-logami into main
3 changed files with 277 additions and 0 deletions
Showing only changes of commit 9c654357c8 - Show all commits

View File

@ -0,0 +1,111 @@
%
% -----------------
% Copyright © 2024 ACK Cyfronet AGH, Poland.
% -----------------
%
% BASE_LOGGER A singleton logger class for logging messages to a file.
%
% This class implements a simple logging mechanism with different log levels
% (TRACE, DEBUG, INFO, WARNING, ERROR) and writes log entries to a file.
% It follows the Singleton pattern to ensure that only one instance of the logger
% exists throughout the application.
%
% Properties:
% fid - (private) The file identifier for the log file. This is used to write logs.
%
% Methods:
%
% getInstance() - Retrieves the singleton instance of the logger.
%
% trace(message) - Logs a message with TRACE level.
% @param message The message to log.
%
% debug(message) - Logs a message with DEBUG level.
% @param message The message to log.
%
% info(message) - Logs a message with INFO level.
% @param message The message to log.
%
% warning(message) - Logs a message with WARNING level.
% @param message The message to log.
%
% error(message) - Logs a message with ERROR level.
% @param message The message to log.
%
% delete() - Destructor method that closes the file identifier when the logger is deleted.
%
% Example usage:
% logger = base_logger.getInstance();
% logger.info('This is an info message');
% logger.error('This is an error message');
Outdated
Review

Can we put here an example with error object? So that the stack trace of the exception gets logged.

Can we put here an example with error object? So that the stack trace of the exception gets logged.

Base logger has been enchanced and example for error logging has been added.

Base logger has been enchanced and example for error logging has been added.
%
% See also: fopen, fclose, dbstack, fprintf
classdef base_logger < handle
properties(Access=private)
fid;
end
methods(Static, Access = public)
function obj = getInstance()
persistent instance;
if isempty(instance)
instance = base_logger();
end
obj = instance;
end
end
methods(Access=private)
function this = base_logger()
this.fid = fopen(getenv("DEFAULT_LOG_PATH"), 'a');
if this.fid == -1
error('Failed to open log file: application.log');
end
end
function log(this, level, message)
current_time = datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss');
asia marked this conversation as resolved Outdated
Outdated
Review

Can we use a different name? Something that we would consider a log file at first glance ('fallbackPath' is something I wouldn't) - if we don't want to make it too simple, to avoid name collision with other things, maybe e.g. base-app-log.log or base-logger-log.log or something like that

Can we use a different name? Something that we would consider a log file at first glance ('fallbackPath' is something I wouldn't) - if we don't want to make it too simple, to avoid name collision with other things, maybe e.g. base-app-log.log or base-logger-log.log or something like that
stack = dbstack('-completenames');
if length(stack) > 2
script_name = stack(3).name;
else
script_name = 'Unknown';
end
if this.fid ~= -1
fprintf(this.fid, '%s %s %s %s\n', char(current_time), level, script_name, message);
else
error('File identifier is invalid. Cannot write to log.');
end
end
end
methods(Access=public)
function trace(this, message)
this.log("TRACE", message);
end
function debug(this, message)
this.log("DEBUG", message);
end
function info(this, message)
this.log("INFO", message);
end
function warning(this, message)
this.log("WARNING", message);
end
function error(this, message)
this.log("ERROR", message);
end
function delete(this)
if this.fid ~= -1
fclose(this.fid);
end
end
end
end

View File

@ -0,0 +1,111 @@
%
% -----------------
% Copyright © 2024 ACK Cyfronet AGH, Poland.
% -----------------
%
% BASE_LOGGER A singleton logger class for logging messages to a file.
%
% This class implements a simple logging mechanism with different log levels
% (TRACE, DEBUG, INFO, WARNING, ERROR) and writes log entries to a file.
% It follows the Singleton pattern to ensure that only one instance of the logger
% exists throughout the application.
%
% Properties:
% fid - (private) The file identifier for the log file. This is used to write logs.
%
% Methods:
%
% getInstance() - Retrieves the singleton instance of the logger.
%
% trace(message) - Logs a message with TRACE level.
% @param message The message to log.
%
% debug(message) - Logs a message with DEBUG level.
% @param message The message to log.
%
% info(message) - Logs a message with INFO level.
% @param message The message to log.
%
% warning(message) - Logs a message with WARNING level.
% @param message The message to log.
%
% error(message) - Logs a message with ERROR level.
% @param message The message to log.
%
% delete() - Destructor method that closes the file identifier when the logger is deleted.
%
% Example usage:
% logger = base_logger.getInstance();
% logger.info('This is an info message');
% logger.error('This is an error message');
%
% See also: fopen, fclose, dbstack, fprintf
classdef base_logger < handle
properties(Access=private)
fid;
end
methods(Static, Access = public)
function obj = getInstance()
persistent instance;
if isempty(instance)
instance = base_logger();
end
obj = instance;
end
end
methods(Access=private)
function this = base_logger()
this.fid = fopen(getenv("DEFAULT_LOG_PATH"), 'a');
if this.fid == -1
error('Failed to open log file: application.log');
end
end
function log(this, level, message)
current_time = strftime('%Y-%m-%d %H:%M:%S', localtime(time()));
stack = dbstack('-completenames');
if length(stack) > 2
script_name = stack(3).name;
else
script_name = 'Unknown';
end
if this.fid ~= -1
fprintf(this.fid, '%s %s %s %s\n', char(current_time), level, script_name, message);
else
error('File identifier is invalid. Cannot write to log.');
end
end
end
methods(Access=public)
function trace(this, message)
this.log("TRACE", message);
end
function debug(this, message)
this.log("DEBUG", message);
end
function info(this, message)
this.log("INFO", message);
end
function warning(this, message)
this.log("WARNING", message);
end
function error(this, message)
this.log("ERROR", message);
end
function delete(this)
if this.fid ~= -1
fclose(this.fid);
end
end
end
end

View File

@ -0,0 +1,55 @@
#
# -----------------
# Copyright © 2024 ACK Cyfronet AGH, Poland.
# -----------------
#
import os
import logging
def getDefaultLogger(name):
"""
Retrieves or creates a logger with the specified name and sets it up with a file handler.
The logger is configured to write log messages to the file path specified by the
'DEFAULT_LOG_PATH' environment variable. It uses the 'INFO' level as the default
Outdated
Review

I think the name was changed to 'APP_LOG_FILE'

I think the name was changed to 'APP_LOG_FILE'
logging level and writes log entries in the following format:
'YYYY-MM-DD HH:MM:SS,ms LEVEL logger_name message'
If the logger does not already have handlers, a file handler is created, and the
logging output is appended to the file. The log format includes the timestamp with
milliseconds, log level, logger name, and the log message.
Parameters:
-----------
name : str
The name of the logger. This can be the name of the module or any identifier
that you want to associate with the logger.
Returns:
--------
logger : logging.Logger
A logger instance with the specified name. The logger is configured with a
file handler that writes to the file specified by the 'DEFAULT_LOG_PATH'
environment variable.
Raises:
-------
KeyError:
If the 'DEFAULT_LOG_PATH' environment variable is not set.
Review

Here, again, the name of the variable should be updated. But I thought also that instead of throwing an error, we might use some default file name, to allow users to run the app also outside of the platform. But, I would set the default log file name to something different than "application.log", so that we know that we could notice if something is wrong with setting the environment variable.

Here, again, the name of the variable should be updated. But I thought also that instead of throwing an error, we might use some default file name, to allow users to run the app also outside of the platform. But, I would set the default log file name to something different than "application.log", so that we know that we could notice if something is wrong with setting the environment variable.
Example:
--------
logger = getDefaultLogger(__name__)
logger.info("This is an info message.")
Outdated
Review

An example with an error with logging stack trace would be convenient here

An example with an error with logging stack trace would be convenient here

Example for error with stack trace logging has been added.

Example for error with stack trace logging has been added.
"""
Outdated
Review

Maybe just put here some placeholder meaning that this is a code that may produce an error

Maybe just put here some placeholder meaning that this is a code that may produce an error

Ok, I will do the same for Matlab and Octave versions.

Ok, I will do the same for Matlab and Octave versions.
logger = logging.getLogger(name)
if not logger.hasHandlers():
Outdated
Review

We have except Exception as e but the e is not used anywhere... is this correct?

We have `except Exception as e` but the `e` is not used anywhere... is this correct?

Maybe it is good to use here the logger.exception() (doc) method as a showcase.

Maybe it is good to use here the `logger.exception()` ([doc](https://docs.python.org/3/library/logging.html#logging.Logger.exception)) method as a showcase.

e could be used to process exception further, but it is not necessary for example, I will remove it.
Would adding logger.exception() as a additional option be a good idea (to still showcase how to log exception at chosen log level)? For now I will change to logger.exception().

`e` could be used to process exception further, but it is not necessary for example, I will remove it. Would adding `logger.exception()` as a additional option be a good idea (to still showcase how to log exception at chosen log level)? For now I will change to `logger.exception()`.
file_handler = logging.FileHandler(os.environ['DEFAULT_LOG_PATH'], mode='a')
formatter = logging.Formatter('%(asctime)s,%(msecs)d %(levelname)s %(name)s %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.setLevel(logging.INFO)
return logger