Compare commits

...

9 Commits

Author SHA1 Message Date
Tristan B. Velloza Kildaire 1d57ab1529 ConsoleLogger
- Removed
2024-04-09 19:31:52 +02:00
Tristan B. Velloza Kildaire b29e2eb1c7 LevelFilter
- Now returns a `true` verdict if the incoming `Message` is NOT a kind-of `BasicMessage`
2024-04-09 19:31:22 +02:00
Tristan B. Velloza Kildaire 6be4be3534 Basics
- More docs
2024-04-09 19:30:56 +02:00
Tristan B. Velloza Kildaire 49f1f70f28 Basics
- Added some missing docs
2024-04-09 19:27:15 +02:00
Tristan B. Velloza Kildaire 986a3d3bdf RWADME
- Fixed up
2024-04-09 19:20:25 +02:00
Tristan B. Velloza Kildaire 8bb28de027 DefaultLogger
- Now is a kind-of `BasicLogger` (gives us log-level support

Defaults (unittests)

- Updated
2024-04-09 19:14:18 +02:00
Tristan B. Velloza Kildaire 9d8f7af4a3 Core
- Last few docs added
2024-04-09 19:04:31 +02:00
Tristan B. Velloza Kildaire 66adb8f700 Core
- Added more docs
2024-04-09 18:59:25 +02:00
Tristan B. Velloza Kildaire c53558f847 Refactored
- Cleaned up
2024-04-09 18:56:33 +02:00
6 changed files with 296 additions and 143 deletions

129
README.md
View File

@ -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

View File

@ -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));
}
}

View File

@ -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)

View File

@ -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();
}

View File

@ -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;

View File

@ -1,5 +1,7 @@
/**
* Helper functions
*
* Authors: Tristan Brice Velloza Kildaire (deavmi)
*/
module dlog.utilities;