mirror of https://github.com/deavmi/dlog.git
Compare commits
9 Commits
e935014573
...
1d57ab1529
Author | SHA1 | Date |
---|---|---|
Tristan B. Velloza Kildaire | 1d57ab1529 | |
Tristan B. Velloza Kildaire | b29e2eb1c7 | |
Tristan B. Velloza Kildaire | 6be4be3534 | |
Tristan B. Velloza Kildaire | 49f1f70f28 | |
Tristan B. Velloza Kildaire | 986a3d3bdf | |
Tristan B. Velloza Kildaire | 8bb28de027 | |
Tristan B. Velloza Kildaire | 9d8f7af4a3 | |
Tristan B. Velloza Kildaire | 66adb8f700 | |
Tristan B. Velloza Kildaire | c53558f847 |
129
README.md
129
README.md
|
@ -35,9 +35,15 @@ dlog is formed out of two main components:
|
|||
|
||||
1. `Logger`
|
||||
* The logger contains the needed barebones for facilitating the actual logging of text
|
||||
2. `MessageTransform`
|
||||
* A MessageTransform is attached to a logger and performs manipulation on the text input into the logger for logging
|
||||
* The _base logger_ (i.e. `Logger`) maintains a list of attaches _filters_, _message transformers_ and _handlers_
|
||||
2. `Filter`
|
||||
* Acts as a predicate on the incoming _message_ and determines whether it should be logged or not
|
||||
* This is used by the `BasicLogger` to implement log levels
|
||||
3. `Transform`
|
||||
* A _message transform_ is attached to a logger and performs manipulation on the message logged
|
||||
* They may be chained as to perform multiple transformations in a stream-like fashion
|
||||
4. `Handler`
|
||||
* A _handler_ handles the final transformed message, for some this means outputting to standard out, or a file
|
||||
|
||||
### Quick start
|
||||
|
||||
|
@ -47,60 +53,29 @@ simply use the default logger as follows:
|
|||
```d
|
||||
import dlog;
|
||||
|
||||
Logger logger = new DefaultLogger();
|
||||
DefaultLogger logger = new DefaultLogger();
|
||||
|
||||
logger.setLevel(Level.DEBUG);
|
||||
logger.error(["woah", "LEVELS!"], 69.420);
|
||||
logger.info(["woah", "LEVELS!"], 69.420);
|
||||
logger.warn(["woah", "LEVELS!"], 69.420);
|
||||
logger.debug_(["woah", "LEVELS!"], 69.420);
|
||||
|
||||
logger.log("This is a log message");
|
||||
logger.log(1);
|
||||
logger.log(1==1);
|
||||
logger.log([1,2,3]);
|
||||
// Should not be able to see this
|
||||
logger.setLevel(Level.INFO);
|
||||
logger.debug_("Can't see me!");
|
||||
```
|
||||
|
||||
This will output the following:
|
||||
|
||||
```
|
||||
[2021-Dec-23 11:17:35.3527637] (source/dlog/testing/thing.d:12): This is a log message
|
||||
[2021-Dec-23 11:17:35.3527717] (source/dlog/testing/thing.d:13): 1
|
||||
[2021-Dec-23 11:17:35.3527789] (source/dlog/testing/thing.d:14): true
|
||||
[2021-Dec-23 11:17:35.3527871] (source/dlog/testing/thing.d:15): [1, 2, 3]
|
||||
[2024-Apr-09 19:14:38.3077171] (ERROR): ["woah", "LEVELS!"] 69.42
|
||||
[2024-Apr-09 19:14:38.3077346] (INFO): ["woah", "LEVELS!"] 69.42
|
||||
[2024-Apr-09 19:14:38.3077559] (WARN): ["woah", "LEVELS!"] 69.42
|
||||
[2024-Apr-09 19:14:38.3077759] (DEBUG): ["woah", "LEVELS!"] 69.42
|
||||
```
|
||||
|
||||
As you can see file and line numbering of where the `log()` function is called appears in the log message which can be quite helpful
|
||||
for debugging.
|
||||
|
||||
---
|
||||
|
||||
We also support many different logging levels which can be accomplished using the `error`, `debug_` (or the `dbg` alias), `info `(the default) and `warn`:
|
||||
|
||||
```d
|
||||
Logger logger = new DefaultLogger();
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
|
||||
// Test out `error()`
|
||||
logger.error(["woah", "LEVELS!"], 69.420);
|
||||
|
||||
// Test out `info()`
|
||||
logger.info(["woah", "LEVELS!"], 69.420);
|
||||
|
||||
// Test out `warn()`
|
||||
logger.warn(["woah", "LEVELS!"], 69.420);
|
||||
|
||||
// Test out `debug_()`
|
||||
logger.debug_(["woah", "LEVELS!"], 69.420);
|
||||
```
|
||||
|
||||
This outputs the following:
|
||||
|
||||
```
|
||||
[2023-Mar-03 11:33:49.2617904] (source/dlog/core.d:427): ["woah", "LEVELS!"] 69.42
|
||||
[2023-Mar-03 11:33:49.2618091] (source/dlog/core.d:430): ["woah", "LEVELS!"] 69.42
|
||||
[2023-Mar-03 11:33:49.2618273] (source/dlog/core.d:433): ["woah", "LEVELS!"] 69.42
|
||||
[2023-Mar-03 11:33:49.2618457] (source/dlog/core.d:436): ["woah", "LEVELS!"] 69.42
|
||||
```
|
||||
|
||||
You can also look into `logc(Context, string)` which allows you to use a `Context` object when logging, more information available in the [full API](https://dlog.dpldocs.info/v0.3.19/dlog.context.html).
|
||||
You can see the [full API](https://dlog.dpldocs.info/v0.3.19/dlog.context.html) for more information.
|
||||
|
||||
### Custom loggers
|
||||
|
||||
|
@ -110,64 +85,32 @@ Perhaps the default transformation, `DefaultTransform`, may not be what you want
|
|||
messages or perhaps don't want the date-and-timestamp included at all. All of this can be up to you if you choose to implement your own
|
||||
message transform.
|
||||
|
||||
You will need to start off with a class that inherits from the `MessageTransform` class and then which overrides the `transform` method as shown below:
|
||||
You will need to start off with a class that inherits from the `Transform` class and then which overrides the `transform` method as shown below:
|
||||
|
||||
```d
|
||||
import dlog;
|
||||
|
||||
public class CustomTranform : MessageTransform
|
||||
public class CustomTranform : Transform
|
||||
{
|
||||
public override string transform(string text, Context context)
|
||||
public override Message transform(Message message)
|
||||
{
|
||||
BasicMessage bmesg = cast(BasicMessage)message;
|
||||
|
||||
// Only handle BasicMessage(s) - ones which have `setText(string)`
|
||||
if(bmesg is null)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
string transformed;
|
||||
/* Insert transformation code here */
|
||||
bmesg.setText(transformed);
|
||||
|
||||
/* Insert code to transform `text` and return the `transformed` text */
|
||||
|
||||
return transformed;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Additional information, besides the text being logged itself (this is the `string text` argument), comes in the form of a `Context` object `context`. What one can get from this is a `CompilationInfo` struct which contains the following fields below if one calls `toArray()` on
|
||||
it which will return a string array shown below (we refer to this array as `lineInfo`):
|
||||
|
||||
1. `lineInfo[0]`
|
||||
* This contains `__FILE_FULL_PATH__` which is the full path (absolute) to the source file where `log()` was called
|
||||
2. `lineInfo[1]`
|
||||
* This contains `__FILE__` which is the path (starting at `source/` to the source file where `log()` was called
|
||||
3. `lineInfo[2]`
|
||||
* This contains a stringified version of `__LINE__` which is the line number of the call to `log()`
|
||||
4. `lineInfo[3]`
|
||||
* This contains `__MODULE__` which is the name of the module the call to `log()` appeared in
|
||||
5. `lineInfo[4]`
|
||||
* This contains `__FUNCTION__` which is the name of the function `log()` was called in
|
||||
6. `lineInfo[5]`
|
||||
* This contains `__PRETTY_FUNCTION__` which is the same as above but with type information
|
||||
|
||||
The point of a `Context` object is also such that a custom transformer may expect a kind-of `Context` like a custom one (i.e. `CustomContext`)
|
||||
which perhaps a custom logger (kind-of `Logger`) can then have set certain fields in it.
|
||||
|
||||
## Creating a Logger
|
||||
|
||||
We now need to create a logger that makes use of our message transform, we can do so by creating an instance
|
||||
of the `Logger` class and passing in our `MessageTransform` as so:
|
||||
|
||||
```d
|
||||
Logger customLogger = new DefaultLogger(new CustomTranform());
|
||||
```
|
||||
|
||||
The above is all one needs to be able to pull off a custom transformation.
|
||||
|
||||
### Custom Logger
|
||||
|
||||
Custom loggers can also be created by sub-classing the `Logger` class and overriding the `logImpl(string)` method.
|
||||
The reason someone may want to do this is up to them. One easy to think of reason is to perhaps applying filtering
|
||||
of messages to be logged and skip them (as this method is where the I/O of printing out the logs normally happens).
|
||||
Another reason may be to log to a different data resource, the `DefaultLogger` writes to the file descriptor `0` (stdout),
|
||||
but you may want to log over a socket connection to a remote machine for example, or perhaps do several pieces of
|
||||
I/O for your logging. One can do that with a custom logger, you shoudl see `source/dlog/defaults.d` for the implementation
|
||||
of a custom logger, such as `DefaultLogger`.
|
||||
|
||||
## License
|
||||
|
||||
LGPL v3
|
|
@ -1,58 +1,137 @@
|
|||
/**
|
||||
* Defines some basic message
|
||||
* types, filters and handlers
|
||||
* that may be of use in
|
||||
* some combination or
|
||||
* seperate
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.basic;
|
||||
|
||||
import dlog.core;
|
||||
import std.stdio : File;
|
||||
|
||||
/**
|
||||
* Represents a basic message
|
||||
* with log level information
|
||||
* associated with it as well
|
||||
* as text
|
||||
*/
|
||||
public class BasicMessage : Message
|
||||
{
|
||||
/**
|
||||
* The text message
|
||||
*/
|
||||
private string text;
|
||||
|
||||
/**
|
||||
* Log level
|
||||
*/
|
||||
private Level level;
|
||||
|
||||
/**
|
||||
* Constructs a new `BasicMessage`
|
||||
* with the given text and log level
|
||||
*
|
||||
* Params:
|
||||
* text = the message text
|
||||
* level = the log level (default
|
||||
* is `Level.INFO`)
|
||||
*/
|
||||
this(string text, Level level = Level.INFO)
|
||||
{
|
||||
this.text = text;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty message
|
||||
* with the highest log level
|
||||
* (least verbose)
|
||||
*/
|
||||
this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text
|
||||
*
|
||||
* Params:
|
||||
* text = the message's
|
||||
* text
|
||||
*/
|
||||
public void setText(string text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text
|
||||
*
|
||||
* Returns: the text
|
||||
*/
|
||||
public string getText()
|
||||
{
|
||||
return this.text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the log level
|
||||
*
|
||||
* Returns: the level
|
||||
*/
|
||||
public Level getLevel()
|
||||
{
|
||||
return this.level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the log level
|
||||
*
|
||||
* Params:
|
||||
* level = the level
|
||||
*/
|
||||
public void setLevel(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
public class Context
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A file-based handler which
|
||||
* writes `BasicMessage`(s)
|
||||
* to a provided file
|
||||
*/
|
||||
public class FileHandler : Handler
|
||||
{
|
||||
import std.stdio : File;
|
||||
/**
|
||||
* File to write to
|
||||
*/
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Constrtucts a new
|
||||
* `FileHandler` with
|
||||
* the given file
|
||||
*
|
||||
* Params:
|
||||
* file = the file
|
||||
*/
|
||||
this(File file)
|
||||
{
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the message, does a
|
||||
* no-op if the message is
|
||||
* not a kind-of `BasicMessage`
|
||||
*
|
||||
* Params:
|
||||
* message = the message
|
||||
*/
|
||||
public void handle(Message message)
|
||||
{
|
||||
// Only handle BasicMessage(s)
|
||||
|
@ -90,15 +169,41 @@ public enum Level
|
|||
DEBUG
|
||||
}
|
||||
|
||||
/**
|
||||
* A level-based filter which
|
||||
* has a predicate which operates
|
||||
* on the value of a pointed-to
|
||||
* `Level` variable
|
||||
*/
|
||||
private class LevelFilter : Filter
|
||||
{
|
||||
/**
|
||||
* Address of the level var
|
||||
*/
|
||||
private Level* level;
|
||||
|
||||
/**
|
||||
* Constructs a new `LevelFilter`
|
||||
* with the given `Level*`
|
||||
*
|
||||
* Params:
|
||||
* level = the level address
|
||||
*/
|
||||
this(Level* level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given message according
|
||||
* to the current level. This will no-op
|
||||
* and always return `true` if the message
|
||||
* is not a kind-of `BasicMessage`
|
||||
*
|
||||
* Params:
|
||||
* message = the message
|
||||
* Returns: the verdict
|
||||
*/
|
||||
public bool filter(Message message)
|
||||
{
|
||||
// Only handle BasicMessage(s)
|
||||
|
@ -108,7 +213,7 @@ private class LevelFilter : Filter
|
|||
return bmesg.getLevel() <= *this.level;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,13 +238,4 @@ public class BasicLogger : Logger
|
|||
{
|
||||
return this.level;
|
||||
}
|
||||
}
|
||||
|
||||
public class ConsoleLogger : BasicLogger
|
||||
{
|
||||
this()
|
||||
{
|
||||
import std.stdio;
|
||||
addHandler(new FileHandler(stdout));
|
||||
}
|
||||
}
|
|
@ -1,29 +1,77 @@
|
|||
/**
|
||||
* Core logging services
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.core;
|
||||
|
||||
public class Message
|
||||
import std.container.slist : SList;
|
||||
import core.sync.mutex : Mutex;
|
||||
|
||||
/**
|
||||
* The base message type
|
||||
*/
|
||||
public abstract class Message
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the filtering
|
||||
* interface
|
||||
*/
|
||||
public interface Filter
|
||||
{
|
||||
/**
|
||||
* Filters the given message
|
||||
* returning a verdict
|
||||
* based on it
|
||||
*
|
||||
* Params:
|
||||
* message = the message
|
||||
* Returns: the verdct
|
||||
*/
|
||||
public bool filter(Message message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the message
|
||||
* transformation interface
|
||||
*/
|
||||
public interface Transform
|
||||
{
|
||||
/**
|
||||
* Transforms the given message
|
||||
*
|
||||
* Params:
|
||||
* message = the message input
|
||||
* Returns: the transformed
|
||||
* message
|
||||
*/
|
||||
public Message transform(Message message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the interface
|
||||
* for handling messages
|
||||
*/
|
||||
public interface Handler
|
||||
{
|
||||
/**
|
||||
* Handles the given message
|
||||
*
|
||||
* Params:
|
||||
* message = the message to
|
||||
* handle
|
||||
*/
|
||||
public void handle(Message message);
|
||||
}
|
||||
|
||||
import std.container.slist : SList;
|
||||
// import std.range : in;
|
||||
import core.sync.mutex : Mutex;
|
||||
|
||||
/**
|
||||
* Defines the base logger
|
||||
* and functionality associated
|
||||
* with it
|
||||
*/
|
||||
public abstract class Logger
|
||||
{
|
||||
private SList!(Transform) transforms;
|
||||
|
@ -31,12 +79,22 @@ public abstract class Logger
|
|||
private SList!(Handler) handlers;
|
||||
private Mutex lock; // Lock for attached handlers, filters and transforms
|
||||
|
||||
/**
|
||||
* Constructs a new logger
|
||||
*/
|
||||
this()
|
||||
{
|
||||
this.lock = new Mutex();
|
||||
}
|
||||
|
||||
// TODO: Handle duplicate?
|
||||
/**
|
||||
* Adds the given transform
|
||||
*
|
||||
* Params:
|
||||
* transform = the transform
|
||||
* to add
|
||||
*/
|
||||
public final void addTransform(Transform transform)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -50,6 +108,13 @@ public abstract class Logger
|
|||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
/**
|
||||
* Removes the given transform
|
||||
*
|
||||
* Params:
|
||||
* transform = the transform
|
||||
* to remove
|
||||
*/
|
||||
public final void removeTransform(Transform transform)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -63,6 +128,14 @@ public abstract class Logger
|
|||
}
|
||||
|
||||
// TODO: Handle duplicate?
|
||||
|
||||
/**
|
||||
* Adds the given filter
|
||||
*
|
||||
* Params:
|
||||
* filter = the filter
|
||||
* to add
|
||||
*/
|
||||
public final void addFilter(Filter filter)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -76,6 +149,14 @@ public abstract class Logger
|
|||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
|
||||
/**
|
||||
* Removes the given filter
|
||||
*
|
||||
* Params:
|
||||
* filter = the filter
|
||||
* to remove
|
||||
*/
|
||||
public final void removeFilter(Filter filter)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -89,6 +170,14 @@ public abstract class Logger
|
|||
}
|
||||
|
||||
// TODO: Handle duplicate?
|
||||
|
||||
/**
|
||||
* Adds the given handler
|
||||
*
|
||||
* Params:
|
||||
* handler = the handler
|
||||
* to add
|
||||
*/
|
||||
public final void addHandler(Handler handler)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -102,6 +191,14 @@ public abstract class Logger
|
|||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
|
||||
/**
|
||||
* Removes the given handler
|
||||
*
|
||||
* Params:
|
||||
* handler = the handler
|
||||
* to remove
|
||||
*/
|
||||
public final void removeHandler(Handler handler)
|
||||
{
|
||||
scope(exit)
|
||||
|
@ -114,11 +211,17 @@ public abstract class Logger
|
|||
this.handlers.linearRemoveElement(handler);
|
||||
}
|
||||
|
||||
// Logs an actual message
|
||||
//
|
||||
// This first passes it over all filters
|
||||
// then to a transform and then copies
|
||||
// to each handler
|
||||
/**
|
||||
* Logs the provided message by processing
|
||||
* it through all the filters, and if
|
||||
* the verdict is `true` then transforms
|
||||
* the message via all transformers
|
||||
* and finally dispatches said message
|
||||
* to all attached handlers
|
||||
*
|
||||
* Params:
|
||||
* message = the message
|
||||
*/
|
||||
public final void log(Message message)
|
||||
{
|
||||
scope(exit)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
/**
|
||||
* Default logger
|
||||
*/
|
||||
* Default logger
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.defaults;
|
||||
|
||||
import dlog.core;
|
||||
import dlog.basic : BasicMessage, FileHandler, Level;
|
||||
import dlog.basic : BasicMessage, FileHandler, Level, BasicLogger;
|
||||
import std.stdio : stdout;
|
||||
import std.conv : to;
|
||||
import dlog.utilities : flatten;
|
||||
|
@ -19,7 +21,7 @@ import std.datetime.systime : Clock, SysTime;
|
|||
* message transformation and supports
|
||||
* the basic levels of logging.
|
||||
*/
|
||||
public final class DefaultLogger : Logger
|
||||
public final class DefaultLogger : BasicLogger
|
||||
{
|
||||
/**
|
||||
* The joiner for multi-argument
|
||||
|
@ -186,6 +188,9 @@ unittest
|
|||
{
|
||||
DefaultLogger logger = new DefaultLogger();
|
||||
|
||||
// Set logging level to at least INFO
|
||||
logger.setLevel(Level.INFO);
|
||||
|
||||
alias testParameters = AliasSeq!("This is a log message", 1.1, true, [1,2,3], 'f', logger);
|
||||
|
||||
|
||||
|
@ -200,6 +205,10 @@ unittest
|
|||
|
||||
// Same as above but with a custom joiner set
|
||||
logger = new DefaultLogger("(-)");
|
||||
|
||||
// Set logging level to at least INFO
|
||||
logger.setLevel(Level.INFO);
|
||||
|
||||
logger.info(testParameters);
|
||||
|
||||
writeln();
|
||||
|
@ -210,10 +219,13 @@ unittest
|
|||
*/
|
||||
unittest
|
||||
{
|
||||
// Create a default logger with the default joiner
|
||||
DefaultLogger logger = new DefaultLogger();
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
// Set logging level to at least INFO
|
||||
logger.setLevel(Level.INFO);
|
||||
|
||||
// Log some stuff
|
||||
logger.info(["a", "b", "c", "d"], [1, 2], true);
|
||||
|
||||
writeln();
|
||||
|
@ -225,10 +237,11 @@ unittest
|
|||
*/
|
||||
unittest
|
||||
{
|
||||
// Create a default logger with the default joiner
|
||||
DefaultLogger logger = new DefaultLogger();
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
// Set logging level to at least DEBUG
|
||||
logger.setLevel(Level.DEBUG);
|
||||
|
||||
// Test out `error()`
|
||||
logger.error(["woah", "LEVELS!"], 69.420);
|
||||
|
@ -242,5 +255,9 @@ unittest
|
|||
// Test out `debug_()`
|
||||
logger.debug_(["woah", "LEVELS!"], 69.420);
|
||||
|
||||
// Should not be able to see this
|
||||
logger.setLevel(Level.INFO);
|
||||
logger.debug_("Can't see me!");
|
||||
|
||||
writeln();
|
||||
}
|
|
@ -1,24 +1,16 @@
|
|||
/**
|
||||
* DLog logging facilities
|
||||
*/
|
||||
* The DLog logging framework
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog;
|
||||
|
||||
/**
|
||||
* Core logging services
|
||||
*/
|
||||
* Core logging services
|
||||
*/
|
||||
public import dlog.core;
|
||||
|
||||
/**
|
||||
* Transformations
|
||||
*/
|
||||
// public import dlog.transform;
|
||||
|
||||
/**
|
||||
* Context for logging
|
||||
*/
|
||||
// public import dlog.context;
|
||||
|
||||
/**
|
||||
* Default logger
|
||||
*/
|
||||
* Default logger
|
||||
*/
|
||||
public import dlog.defaults;
|
|
@ -1,5 +1,7 @@
|
|||
/**
|
||||
* Helper functions
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.utilities;
|
||||
|
||||
|
|
Loading…
Reference in New Issue