Files
mtapi/Examples/MatLab/AdvancedExample/logging4matlab/+logging/logging.m
T
Christian_x7 76a6216cf7 Add a advanced example
It uses its own API which I packed between MTApi5 and MatLab.
Possible that this is useless, but errors from the .NET assembly are more tradable.
2020-10-24 10:14:58 +02:00

321 lines
8.9 KiB
Matlab

classdef logging < handle
%LOGGING Simple logging framework.
%
% Author:
% Dominique Orban <dominique.orban@gmail.com>
% Heavily modified version of 'log4m': http://goo.gl/qDUcvZ
%
properties (Constant)
ALL = int8(0);
TRACE = int8(1);
DEBUG = int8(2);
INFO = int8(3);
WARNING = int8(4);
ERROR = int8(5);
CRITICAL = int8(6);
OFF = int8(7);
colors_terminal = containers.Map(...
{'normal', 'red', 'green', 'yellow', 'blue', 'brightred'}, ...
{'%s', '\033[31m%s\033[0m', '\033[32m%s\033[0m', '\033[33m%s\033[0m', ...
'\033[34m%s\033[0m', '\033[1;31m%s\033[0m'});
level_colors = containers.Map(...
{logging.logging.INFO, logging.logging.ERROR, logging.logging.TRACE, ...
logging.logging.WARNING, logging.logging.DEBUG, logging.logging.CRITICAL}, ...
{'normal', 'red', 'green', 'yellow', 'blue', 'brightred'});
levels = containers.Map(...
{logging.logging.ALL, logging.logging.TRACE, logging.logging.DEBUG, ...
logging.logging.INFO, logging.logging.WARNING, logging.logging.ERROR, ...
logging.logging.CRITICAL, logging.logging.OFF}, ...
{'ALL', 'TRACE', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL', 'OFF'});
end
properties (SetAccess=immutable)
level_numbers;
level_range;
end
properties (SetAccess=protected)
name;
fullpath = 'logging.log'; % Default log file
logfmt = '%-23s %-40s %-8s %s\n'; % Default LogFormat
logfid = -1;
logcolors = logging.logging.colors_terminal;
using_terminal;
webhook;
instance;
end
properties (Hidden,SetAccess=protected)
datefmt_ = 'yyyy-mm-dd HH:MM:SS,FFF';
logLevel_ = logging.logging.INFO;
commandWindowLevel_ = logging.logging.INFO;
end
properties (Dependent)
datefmt;
logLevel;
commandWindowLevel;
end
methods(Static)
function [name, line] = getCallerInfo(self)
if nargin > 0 && self.ignoreLogging()
name = [];
line = [];
return
end
[ST, ~] = dbstack();
offset = min(size(ST, 1), 3);
name = ST(offset).name;
line = ST(offset).line;
end
end
methods
function setFilename(self, logPath)
[self.logfid, message] = fopen(logPath, 'a');
if self.logfid < 0
warning(['Problem with supplied logfile path: ' message]);
self.logLevel_ = logging.logging.OFF;
end
self.fullpath = logPath;
end
function setCommandWindowLevel(self, level)
self.commandWindowLevel = level;
end
function setLogLevel(self, level)
self.logLevel = level;
end
function setSlackWebHook(self, WebHook)
self.webhook = WebHook;
end
function setSlackInstance(self, Instance)
self.instance = Instance;
end
function tf = ignoreLogging(self)
tf = self.commandWindowLevel_ == self.OFF && self.logLevel_ == self.OFF;
end
function trace(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.TRACE, caller_name, message,sendtoslack);
end
function debug(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.DEBUG, caller_name, message,sendtoslack);
end
function info(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.INFO, caller_name, message,sendtoslack);
end
function warn(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.WARNING, caller_name, message,sendtoslack);
end
function error(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.ERROR, caller_name, message,sendtoslack);
end
function critical(self, message,varargin)
sendtoslack = 0;
if (nargin == 3 )
if varargin{1,1} == 1
sendtoslack = 1;
end
end
[caller_name, ~] = self.getCallerInfo(self);
self.writeLog(self.CRITICAL, caller_name, message,sendtoslack);
end
function self = logging(name, varargin)
levelkeys = self.levels.keys;
self.level_numbers = containers.Map(self.levels.values, levelkeys);
levelkeys = cell2mat(self.levels.keys);
self.level_range = [min(levelkeys), max(levelkeys)];
p = inputParser();
p.addRequired('name', @ischar);
p.addParameter('path', '', @ischar);
p.addParameter('logLevel', self.logLevel);
p.addParameter('commandWindowLevel', self.commandWindowLevel);
p.addParameter('datefmt', self.datefmt_);
p.parse(name, varargin{:});
r = p.Results;
self.name = r.name;
self.commandWindowLevel = r.commandWindowLevel;
self.datefmt = r.datefmt;
if ~isempty(r.path)
self.setFilename(r.path); % Opens the log file.
self.logLevel = r.logLevel;
else
self.logLevel_ = logging.logging.OFF;
end
% Use terminal logging if swing is disabled in matlab environment.
swingError = javachk('swing');
self.using_terminal = (~ isempty(swingError) && strcmp(swingError.identifier, 'MATLAB:javachk:thisFeatureNotAvailable')) || ~desktop('-inuse');
end
function delete(self)
if self.logfid > -1
fclose(self.logfid);
end
end
function writeLog(self, level, caller, message, sendtoslack)
level = self.getLevelNumber(level);
if self.commandWindowLevel_ <= level || self.logLevel_ <= level
timestamp = datestr(now, self.datefmt_);
levelStr = logging.logging.levels(level);
logline = sprintf(self.logfmt, timestamp, caller, levelStr, self.getMessage(message));
end
if sendtoslack
level = self.getLevelNumber(level);
timestamp = datestr(now, self.datefmt_);
levelStr = logging.logging.levels(level);
slacklogline = sprintf(self.logfmt, timestamp, caller, levelStr, self.getMessage(message));
SendSlackNotification(self.webhook,strcat(self.instance,' : ', slacklogline));
end
if self.commandWindowLevel_ <= level
if self.using_terminal
level_color = self.level_colors(level);
else
level_color = self.level_colors(logging.logging.INFO);
end
fprintf(self.logcolors(level_color), logline);
end
if self.logLevel_ <= level && self.logfid > -1
fprintf(self.logfid, logline);
end
end
function set.datefmt(self, fmt)
try
datestr(now(), fmt);
catch
error('Invalid date format');
end
self.datefmt_ = fmt;
end
function fmt = get.datefmt(self)
fmt = self.datefmt_;
end
function set.logLevel(self, level)
level = self.getLevelNumber(level);
if level > logging.logging.OFF || level < logging.logging.ALL
error('invalid logging level');
end
self.logLevel_ = level;
end
function level = get.logLevel(self)
level = self.logLevel_;
end
function set.commandWindowLevel(self, level)
self.commandWindowLevel_ = self.getLevelNumber(level);
end
function level = get.commandWindowLevel(self)
level = self.commandWindowLevel_;
end
end
methods (Hidden)
function level = getLevelNumber(self, level)
% LEVEL = GETLEVELNUMBER(LEVEL)
%
% Converts charecter-based level names to level numbers
% used internally by logging.
%
% If given a number, it makes sure the number is valid
% then returns it unchanged.
%
% This allows users to specify levels by name or number.
if isinteger(level) && self.level_range(1) <= level && level <= self.level_range(2)
return
else
level = self.level_numbers(level);
end
end
function message = getMessage(~, message)
if isa(message, 'function_handle')
message = message();
end
[rows, ~] = size(message);
if rows > 1
message = sprintf('\n %s', evalc('disp(message)'));
end
end
end
end