mirror of
https://github.com/dlang/phobos.git
synced 2025-05-05 01:20:40 +03:00

Abstract methods have to be marked 'abstract', or the compiler wont complain about missing implementations, but instead assume they will be in some .o file that is later passed to the linker. Made reads/writes to globalLogLevel atomic and separated global log level from stdlog's log level. (Had to add a cast to the first array element of LogLevel arrays in foreach(). Possibly a compiler bug in 2.066 master.) Avoid races with reading/writing the standard Logger: * User code can no longer directly read out the stdlog, because by the time it has the reference, it might already be obsolete. (Later an `opApply` style function could be provided to execute functions/delegates in the context of a locked stdlog.) * The stdlog property was split up into a public setter `stdlog` and a package getter `stdlogImpl`, because the compiler doesn't like different visibility for getter and setter. * All module level functions dealing with the stdlog now synchronize with the global `__stdloggermutex`. static assert(__ctfe) doesn't do what one might expect. Code is always generated and emitted for `isLoggingActive` and it can be called at runtime. This commit turns the template function into just a template `isLoggingActiveAt` and a global bool flag `isLoggingActive`, so they can both be evaluated without parenthesis now and don't end up as functions in the final executable. Fix: Unittesting symbols like randomString() are visible outside of the logger package. Changed Logger methods to `final`, that are not documented as overridable to gain more control over the implementation details when I implement thread safety measures. Made `fatalHandler` accessible as a property to allow synchronization in later commits. Also made `writeLogMsg` protected, so it can assume it is properly synchronized already by the public calls calling it. To log a pre-built message `forwardMsg` can now be used. (See `FileLogger`). Fixed my mistake, where setting `stdlog` before querying `stdlogImpl` would revert the setting. And while I was at it, I changed `stdlog`s behavoir on assignment of `null`: It will now put the default logger back in place. First attempt on making logging thread safe: Logger now has a mutex to protect its public API. The module level functions don't perform any runtime checks themselves to avoid races. Instead the logger does all checking under protection of its mutex. `globalLogLevel` will be evaluated only once for any public logging call and thus needs no global locking. All overrides of `writeLogMsg`, `beginLogMsg`, `logMsgPart` and `finishLogMsg` have `protected` visibility now, since they expect their logger to be already under mutex protection and must not be publically visible. Fixed `MultiLogger` to not perform checks for its children to avoid races. Small fix for `StdLoggerDisableLogging`. some more unittests and some fixes doc fixes doc fix MrSmith33 Array problem stupid me stupid me The getter for Logger.logLevel now uses an `atomicLoad` instead of full mutex lock. Read-access to `stdlog` is now back... Fixed inverted DDoc meaning for `isLoggingActive`. Removed synchronization with a global lock for the duration of any standard logging call in an exchange of sequential consinstency for throughput. Rejects valid: logf(LogLevel.fatal, "I am %s", true); (The template constraint is not required in this case.) Fix for `template isLoggingActive` that would break if an actual log level was disabled.
2804 lines
99 KiB
D
2804 lines
99 KiB
D
/**
|
|
Implements logging facilities.
|
|
|
|
Message logging is a common approach to expose runtime information of a
|
|
program. Logging should be easy, but also flexible and powerful, therefore
|
|
$(D D) provides a standard interface for logging.
|
|
|
|
The easiest way to create a log message is to write
|
|
$(D import std.logger; log("I am here");) this will print a message to the
|
|
$(D stderr) device. The message will contain the filename, the linenumber, the
|
|
name of the surrounding function, the time and the message.
|
|
|
|
Copyright: Copyright Robert "burner" Schadek 2013 --
|
|
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
|
Authors: $(WEB http://www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek)
|
|
|
|
-------------
|
|
log("Logging to the stdlog with its default LogLevel");
|
|
logf(LogLevel.info, 5 < 6, "%s to the stdlog with its LogLevel.info", "Logging");
|
|
info("Logging to the stdlog with its info LogLevel");
|
|
warning(5 < 6, "Logging to the stdlog with its LogLevel.warning if 5 is less than 6");
|
|
error("Logging to the stdlog with its error LogLevel");
|
|
errorf("Logging %s the stdlog %s its error LogLevel", "to", "with");
|
|
critical("Logging to the"," stdlog with its error LogLevel");
|
|
fatal("Logging to the stdlog with its fatal LogLevel");
|
|
|
|
auto fLogger = new FileLogger("NameOfTheLogFile");
|
|
fLogger.log("Logging to the fileLogger with its default LogLevel");
|
|
fLogger.info("Logging to the fileLogger with its default LogLevel");
|
|
fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6");
|
|
fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less");
|
|
fLogger.critical("Logging to the fileLogger with its info LogLevel");
|
|
fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6");
|
|
fLogger.fatal("Logging to the fileLogger with its warning LogLevel");
|
|
-------------
|
|
|
|
Top-level calls to logging-related functions go to the default $(D Logger)
|
|
object called $(D stdlog).
|
|
$(LI $(D log))
|
|
$(LI $(D trace))
|
|
$(LI $(D info))
|
|
$(LI $(D warning))
|
|
$(LI $(D critical))
|
|
$(LI $(D fatal))
|
|
The default $(D Logger) will by default log to $(D stderr) and has a default
|
|
$(D LogLevel) of $(D LogLevel.all). The default Logger can be accessed by
|
|
using the property called $(D stdlog). This property a reference to the
|
|
current default $(D Logger). This reference can be used to assign a new
|
|
default $(D Logger).
|
|
-------------
|
|
stdlog = new FileLogger("New_Default_Log_File.log");
|
|
-------------
|
|
|
|
Additional $(D Logger) can be created by creating a new instance of the
|
|
required $(D Logger).
|
|
|
|
The $(D LogLevel) of an log call can be defined in two ways. The first is by
|
|
calling $(D log) and passing the $(D LogLevel) explicit as the first argument.
|
|
The second way of setting the $(D LogLevel) of a
|
|
log call, is by calling either $(D trace), $(D info), $(D warning),
|
|
$(D critical), or $(D fatal). The log call will than have the respective
|
|
$(D LogLevel). If no $(D LogLevel) is defined the log call will use the
|
|
current $(D LogLevel) of the used $(D Logger). If data is logged with
|
|
$(D LogLevel) $(D fatal) by default an $(D Error) will be thrown.
|
|
This behaviour can be modified by using the member $(D fatalHandler) to
|
|
assign a custom delegate to handle log call with $(D LogLevel) $(D fatal).
|
|
|
|
Conditional logging can be achieved be appending passing a $(D bool) as first
|
|
argument to a log function. If conditional logging is used the condition must
|
|
be $(D true) in order to have the log message logged.
|
|
|
|
In order to combine an explicit $(D LogLevel) passing with conditional
|
|
logging, the $(D LogLevel) has to be passed as first argument followed by the
|
|
$(D bool).
|
|
|
|
Messages are logged if the $(D LogLevel) of the log message is greater than or
|
|
equal to than the $(D LogLevel) of the used $(D Logger) and additionally if the
|
|
$(D LogLevel) of the log message is greater equal to the global $(D LogLevel).
|
|
If a condition is passed into the log call, this condition must be true.
|
|
|
|
The global $(D LogLevel) is accessible by using $(D globalLogLevel).
|
|
To assign the $(D LogLevel) of a $(D Logger) use the $(D logLevel) property of
|
|
the logger.
|
|
|
|
If $(D printf)-style logging is needed add a $(B f) to the logging call, such as
|
|
$(D myLogger.infof("Hello %s", "world");) or $(fatalf("errno %d", 1337))
|
|
The additional $(B f) enables $(D printf)-style logging for call combinations of
|
|
explicit $(D LogLevel) and conditional logging functions and methods.
|
|
|
|
To customize the $(D Logger) behavior, create a new $(D class) that inherits from
|
|
the abstract $(D Logger) $(D class), and implements the $(D writeLogMsg)
|
|
method.
|
|
-------------
|
|
class MyCustomLogger : Logger
|
|
{
|
|
this(string newName, LogLevel lv) @safe
|
|
{
|
|
super(newName, lv);
|
|
}
|
|
|
|
override void writeLogMsg(ref LogEntry payload)
|
|
{
|
|
// log message in my custom way
|
|
}
|
|
}
|
|
|
|
auto logger = new MyCustomLogger();
|
|
logger.log("Awesome log message");
|
|
-------------
|
|
|
|
To gain more precise control over the logging process, additionally to
|
|
overwriting the $(D writeLogMsg) method the methods $(D beginLogMsg),
|
|
$(D logMsgPart) and $(D finishLogMsg) can be overwritten.
|
|
|
|
In order to disable logging at compile time, pass $(D StdLoggerDisableLogging) as a
|
|
version argument to the $(D D) compiler when compiling your program code.
|
|
This will disable all logging functionality.
|
|
Specific $(D LogLevel) can be disabled at compile time as well.
|
|
In order to disable logging with the $(D trace) $(D LogLevel) pass
|
|
$(D StdLoggerDisableTrace) as a version.
|
|
The following table shows which version statement disables which
|
|
$(D LogLevel).
|
|
$(TABLE
|
|
$(TR $(TD $(D LogLevel.trace) ) $(TD StdLoggerDisableTrace))
|
|
$(TR $(TD $(D LogLevel.info) ) $(TD StdLoggerDisableInfo))
|
|
$(TR $(TD $(D LogLevel.warning) ) $(TD StdLoggerDisableWarning))
|
|
$(TR $(TD $(D LogLevel.error) ) $(TD StdLoggerDisableError))
|
|
$(TR $(TD $(D LogLevel.critical) ) $(TD StdLoggerDisableCritical))
|
|
$(TR $(TD $(D LogLevel.fatal) ) $(TD StdLoggerDisableFatal))
|
|
)
|
|
Such a version statement will only disable logging in the associated compile
|
|
unit.
|
|
|
|
By default four $(D Logger) implementations are given. The $(D FileLogger)
|
|
logs data to files. It can also be used to log to $(D stdout) and $(D stderr)
|
|
as these devices are files as well. A $(D Logger) that logs to $(D stdout) can
|
|
therefore be created by $(D new FileLogger(stdout)).
|
|
The $(D MultiLogger) is basically an associative array of $(D string)s to
|
|
$(D Logger). It propagates log calls to its stored $(D Logger). The
|
|
$(D ArrayLogger) contains an array of $(D Logger) and also propagates log
|
|
calls to its stored $(D Logger). The $(D NullLogger) does not do anything. It
|
|
will never log a message and will never throw on a log call with $(D LogLevel)
|
|
$(D error).
|
|
*/
|
|
module std.experimental.logger.core;
|
|
|
|
import std.array;
|
|
import std.stdio;
|
|
import std.conv;
|
|
import std.datetime;
|
|
import std.string;
|
|
import std.range;
|
|
import std.traits;
|
|
import std.exception;
|
|
import std.concurrency;
|
|
import std.format;
|
|
import core.atomic;
|
|
import core.sync.mutex : Mutex;
|
|
|
|
import std.experimental.logger.filelogger;
|
|
|
|
shared static this() {
|
|
__defaultLoggerMutex = new Mutex;
|
|
}
|
|
|
|
/** This template evaluates if the passed $(D LogLevel) is active.
|
|
The previously described version statements are used to decide if the
|
|
$(D LogLevel) is active. The version statements only influence the compile
|
|
unit they are used with, therefore this function can only disable logging this
|
|
specific compile unit.
|
|
*/
|
|
template isLoggingActiveAt(LogLevel ll)
|
|
{
|
|
version (StdLoggerDisableLogging)
|
|
{
|
|
enum isLoggingActiveAt = false;
|
|
}
|
|
else
|
|
{
|
|
static if (ll == LogLevel.trace)
|
|
{
|
|
version (StdLoggerDisableTrace) enum isLoggingActiveAt = false;
|
|
}
|
|
else static if (ll == LogLevel.info)
|
|
{
|
|
version (StdLoggerDisableInfo) enum isLoggingActiveAt = false;
|
|
}
|
|
else static if (ll == LogLevel.warning)
|
|
{
|
|
version (StdLoggerDisableWarning) enum isLoggingActiveAt = false;
|
|
}
|
|
else static if (ll == LogLevel.error)
|
|
{
|
|
version (StdLoggerDisableError) enum isLoggingActiveAt = false;
|
|
}
|
|
else static if (ll == LogLevel.critical)
|
|
{
|
|
version (StdLoggerDisableCritical) enum isLoggingActiveAt = false;
|
|
}
|
|
else static if (ll == LogLevel.fatal)
|
|
{
|
|
version (StdLoggerDisableFatal) enum isLoggingActiveAt = false;
|
|
}
|
|
// If `isLoggingActiveAt` didn't get defined above to false,
|
|
// we default it to true.
|
|
static if (!is(typeof(isLoggingActiveAt) == bool))
|
|
{
|
|
enum isLoggingActiveAt = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// This compile-time flag is $(D true) if logging is not statically disabled.
|
|
enum isLoggingActive = isLoggingActiveAt!(LogLevel.all);
|
|
|
|
/** This functions is used at runtime to determine if a $(D LogLevel) is
|
|
active. The same previously defined version statements are used to disable
|
|
certain levels. Again the version statements are associated with a compile
|
|
unit and can therefore not disable logging in other compile units.
|
|
pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc
|
|
*/
|
|
bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
|
|
LogLevel globalLL, lazy bool condition = true) @trusted
|
|
{
|
|
switch (ll)
|
|
{
|
|
case LogLevel.trace:
|
|
version (StdLoggerDisableTrace) return false;
|
|
else break;
|
|
case LogLevel.info:
|
|
version (StdLoggerDisableInfo) return false;
|
|
else break;
|
|
case LogLevel.warning:
|
|
version (StdLoggerDisableWarning) return false;
|
|
else break;
|
|
case LogLevel.critical:
|
|
version (StdLoggerDisableCritical) return false;
|
|
else break;
|
|
case LogLevel.fatal:
|
|
version (StdLoggerDisableFatal) return false;
|
|
else break;
|
|
default: break;
|
|
}
|
|
|
|
return ll >= globalLL
|
|
&& ll >= loggerLL
|
|
&& globalLL != LogLevel.off
|
|
&& loggerLL != LogLevel.off
|
|
&& condition;
|
|
}
|
|
|
|
/* This function formates a $(D SysTime) into an $(D OutputRange).
|
|
|
|
The $(D SysTime) is formatted similar to
|
|
$(LREF std.datatime.DateTime.toISOExtString) expect the fractional second part.
|
|
The sub second part is the upper three digest of the microsecond.
|
|
*/
|
|
void systimeToISOString(OutputRange)(OutputRange o, const ref SysTime time)
|
|
if(isOutputRange!(OutputRange,string))
|
|
{
|
|
auto fsec = time.fracSec.usecs / 1000;
|
|
|
|
formattedWrite(o, "%04d-%02d-%02dT%02d:%02d:%02d.%03d",
|
|
time.year, time.month, time.day, time.hour, time.minute, time.second,
|
|
fsec);
|
|
}
|
|
|
|
/** This function logs data.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D LogLevel) of the $(D stdlog) and the
|
|
$(D defaultLogLevel) additionally the condition passed must be $(D true).
|
|
|
|
Params:
|
|
ll = The $(D LogLevel) used by this log call.
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
log(LogLevel.warning, true, "Hello World", 3.1415);
|
|
--------------------
|
|
*/
|
|
void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll,
|
|
lazy bool condition, lazy A args) @trusted
|
|
if (args.length > 1)
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!(line, file, funcName, prettyFuncName, moduleName)
|
|
(ll, condition, args);
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
void log(T)(const LogLevel ll, lazy bool condition, lazy T arg,
|
|
int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!T(ll, condition, arg, line, file, funcName,
|
|
prettyFuncName, moduleName);
|
|
}
|
|
}
|
|
|
|
/** This function logs data.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D LogLevel) of the $(D stdlog).
|
|
|
|
Params:
|
|
ll = The $(D LogLevel) used by this log call.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
log(LogLevel.warning, "Hello World", 3.1415);
|
|
--------------------
|
|
*/
|
|
void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
|
|
@trusted
|
|
if (args.length > 1 && !is(Unqual!(A[0]) : bool))
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!(line, file, funcName, prettyFuncName, moduleName)
|
|
(ll, args);
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
void log(T)(const LogLevel ll, lazy T arg, int line = __LINE__,
|
|
string file = __FILE__, string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!T(ll, arg, line, file, funcName, prettyFuncName,
|
|
moduleName);
|
|
}
|
|
}
|
|
|
|
/** This function logs data.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the
|
|
$(D stdlog) must be greater or equal to the $(D defaultLogLevel)
|
|
add the condition passed must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
log(true, "Hello World", 3.1415);
|
|
--------------------
|
|
*/
|
|
void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
|
|
@trusted
|
|
if (args.length > 1)
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!(line, file, funcName, prettyFuncName, moduleName)
|
|
(condition, args);
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
void log(T)(lazy bool condition, lazy T arg, int line = __LINE__,
|
|
string file = __FILE__, string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__, string moduleName = __MODULE__)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!T(condition, arg, line, file, funcName, prettyFuncName,
|
|
moduleName);
|
|
}
|
|
}
|
|
|
|
/** This function logs data.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the
|
|
$(D stdlog) must be greater or equal to the $(D defaultLogLevel).
|
|
|
|
Params:
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
log("Hello World", 3.1415);
|
|
--------------------
|
|
*/
|
|
void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy A args)
|
|
@trusted
|
|
if (args.length > 1 && !is(Unqual!(A[0]) : bool)
|
|
&& !is(Unqual!(A[0]) == LogLevel))
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!(line, file, funcName, prettyFuncName, moduleName)(args);
|
|
}
|
|
}
|
|
|
|
void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.log!T(arg, line, file, funcName, prettyFuncName, moduleName);
|
|
}
|
|
}
|
|
|
|
/** This function logs data in a $(D printf)-style manner.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D LogLevel) of the $(D stdlog) and the
|
|
$(D defaultLogLevel) additionally the condition passed must be $(D true).
|
|
|
|
Params:
|
|
ll = The $(D LogLevel) used by this log call.
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
logf(LogLevel.warning, true, "Hello World %f", 3.1415);
|
|
--------------------
|
|
*/
|
|
void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll,
|
|
lazy bool condition, lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.logf!(line, file, funcName, prettyFuncName, moduleName)
|
|
(ll, condition, msg, args);
|
|
}
|
|
}
|
|
|
|
/** This function logs data in a $(D printf)-style manner.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D LogLevel) of the $(D stdlog) and the
|
|
$(D defaultLogLevel).
|
|
|
|
Params:
|
|
ll = The $(D LogLevel) used by this log call.
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
logf(LogLevel.warning, "Hello World %f", 3.1415);
|
|
--------------------
|
|
*/
|
|
void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll, lazy string msg,
|
|
lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.logf!(line, file, funcName, prettyFuncName, moduleName)
|
|
(ll, msg, args);
|
|
}
|
|
}
|
|
|
|
/** This function logs data in a $(D printf)-style manner.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D defaultLogLevel) additionally the condition
|
|
passed must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
logf(true, "Hello World %f", 3.1415);
|
|
--------------------
|
|
*/
|
|
void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition,
|
|
lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.logf!(line, file, funcName, prettyFuncName, moduleName)
|
|
(condition, msg, args);
|
|
}
|
|
}
|
|
|
|
/** This function logs data in a $(D printf)-style manner.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the log call must
|
|
be greater or equal to the $(D defaultLogLevel).
|
|
|
|
Params:
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
logf("Hello World %f", 3.1415);
|
|
--------------------
|
|
*/
|
|
void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
stdlog.logf!(line, file, funcName,prettyFuncName, moduleName)
|
|
(msg, args);
|
|
}
|
|
}
|
|
|
|
/** This template provides the global log functions with the $(D LogLevel)
|
|
is encoded in the function name.
|
|
|
|
For further information see the the two functions defined inside of this
|
|
template.
|
|
|
|
The aliases following this template create the public names of these log
|
|
functions.
|
|
*/
|
|
template defaultLogFunction(LogLevel ll)
|
|
{
|
|
/** This function logs data to the $(D stdlog).
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the $(D stdlog) and
|
|
must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
trace(1337, "is number");
|
|
info(1337, "is number");
|
|
error(1337, "is number");
|
|
critical(1337, "is number");
|
|
fatal(1337, "is number");
|
|
--------------------
|
|
*/
|
|
void defaultLogFunction(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy A args) @trusted
|
|
if (args.length > 0 && !is(Unqual!(A[0]) : bool))
|
|
{
|
|
static if (isLoggingActiveAt!ll)
|
|
{
|
|
stdlog.memLogFunctions!(ll).logImpl!(line, file, funcName,
|
|
prettyFuncName, moduleName)(args);
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the $(D stdlog) depending on a condition.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the $(D stdlog) and
|
|
must be greater or equal than the global $(D LogLevel) additionally the
|
|
condition passed must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
trace(true, 1337, "is number");
|
|
info(false, 1337, "is number");
|
|
error(true, 1337, "is number");
|
|
critical(false, 1337, "is number");
|
|
fatal(true, 1337, "is number");
|
|
--------------------
|
|
*/
|
|
void defaultLogFunction(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll)
|
|
{
|
|
stdlog.memLogFunctions!(ll).logImpl!(line, file, funcName,
|
|
prettyFuncName, moduleName)(condition, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
alias trace = defaultLogFunction!(LogLevel.trace);
|
|
/// Ditto
|
|
alias info = defaultLogFunction!(LogLevel.info);
|
|
/// Ditto
|
|
alias warning = defaultLogFunction!(LogLevel.warning);
|
|
/// Ditto
|
|
alias error = defaultLogFunction!(LogLevel.error);
|
|
/// Ditto
|
|
alias critical = defaultLogFunction!(LogLevel.critical);
|
|
/// Ditto
|
|
alias fatal = defaultLogFunction!(LogLevel.fatal);
|
|
|
|
/** This template provides the global $(D printf)-style log functions with
|
|
the $(D LogLevel) is encoded in the function name.
|
|
|
|
For further information see the the two functions defined inside of this
|
|
template.
|
|
|
|
The aliases following this template create the public names of the log
|
|
functions.
|
|
*/
|
|
template defaultLogFunctionf(LogLevel ll)
|
|
{
|
|
/** This function logs data to the $(D stdlog) in a $(D printf)-style
|
|
manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the $(D stdlog) and
|
|
must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
tracef("is number %d", 1);
|
|
infof("is number %d", 2);
|
|
errorf("is number %d", 3);
|
|
criticalf("is number %d", 4);
|
|
fatalf("is number %d", 5);
|
|
--------------------
|
|
*/
|
|
void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll)
|
|
{
|
|
stdlog.memLogFunctions!(ll).logImplf!(line, file, funcName,
|
|
prettyFuncName, moduleName)(msg, args);
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the $(D stdlog) in a $(D printf)-style
|
|
manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the $(D stdlog) and
|
|
must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
tracef(false, "is number %d", 1);
|
|
infof(false, "is number %d", 2);
|
|
errorf(true, "is number %d", 3);
|
|
criticalf(true, "is number %d", 4);
|
|
fatalf(someFunct(), "is number %d", 5);
|
|
--------------------
|
|
*/
|
|
void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition,
|
|
lazy string msg, lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll)
|
|
{
|
|
stdlog.memLogFunctions!(ll).logImplf!(line, file, funcName,
|
|
prettyFuncName, moduleName)(condition, msg, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
alias tracef = defaultLogFunctionf!(LogLevel.trace);
|
|
/// Ditto
|
|
alias infof = defaultLogFunctionf!(LogLevel.info);
|
|
/// Ditto
|
|
alias warningf = defaultLogFunctionf!(LogLevel.warning);
|
|
/// Ditto
|
|
alias errorf = defaultLogFunctionf!(LogLevel.error);
|
|
/// Ditto
|
|
alias criticalf = defaultLogFunctionf!(LogLevel.critical);
|
|
/// Ditto
|
|
alias fatalf = defaultLogFunctionf!(LogLevel.fatal);
|
|
|
|
private struct MsgRange
|
|
{
|
|
private Logger log;
|
|
|
|
this(Logger log)
|
|
{
|
|
this.log = log;
|
|
}
|
|
|
|
void put(const(char)[] msg)
|
|
{
|
|
log.logMsgPart(msg);
|
|
}
|
|
}
|
|
|
|
private void formatString(A...)(MsgRange oRange, A args)
|
|
{
|
|
import std.format : formattedWrite;
|
|
|
|
foreach (arg; args)
|
|
{
|
|
std.format.formattedWrite!(MsgRange,char)(oRange, "%s", arg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
There are eight usable logging level. These level are $(I all), $(I trace),
|
|
$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
|
|
If a log function with $(D LogLevel.fatal) is called the shutdown handler of
|
|
that logger is called.
|
|
*/
|
|
enum LogLevel : ubyte
|
|
{
|
|
all = 1, /** Lowest possible assignable $(D LogLevel). */
|
|
trace = 32, /** $(D LogLevel) for tracing the execution of the program. */
|
|
info = 64, /** This level is used to display information about the
|
|
program. */
|
|
warning = 96, /** warnings about the program should be displayed with this
|
|
level. */
|
|
error = 128, /** Information about errors should be logged with this
|
|
level.*/
|
|
critical = 160, /** Messages that inform about critical errors should be
|
|
logged with this level. */
|
|
fatal = 192, /** Log messages that describe fatal errors should use this
|
|
level. */
|
|
off = ubyte.max /** Highest possible $(D LogLevel). */
|
|
}
|
|
|
|
/** This class is the base of every logger. In order to create a new kind of
|
|
logger a deriving class needs to implement the $(D writeLogMsg) method. By
|
|
default this is not thread-safe.
|
|
|
|
It is also possible to $(D override) the three methods $(D beginLogMsg),
|
|
$(D logMsgPart) and $(D finishLogMsg) together, this option gives more
|
|
flexibility.
|
|
*/
|
|
abstract class Logger
|
|
{
|
|
/** LogEntry is a aggregation combining all information associated
|
|
with a log message. This aggregation will be passed to the method
|
|
writeLogMsg.
|
|
*/
|
|
protected struct LogEntry
|
|
{
|
|
/// the filename the log function was called from
|
|
string file;
|
|
/// the line number the log function was called from
|
|
int line;
|
|
/// the name of the function the log function was called from
|
|
string funcName;
|
|
/// the pretty formatted name of the function the log function was
|
|
/// called from
|
|
string prettyFuncName;
|
|
/// the name of the module the log message is coming from
|
|
string moduleName;
|
|
/// the $(D LogLevel) associated with the log message
|
|
LogLevel logLevel;
|
|
/// thread id of the log message
|
|
Tid threadId;
|
|
/// the time the message was logged
|
|
SysTime timestamp;
|
|
/// the message of the log message
|
|
string msg;
|
|
/// A refernce to the $(D Logger) used to create this $(D LogEntry)
|
|
Logger logger;
|
|
}
|
|
|
|
/** This constructor takes a name of type $(D string), and a $(D LogLevel).
|
|
|
|
Every subclass of $(D Logger) has to call this constructor from there
|
|
constructor. It sets the $(D LogLevel), the name of the $(D Logger), and
|
|
creates a fatal handler. The fatal handler will throw an $(D Error) if a
|
|
log call is made with a $(D LogLevel) $(D LogLevel.fatal).
|
|
*/
|
|
this(LogLevel lv)
|
|
{
|
|
this.logLevel_ = lv;
|
|
this.fatalHandler_ = delegate() {
|
|
throw new Error("A fatal log message was logged");
|
|
};
|
|
this.mutex = new Mutex();
|
|
|
|
this.msgAppender = appender!string();
|
|
}
|
|
|
|
/** A custom logger must implement this method in order to work in a
|
|
$(D MultiLogger) and $(D ArrayLogger).
|
|
|
|
Params:
|
|
payload = All information associated with call to log function.
|
|
See_Also: beginLogMsg, logMsgPart, finishLogMsg
|
|
*/
|
|
abstract protected void writeLogMsg(ref LogEntry payload);
|
|
|
|
/* The default implementation will use an $(D std.array.appender)
|
|
internally to construct the message string. This means dynamic,
|
|
GC memory allocation. A logger can avoid this allocation by
|
|
reimplementing $(D beginLogMsg), $(D logMsgPart) and $(D finishLogMsg).
|
|
$(D beginLogMsg) is always called first, followed by any number of calls
|
|
to $(D logMsgPart) and one call to $(D finishLogMsg).
|
|
|
|
As an example for such a custom $(D Logger) compare this:
|
|
----------------
|
|
class CLogger : Logger {
|
|
override void beginLogMsg(string file, int line, string funcName,
|
|
string prettyFuncName, string moduleName, LogLevel logLevel,
|
|
Tid threadId, SysTime timestamp)
|
|
{
|
|
... logic here
|
|
}
|
|
|
|
override void logMsgPart(const(char)[] msg)
|
|
{
|
|
... logic here
|
|
}
|
|
|
|
override void finishLogMsg()
|
|
{
|
|
... logic here
|
|
}
|
|
|
|
void writeLogMsg(ref LogEntry payload)
|
|
{
|
|
this.beginLogMsg(payload.file, payload.line, payload.funcName,
|
|
payload.prettyFuncName, payload.moduleName, payload.logLevel,
|
|
payload.threadId, payload.timestamp, payload.logger);
|
|
|
|
this.logMsgPart(payload.msg);
|
|
this.finishLogMsg();
|
|
}
|
|
}
|
|
----------------
|
|
*/
|
|
protected void beginLogMsg(string file, int line, string funcName,
|
|
string prettyFuncName, string moduleName, LogLevel logLevel,
|
|
Tid threadId, SysTime timestamp, Logger logger)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
header = LogEntry(file, line, funcName, prettyFuncName,
|
|
moduleName, logLevel, threadId, timestamp, null, logger);
|
|
}
|
|
}
|
|
|
|
/** Logs a part of the log message. */
|
|
protected void logMsgPart(const(char)[] msg)
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
msgAppender.put(msg);
|
|
}
|
|
}
|
|
|
|
/** Signals that the message has been written and no more calls to
|
|
$(D logMsgPart) follow. */
|
|
protected void finishLogMsg()
|
|
{
|
|
static if (isLoggingActive)
|
|
{
|
|
header.msg = msgAppender.data;
|
|
this.writeLogMsg(header);
|
|
msgAppender = appender!string();
|
|
}
|
|
}
|
|
|
|
/** The $(D LogLevel) determines if the log call are processed or dropped
|
|
by the $(D Logger). In order for the log call to be processed the
|
|
$(D LogLevel) of the log call must be greater or equal to the $(D LogLevel)
|
|
of the $(D logger).
|
|
|
|
These two methods set and get the $(D LogLevel) of the used $(D Logger).
|
|
|
|
Example:
|
|
-----------
|
|
auto f = new FileLogger(stdout);
|
|
f.logLevel = LogLevel.info;
|
|
assert(f.logLevel == LogLevel.info);
|
|
-----------
|
|
*/
|
|
@property final LogLevel logLevel() const pure @trusted @nogc
|
|
{
|
|
return atomicLoad!(MemoryOrder.acq)(this.logLevel_);
|
|
}
|
|
|
|
/// Ditto
|
|
@property final void logLevel(const LogLevel lv) pure @safe @nogc
|
|
{
|
|
synchronized (mutex) this.logLevel_ = lv;
|
|
}
|
|
|
|
/** This $(D delegate) is called in case a log message with
|
|
$(D LogLevel.fatal) gets logged.
|
|
|
|
By default an $(D Error) will be thrown.
|
|
*/
|
|
@property final void delegate() fatalHandler() const pure @safe @nogc
|
|
{
|
|
synchronized (mutex) return this.fatalHandler_;
|
|
}
|
|
|
|
/// Ditto
|
|
@property final void fatalHandler(void delegate() fh) pure @safe @nogc
|
|
{
|
|
synchronized (mutex) this.fatalHandler_ = fh;
|
|
}
|
|
|
|
/** This method allows forwarding log entries from one logger to another.
|
|
|
|
$(D forwardMsg) will ensure proper synchronization and then call
|
|
$(D writeLogMsg). This is an API for implementing your own loggers and
|
|
should not be called by normal user code. A notable difference from other
|
|
logging functions is that the $(D globalLogLevel) wont be evaluated again
|
|
since it is assumed that the caller already checked that.
|
|
*/
|
|
final void forwardMsg(ref LogEntry payload)
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(payload.logLevel, this.logLevel_,
|
|
LogLevel.all))
|
|
{
|
|
this.writeLogMsg(payload);
|
|
|
|
if (payload.logLevel == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This template provides the log functions for the $(D Logger) $(D class)
|
|
with the $(D LogLevel) encoded in the function name.
|
|
|
|
For further information see the the two functions defined inside of this
|
|
template.
|
|
|
|
The aliases following this template create the public names of these log
|
|
functions.
|
|
*/
|
|
template memLogFunctions(LogLevel ll)
|
|
{
|
|
/** This function logs data to the used $(D Logger).
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
must be greater or equal than the $(D LogLevel) of the used $(D Logger)
|
|
and must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.trace(1337, "is number");
|
|
s.info(1337, "is number");
|
|
s.error(1337, "is number");
|
|
s.critical(1337, "is number");
|
|
s.fatal(1337, "is number");
|
|
--------------------
|
|
*/
|
|
void logImpl(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy A args) @trusted
|
|
if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
|
|
{
|
|
static if (isLoggingActiveAt!ll) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
static if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) depending on a
|
|
condition.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the used $(D Logger) and
|
|
must be greater or equal than the global $(D LogLevel) additionally the
|
|
condition passed must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.trace(true, 1337, "is number");
|
|
s.info(false, 1337, "is number");
|
|
s.error(true, 1337, "is number");
|
|
s.critical(false, 1337, "is number");
|
|
s.fatal(true, 1337, "is number");
|
|
--------------------
|
|
*/
|
|
void logImpl(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition,
|
|
lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
|
|
condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
static if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) in a
|
|
$(D printf)-style manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
must be greater or equal than the $(D LogLevel) of the used $(D Logger)
|
|
and must be greater or equal than the global $(D LogLevel) additionally
|
|
the passed condition must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stderr);
|
|
s.tracef(true, "is number %d", 1);
|
|
s.infof(true, "is number %d", 2);
|
|
s.errorf(false, "is number %d", 3);
|
|
s.criticalf(someFunc(), "is number %d", 4);
|
|
s.fatalf(true, "is number %d", 5);
|
|
--------------------
|
|
*/
|
|
void logImplf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition,
|
|
lazy string msg, lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
|
|
condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
static if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) in a
|
|
$(D printf)-style manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel) must
|
|
be greater or equal than the $(D LogLevel) of the used $(D Logger) and
|
|
must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
msg = The $(D printf)-style string.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stderr);
|
|
s.tracef("is number %d", 1);
|
|
s.infof("is number %d", 2);
|
|
s.errorf("is number %d", 3);
|
|
s.criticalf("is number %d", 4);
|
|
s.fatalf("is number %d", 5);
|
|
--------------------
|
|
*/
|
|
void logImplf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActiveAt!ll) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
static if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
alias trace = memLogFunctions!(LogLevel.trace).logImpl;
|
|
/// Ditto
|
|
alias tracef = memLogFunctions!(LogLevel.trace).logImplf;
|
|
/// Ditto
|
|
alias info = memLogFunctions!(LogLevel.info).logImpl;
|
|
/// Ditto
|
|
alias infof = memLogFunctions!(LogLevel.info).logImplf;
|
|
/// Ditto
|
|
alias warning = memLogFunctions!(LogLevel.warning).logImpl;
|
|
/// Ditto
|
|
alias warningf = memLogFunctions!(LogLevel.warning).logImplf;
|
|
/// Ditto
|
|
alias error = memLogFunctions!(LogLevel.error).logImpl;
|
|
/// Ditto
|
|
alias errorf = memLogFunctions!(LogLevel.error).logImplf;
|
|
/// Ditto
|
|
alias critical = memLogFunctions!(LogLevel.critical).logImpl;
|
|
/// Ditto
|
|
alias criticalf = memLogFunctions!(LogLevel.critical).logImplf;
|
|
/// Ditto
|
|
alias fatal = memLogFunctions!(LogLevel.fatal).logImpl;
|
|
/// Ditto
|
|
alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
|
|
|
|
/** This method logs data with the $(D LogLevel) of the used $(D Logger).
|
|
|
|
This method takes a $(D bool) as first argument. In order for the
|
|
data to be processed the $(D bool) must be $(D true) and the $(D LogLevel)
|
|
of the Logger must be greater or equal to the global $(D LogLevel).
|
|
|
|
Params:
|
|
args = The data that should be logged.
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that is to be logged.
|
|
|
|
Returns: The logger used by the logging function as reference.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto l = new StdioLogger();
|
|
l.log(1337);
|
|
--------------------
|
|
*/
|
|
final void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll,
|
|
lazy bool condition, lazy A args) @trusted
|
|
if (args.length > 1)
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
final void log(T)(const LogLevel ll, lazy bool condition, lazy T args,
|
|
int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
|
|
condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) with a specific
|
|
$(D LogLevel).
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
must be greater or equal than the $(D LogLevel) of the used $(D Logger)
|
|
and must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
ll = The specific $(D LogLevel) used for logging the log message.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.log(LogLevel.trace, 1337, "is number");
|
|
s.log(LogLevel.info, 1337, "is number");
|
|
s.log(LogLevel.warning, 1337, "is number");
|
|
s.log(LogLevel.error, 1337, "is number");
|
|
s.log(LogLevel.fatal, 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll, lazy A args)
|
|
@trusted
|
|
if (args.length > 1 && !is(Unqual!(A[0]) : bool))
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
final void log(T)(const LogLevel ll, lazy T args, int line = __LINE__,
|
|
string file = __FILE__, string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) depending on a
|
|
explicitly passed condition with the $(D LogLevel) of the used
|
|
$(D Logger).
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
of the used $(D Logger) must be greater or equal than the global
|
|
$(D LogLevel) and the condition must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.log(true, 1337, "is number");
|
|
s.log(true, 1337, "is number");
|
|
s.log(true, 1337, "is number");
|
|
s.log(false, 1337, "is number");
|
|
s.log(false, 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
|
|
@trusted
|
|
if (args.length > 1)
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_,
|
|
globalLogLevel, condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
final void log(T)(lazy bool condition, lazy T args, int line = __LINE__,
|
|
string file = __FILE__, string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
|
|
condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) with the $(D LogLevel)
|
|
of the used $(D Logger).
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
of the used $(D Logger) must be greater or equal than the global
|
|
$(D LogLevel).
|
|
|
|
Params:
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.log(1337, "is number");
|
|
s.log(info, 1337, "is number");
|
|
s.log(1337, "is number");
|
|
s.log(1337, "is number");
|
|
s.log(1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void log(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy A args)
|
|
@trusted
|
|
if (args.length > 1
|
|
&& !is(Unqual!(A[0]) : bool)
|
|
&& !is(Unqual!(A[0]) == LogLevel))
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_,
|
|
globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
final void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
auto writer = MsgRange(this);
|
|
formatString(writer, arg);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) with a specific
|
|
$(D LogLevel) and depending on a condition in a $(D printf)-style manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
must be greater or equal than the $(D LogLevel) of the used $(D Logger)
|
|
and must be greater or equal than the global $(D LogLevel) and the
|
|
condition must be $(D true).
|
|
|
|
Params:
|
|
ll = The specific $(D LogLevel) used for logging the log message.
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The format string used for this log call.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number");
|
|
s.logf(LogLevel.info, true ,"%d %s", 1337, "is number");
|
|
s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number");
|
|
s.logf(LogLevel.error, false ,"%d %s", 1337, "is number");
|
|
s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll,
|
|
lazy bool condition, lazy string msg, lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) with a specific
|
|
$(D LogLevel) in a $(D printf)-style manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
must be greater or equal than the $(D LogLevel) of the used $(D Logger)
|
|
and must be greater or equal than the global $(D LogLevel).
|
|
|
|
Params:
|
|
ll = The specific $(D LogLevel) used for logging the log message.
|
|
msg = The format string used for this log call.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.logf(LogLevel.trace, "%d %s", 1337, "is number");
|
|
s.logf(LogLevel.info, "%d %s", 1337, "is number");
|
|
s.logf(LogLevel.warning, "%d %s", 1337, "is number");
|
|
s.logf(LogLevel.error, "%d %s", 1337, "is number");
|
|
s.logf(LogLevel.fatal, "%d %s", 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(const LogLevel ll,
|
|
lazy string msg, lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, ll, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (ll == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This function logs data to the used $(D Logger) depending on a
|
|
condition with the $(D LogLevel) of the used $(D Logger) in a
|
|
$(D printf)-style manner.
|
|
|
|
In order for the resulting log message to be logged the $(D LogLevel)
|
|
of the used $(D Logger) must be greater or equal than the global
|
|
$(D LogLevel) and the condition must be $(D true).
|
|
|
|
Params:
|
|
condition = The condition must be $(D true) for the data to be logged.
|
|
msg = The format string used for this log call.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.logf(true ,"%d %s", 1337, "is number");
|
|
s.logf(true ,"%d %s", 1337, "is number");
|
|
s.logf(true ,"%d %s", 1337, "is number");
|
|
s.logf(false ,"%d %s", 1337, "is number");
|
|
s.logf(true ,"%d %s", 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy bool condition,
|
|
lazy string msg, lazy A args) @trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
|
|
condition))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This method logs data to the used $(D Logger) with the $(D LogLevel)
|
|
of the this $(D Logger) in a $(D printf)-style manner.
|
|
|
|
In order for the data to be processed the $(D LogLevel) of the $(D Logger)
|
|
must be greater or equal to the global $(D LogLevel).
|
|
|
|
Params:
|
|
msg = The format string used for this log call.
|
|
args = The data that should be logged.
|
|
|
|
Examples:
|
|
--------------------
|
|
auto s = new FileLogger(stdout);
|
|
s.logf("%d %s", 1337, "is number");
|
|
s.logf("%d %s", 1337, "is number");
|
|
s.logf("%d %s", 1337, "is number");
|
|
s.logf("%d %s", 1337, "is number");
|
|
s.logf("%d %s", 1337, "is number");
|
|
--------------------
|
|
*/
|
|
final void logf(int line = __LINE__, string file = __FILE__,
|
|
string funcName = __FUNCTION__,
|
|
string prettyFuncName = __PRETTY_FUNCTION__,
|
|
string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
|
|
@trusted
|
|
{
|
|
static if (isLoggingActive) synchronized (mutex)
|
|
{
|
|
if (isLoggingEnabled(this.logLevel_, this.logLevel_,
|
|
globalLogLevel))
|
|
{
|
|
this.beginLogMsg(file, line, funcName, prettyFuncName,
|
|
moduleName, this.logLevel_, thisTid, Clock.currTime, this);
|
|
|
|
auto writer = MsgRange(this);
|
|
formattedWrite(writer, msg, args);
|
|
|
|
this.finishLogMsg();
|
|
|
|
if (this.logLevel_ == LogLevel.fatal)
|
|
this.fatalHandler_();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void delegate() fatalHandler_;
|
|
private shared LogLevel logLevel_ = LogLevel.info;
|
|
private Mutex mutex;
|
|
|
|
protected Appender!string msgAppender;
|
|
protected LogEntry header;
|
|
}
|
|
|
|
private __gshared Mutex __defaultLoggerMutex;
|
|
private __gshared Logger __defaultLogger;
|
|
private shared Logger __stdlog;
|
|
private shared LogLevel __globalLogLevel = LogLevel.all;
|
|
|
|
|
|
/* This method returns the global default Logger. */
|
|
private @property Logger defaultLoggerImpl() @trusted
|
|
{
|
|
static __gshared ubyte[__traits(classInstanceSize, FileLogger)] buffer;
|
|
|
|
synchronized (__defaultLoggerMutex)
|
|
{
|
|
if (__defaultLogger is null)
|
|
{
|
|
__defaultLogger = emplace!FileLogger(buffer, stderr, LogLevel.all);
|
|
}
|
|
}
|
|
return __defaultLogger;
|
|
}
|
|
|
|
/** This property sets and gets the default $(D Logger).
|
|
|
|
Example:
|
|
-------------
|
|
stdlog = new FileLogger(yourFile);
|
|
-------------
|
|
The example sets a new $(D StdioLogger) as new $(D stdlog).
|
|
|
|
If at some point you want to use the original default logger again, you can
|
|
use $(D stdlog = null;). This will put back the original.
|
|
|
|
Note:
|
|
While getting and setting $(D stdlog) is thread-safe, it has to be considered
|
|
that the returned reference is only a current snapshot and in the following
|
|
code, you must make sure no other thread reassigns to it between reading and
|
|
writing $(D stdlog).
|
|
-------------
|
|
if (stdlog !is myLogger)
|
|
stdlog = new myLogger;
|
|
-------------
|
|
*/
|
|
@property Logger stdlog() @trusted
|
|
{
|
|
// If we have set up our own logger use that
|
|
if (auto logger = atomicLoad!(MemoryOrder.acq)(__stdlog))
|
|
{
|
|
return logger;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise resort to the default logger
|
|
return defaultLoggerImpl;
|
|
}
|
|
}
|
|
|
|
/// Ditto
|
|
@property void stdlog(Logger logger) @trusted
|
|
{
|
|
atomicStore!(MemoryOrder.rel)(__stdlog, cast(shared) logger);
|
|
}
|
|
|
|
/** This methods get and set the global $(D LogLevel).
|
|
|
|
Every log message with a $(D LogLevel) lower as the global $(D LogLevel)
|
|
will be discarded before it reaches $(D writeLogMessage) method of any
|
|
$(D Logger).
|
|
*/
|
|
/* Implementation note:
|
|
For any public logging call, the global log level shall only be queried once on
|
|
entry. Otherwise when another threads changes the level, we wouls work with
|
|
different levels at different spots in the code.
|
|
*/
|
|
@property LogLevel globalLogLevel() @trusted @nogc
|
|
{
|
|
return atomicLoad!(MemoryOrder.acq)(__globalLogLevel);
|
|
}
|
|
|
|
/// Ditto
|
|
@property void globalLogLevel(LogLevel ll) @trusted
|
|
{
|
|
atomicStore!(MemoryOrder.rel)(__globalLogLevel, ll);
|
|
}
|
|
|
|
version (unittest)
|
|
{
|
|
import std.array;
|
|
import std.ascii;
|
|
import std.random;
|
|
|
|
@trusted package string randomString(size_t upto)
|
|
{
|
|
auto app = Appender!string();
|
|
foreach(_ ; 0 .. upto)
|
|
app.put(letters[uniform(0, letters.length)]);
|
|
return app.data;
|
|
}
|
|
}
|
|
|
|
@safe unittest
|
|
{
|
|
LogLevel ll = globalLogLevel;
|
|
globalLogLevel = LogLevel.fatal;
|
|
assert(globalLogLevel == LogLevel.fatal);
|
|
globalLogLevel = ll;
|
|
}
|
|
|
|
version (unittest)
|
|
{
|
|
package class TestLogger : Logger
|
|
{
|
|
int line = -1;
|
|
string file = null;
|
|
string func = null;
|
|
string prettyFunc = null;
|
|
string msg = null;
|
|
LogLevel lvl;
|
|
|
|
this(const LogLevel lv = LogLevel.info)
|
|
{
|
|
super(lv);
|
|
}
|
|
|
|
override protected void writeLogMsg(ref LogEntry payload) @safe
|
|
{
|
|
this.line = payload.line;
|
|
this.file = payload.file;
|
|
this.func = payload.funcName;
|
|
this.prettyFunc = payload.prettyFuncName;
|
|
this.lvl = payload.logLevel;
|
|
this.msg = payload.msg;
|
|
}
|
|
}
|
|
|
|
private void testFuncNames(Logger logger) {
|
|
string s = "I'm here";
|
|
logger.log(s);
|
|
}
|
|
}
|
|
|
|
|
|
unittest
|
|
{
|
|
auto tl1 = new TestLogger();
|
|
testFuncNames(tl1);
|
|
assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func);
|
|
assert(tl1.prettyFunc ==
|
|
"void std.experimental.logger.core.testFuncNames(Logger logger)",
|
|
tl1.prettyFunc);
|
|
assert(tl1.msg == "I'm here", tl1.msg);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.experimental.logger.multilogger;
|
|
|
|
auto tl1 = new TestLogger;
|
|
auto tl2 = new TestLogger;
|
|
|
|
auto ml = new MultiLogger();
|
|
ml.insertLogger("one", tl1);
|
|
ml.insertLogger("two", tl2);
|
|
assertThrown!Exception(ml.insertLogger("one", tl1));
|
|
|
|
string msg = "Hello Logger World";
|
|
ml.log(msg);
|
|
int lineNumber = __LINE__ - 1;
|
|
assert(tl1.msg == msg);
|
|
assert(tl1.line == lineNumber);
|
|
assert(tl2.msg == msg);
|
|
assert(tl2.line == lineNumber);
|
|
|
|
ml.removeLogger("one");
|
|
ml.removeLogger("two");
|
|
assertThrown!Exception(ml.removeLogger("one"));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
bool errorThrown = false;
|
|
auto tl = new TestLogger;
|
|
auto dele = delegate() {
|
|
errorThrown = true;
|
|
};
|
|
tl.fatalHandler = dele;
|
|
tl.fatal();
|
|
assert(errorThrown);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto l = new TestLogger(LogLevel.info);
|
|
string msg = "Hello Logger World";
|
|
l.log(msg);
|
|
int lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg);
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
l.log(true, msg);
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg);
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
l.log(false, msg);
|
|
assert(l.msg == msg);
|
|
assert(l.line == lineNumber, to!string(l.line));
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
msg = "%s Another message";
|
|
l.logf(msg, "Yet");
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
l.logf(true, msg, "Yet");
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
l.logf(false, msg, "Yet");
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
assert(oldunspecificLogger.logLevel == LogLevel.all,
|
|
to!string(oldunspecificLogger.logLevel));
|
|
|
|
assert(l.logLevel == LogLevel.info);
|
|
stdlog = l;
|
|
assert(globalLogLevel == LogLevel.all,
|
|
to!string(globalLogLevel));
|
|
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
}
|
|
|
|
assert(stdlog.logLevel == LogLevel.info);
|
|
assert(globalLogLevel == LogLevel.all);
|
|
|
|
msg = "Another message";
|
|
log(msg);
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.logLevel == LogLevel.info);
|
|
assert(l.line == lineNumber, to!string(l.line));
|
|
assert(l.msg == msg, l.msg);
|
|
|
|
log(true, msg);
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg);
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
log(false, msg);
|
|
assert(l.msg == msg);
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
msg = "%s Another message";
|
|
logf(msg, "Yet");
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
logf(true, msg, "Yet");
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
logf(false, msg, "Yet");
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
msg = "%s Another message";
|
|
assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
|
|
lineNumber = __LINE__ - 1;
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
|
|
assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
|
|
assert(l.msg == msg.format("Yet"));
|
|
assert(l.line == lineNumber);
|
|
assert(l.logLevel == LogLevel.info);
|
|
}
|
|
|
|
unittest // default logger
|
|
{
|
|
import std.file;
|
|
string filename = randomString(32) ~ ".tempLogFile";
|
|
FileLogger l = new FileLogger(filename);
|
|
auto oldunspecificLogger = stdlog;
|
|
stdlog = l;
|
|
|
|
scope(exit)
|
|
{
|
|
remove(filename);
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
string notWritten = "this should not be written to file";
|
|
string written = "this should be written to file";
|
|
|
|
globalLogLevel = LogLevel.critical;
|
|
assert(globalLogLevel == LogLevel.critical);
|
|
|
|
log(LogLevel.warning, notWritten);
|
|
log(LogLevel.critical, written);
|
|
|
|
l.file.flush();
|
|
l.file.close();
|
|
|
|
auto file = File(filename, "r");
|
|
assert(!file.eof);
|
|
|
|
string readLine = file.readln();
|
|
assert(readLine.indexOf(written) != -1, readLine);
|
|
assert(readLine.indexOf(notWritten) == -1, readLine);
|
|
file.close();
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.file;
|
|
import core.memory;
|
|
string filename = randomString(32) ~ ".tempLogFile";
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
scope(exit)
|
|
{
|
|
remove(filename);
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
string notWritten = "this should not be written to file";
|
|
string written = "this should be written to file";
|
|
|
|
auto l = new FileLogger(filename);
|
|
stdlog = l;
|
|
stdlog.logLevel = LogLevel.critical;
|
|
|
|
log(LogLevel.error, false, notWritten);
|
|
log(LogLevel.critical, true, written);
|
|
destroy(l);
|
|
|
|
auto file = File(filename, "r");
|
|
auto readLine = file.readln();
|
|
assert(!readLine.empty, readLine);
|
|
assert(readLine.indexOf(written) != -1);
|
|
assert(readLine.indexOf(notWritten) == -1);
|
|
file.close();
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto tl = new TestLogger(LogLevel.all);
|
|
int l = __LINE__;
|
|
tl.info("a");
|
|
assert(tl.line == l+1);
|
|
assert(tl.msg == "a");
|
|
assert(tl.logLevel == LogLevel.all);
|
|
assert(globalLogLevel == LogLevel.all);
|
|
l = __LINE__;
|
|
tl.trace("b");
|
|
assert(tl.msg == "b", tl.msg);
|
|
assert(tl.line == l+1, to!string(tl.line));
|
|
}
|
|
|
|
// testing possible log conditions
|
|
unittest
|
|
{
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
auto mem = new TestLogger;
|
|
mem.fatalHandler = delegate() {};
|
|
stdlog = mem;
|
|
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
int value = 0;
|
|
foreach(gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
|
|
globalLogLevel = gll;
|
|
|
|
foreach(ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
|
|
mem.logLevel = ll;
|
|
|
|
foreach(cond; [true, false])
|
|
{
|
|
foreach(condValue; [true, false])
|
|
{
|
|
foreach(memOrG; [true, false])
|
|
{
|
|
foreach(prntf; [true, false])
|
|
{
|
|
foreach(ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning,
|
|
LogLevel.error, LogLevel.critical,
|
|
LogLevel.fatal, LogLevel.off])
|
|
{
|
|
foreach(singleMulti; 0 .. 2)
|
|
{
|
|
int lineCall;
|
|
mem.msg = "-1";
|
|
if (memOrG)
|
|
{
|
|
if (prntf)
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.logf(ll2, condValue, "%s",
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.logf(ll2, condValue,
|
|
"%d %d", value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.logf(ll2, "%s", value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.logf(ll2, "%d %d",
|
|
value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.log(ll2, condValue,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.log(ll2, condValue,
|
|
to!string(value), value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.log(ll2, to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.log(ll2,
|
|
to!string(value),
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (prntf)
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
logf(ll2, condValue, "%s",
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
logf(ll2, condValue,
|
|
"%s %d", value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
logf(ll2, "%s", value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
logf(ll2, "%s %s", value,
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
log(ll2, condValue,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
log(ll2, condValue, value,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
log(ll2, to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
log(ll2, value,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string valueStr = to!string(value);
|
|
++value;
|
|
|
|
bool gllOff = (gll != LogLevel.off);
|
|
bool llOff = (ll != LogLevel.off);
|
|
bool condFalse = (cond ? condValue : true);
|
|
bool ll2VSgll = (ll2 >= gll);
|
|
bool ll2VSll = (ll2 >= ll);
|
|
|
|
bool shouldLog = gllOff && llOff && condFalse
|
|
&& ll2VSgll && ll2VSll;
|
|
|
|
/*
|
|
writefln(
|
|
"go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
|
|
, gll != LogLevel.off, ll2 != LogLevel.off,
|
|
cond ? condValue : true,
|
|
ll2 >= gll, ll2 >= ll, shouldLog);
|
|
*/
|
|
|
|
|
|
if (shouldLog)
|
|
{
|
|
assert(mem.msg.indexOf(valueStr) != -1,
|
|
format(
|
|
"lineCall(%d) gll(%u) ll(%u) ll2(%u) " ~
|
|
"cond(%b) condValue(%b)" ~
|
|
" memOrG(%b) shouldLog(%b) %s == %s" ~
|
|
" %b %b %b %b %b",
|
|
lineCall, gll, ll, ll2, cond,
|
|
condValue, memOrG, shouldLog, mem.msg,
|
|
valueStr, gllOff, llOff, condFalse,
|
|
ll2VSgll, ll2VSll
|
|
));
|
|
}
|
|
else
|
|
{
|
|
assert(mem.msg.indexOf(valueStr),
|
|
format(
|
|
"lineCall(%d) gll(%u) ll(%u) ll2(%u) " ~
|
|
" cond(%b)condValue(%b) memOrG(%b) " ~
|
|
"shouldLog(%b) %s != %s", gll,
|
|
lineCall, ll, ll2, cond, condValue,
|
|
memOrG, shouldLog, mem.msg, valueStr
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// more testing
|
|
unittest
|
|
{
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
auto mem = new TestLogger;
|
|
mem.fatalHandler = delegate() {};
|
|
stdlog = mem;
|
|
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
int value = 0;
|
|
foreach(gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
|
|
globalLogLevel = gll;
|
|
|
|
foreach(ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
|
|
mem.logLevel = ll;
|
|
|
|
foreach(cond; [true, false])
|
|
{
|
|
foreach(condValue; [true, false])
|
|
{
|
|
foreach(memOrG; [true, false])
|
|
{
|
|
foreach(prntf; [true, false])
|
|
{
|
|
foreach(singleMulti; 0 .. 2)
|
|
{
|
|
int lineCall;
|
|
mem.msg = "-1";
|
|
if (memOrG)
|
|
{
|
|
if (prntf)
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.logf(condValue, "%s",
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.logf(condValue,
|
|
"%d %d", value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.logf("%s", value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.logf("%d %d",
|
|
value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.log(condValue,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.log(condValue,
|
|
to!string(value), value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
mem.log(to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
mem.log(to!string(value),
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (prntf)
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
logf(condValue, "%s", value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
logf(condValue, "%s %d", value,
|
|
value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
logf("%s", value);
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
logf("%s %s", value, value);
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (cond)
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
log(condValue,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
log(condValue, value,
|
|
to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (singleMulti == 0)
|
|
{
|
|
log(to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
else
|
|
{
|
|
log(value, to!string(value));
|
|
lineCall = __LINE__;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string valueStr = to!string(value);
|
|
++value;
|
|
|
|
bool gllOff = (gll != LogLevel.off);
|
|
bool llOff = (ll != LogLevel.off);
|
|
bool llVSgll = (ll >= gll);
|
|
bool condFalse = (cond ? condValue : true);
|
|
|
|
bool shouldLog = gllOff && llOff && condFalse
|
|
&& llVSgll;
|
|
|
|
/*
|
|
writefln(
|
|
"go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
|
|
, gll != LogLevel.off, ll2 != LogLevel.off,
|
|
cond ? condValue : true,
|
|
ll2 >= gll, ll2 >= ll, shouldLog);
|
|
*/
|
|
|
|
|
|
if (shouldLog)
|
|
{
|
|
assert(mem.msg.indexOf(valueStr) != -1,
|
|
format(
|
|
"lineCall(%d) gll(%u) ll(%u) " ~
|
|
"cond(%b) condValue(%b)" ~
|
|
" memOrG(%b) shouldLog(%b) %s == %s" ~
|
|
" %b %b %b",
|
|
lineCall, gll, ll, cond,
|
|
condValue, memOrG, shouldLog, mem.msg,
|
|
valueStr, gllOff, llOff, condFalse
|
|
));
|
|
}
|
|
else
|
|
{
|
|
assert(mem.msg.indexOf(valueStr),
|
|
format(
|
|
"lineCall(%d) gll(%u) ll(%u) " ~
|
|
" cond(%b)condValue(%b) memOrG(%b) " ~
|
|
"shouldLog(%b) %s != %s", gll,
|
|
lineCall, ll, cond, condValue,
|
|
memOrG, shouldLog, mem.msg, valueStr
|
|
));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// testing more possible log conditions
|
|
unittest
|
|
{
|
|
bool fatalLog;
|
|
auto mem = new TestLogger;
|
|
mem.fatalHandler = delegate() { fatalLog = true; };
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
stdlog = mem;
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
foreach(gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
|
|
globalLogLevel = gll;
|
|
|
|
foreach(ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
|
|
LogLevel.info, LogLevel.warning, LogLevel.error,
|
|
LogLevel.critical, LogLevel.fatal, LogLevel.off])
|
|
{
|
|
mem.logLevel = ll;
|
|
|
|
foreach(cond; [true, false])
|
|
{
|
|
assert(globalLogLevel == gll);
|
|
assert(mem.logLevel == ll);
|
|
|
|
bool gllVSll = LogLevel.trace >= globalLogLevel;
|
|
bool llVSgll = ll >= globalLogLevel;
|
|
bool lVSll = LogLevel.trace >= ll;
|
|
bool gllOff = globalLogLevel != LogLevel.off;
|
|
bool llOff = mem.logLevel != LogLevel.off;
|
|
|
|
bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
|
|
|
|
mem.line = -1;
|
|
/*
|
|
writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
|
|
gll, ll, cond, test);
|
|
writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
|
|
gllOff, llOff, cond, test2);
|
|
*/
|
|
|
|
mem.trace(__LINE__); int line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
trace(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.trace(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
trace(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.tracef("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
tracef("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.tracef(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
tracef(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
llVSgll = ll >= globalLogLevel;
|
|
lVSll = LogLevel.info >= ll;
|
|
test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
|
|
|
|
mem.info(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
info(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.info(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
info(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.infof("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
infof("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.infof(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
infof(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
llVSgll = ll >= globalLogLevel;
|
|
lVSll = LogLevel.warning >= ll;
|
|
test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
|
|
|
|
mem.warning(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
warning(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.warning(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
warning(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.warningf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
warningf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.warningf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
warningf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
llVSgll = ll >= globalLogLevel;
|
|
lVSll = LogLevel.critical >= ll;
|
|
test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
|
|
|
|
mem.critical(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
critical(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.critical(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
critical(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.criticalf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
criticalf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
criticalf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
|
|
llVSgll = ll >= globalLogLevel;
|
|
lVSll = LogLevel.fatal >= ll;
|
|
test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
|
|
|
|
mem.fatal(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
fatal(__LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
mem.fatal(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
fatal(cond, __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
mem.fatalf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
fatalf("%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
|
|
fatalf(cond, "%d", __LINE__); line = __LINE__;
|
|
assert(test ? mem.line == line : true); line = -1;
|
|
assert(test ? fatalLog : true);
|
|
fatalLog = false;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Issue #5
|
|
unittest
|
|
{
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
auto tl = new TestLogger(LogLevel.info);
|
|
stdlog = tl;
|
|
|
|
trace("trace");
|
|
assert(tl.msg.indexOf("trace") == -1);
|
|
//info("info");
|
|
//assert(tl.msg.indexOf("info") == 0);
|
|
}
|
|
|
|
// Issue #5
|
|
unittest
|
|
{
|
|
import std.experimental.logger.multilogger;
|
|
|
|
auto oldunspecificLogger = stdlog;
|
|
|
|
scope(exit)
|
|
{
|
|
stdlog = oldunspecificLogger;
|
|
globalLogLevel = LogLevel.all;
|
|
}
|
|
|
|
auto logger = new MultiLogger(LogLevel.error);
|
|
|
|
auto tl = new TestLogger(LogLevel.info);
|
|
logger.insertLogger("required", tl);
|
|
stdlog = logger;
|
|
|
|
trace("trace");
|
|
assert(tl.msg.indexOf("trace") == -1);
|
|
info("info");
|
|
assert(tl.msg.indexOf("info") == -1);
|
|
error("error");
|
|
assert(tl.msg.indexOf("error") == 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.exception : assertThrown;
|
|
auto tl = new TestLogger();
|
|
assertThrown!Throwable(tl.fatal("fatal"));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto dl = cast(FileLogger)stdlog;
|
|
assert(dl !is null);
|
|
assert(dl.logLevel == LogLevel.all);
|
|
assert(globalLogLevel == LogLevel.all);
|
|
}
|