mirror of https://github.com/deavmi/dlog.git
Compare commits
43 Commits
|
@ -0,0 +1,32 @@
|
|||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
name: D
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dlang-community/setup-dlang@4c99aa991ce7d19dd3064de0a4f2f6b2f152e2d7
|
||||
|
||||
- name: 'Build & Test'
|
||||
run: |
|
||||
# Build the project, with its main file included, without unittests
|
||||
dub build --compiler=$DC
|
||||
# Build and run tests, as defined by `unittest` configuration
|
||||
# In this mode, `mainSourceFile` is excluded and `version (unittest)` are included
|
||||
# See https://dub.pm/package-format-json.html#configurations
|
||||
dub test --compiler=$DC
|
|
@ -2,3 +2,4 @@
|
|||
*test-library*
|
||||
*.lst
|
||||
libdlog.a
|
||||
dub.selections.json
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
120
README.md
120
README.md
|
@ -1,13 +1,24 @@
|
|||
![](branding/logo.png)
|
||||
<p align="center">
|
||||
<img src="branding/logo.png" width=220>
|
||||
</p>
|
||||
|
||||
dlog
|
||||
====
|
||||
<br>
|
||||
|
||||
<h1 align="center">dlog</h1>
|
||||
|
||||
<h3 align="center"><i><b>Simple and modular logging library</i></b></h3>
|
||||
|
||||
---
|
||||
|
||||
<br>
|
||||
<br
|
||||
|
||||
**Simple and modular logging library**
|
||||
|
||||
`[2021-Dec-23 11:17:35.3527637] (source/dlog/testing/thing.d:12): This is a log message`
|
||||
|
||||
---
|
||||
|
||||
[![D](https://github.com/deavmi/dlog/actions/workflows/d.yml/badge.svg)](https://github.com/deavmi/dlog/actions/workflows/d.yml)
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -26,38 +37,47 @@ 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
|
||||
|
||||
If you want to immediately begin logging text usin the defaults and don't care about implementing your own transformations then you can
|
||||
simply use the defaulkt logger as follows:
|
||||
If you want to immediately begin logging text using the defaults and don't care about implementing your own transformations then you can
|
||||
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.
|
||||
You can see the [full API](https://dlog.dpldocs.info/) for more information.
|
||||
|
||||
### Custom loggers
|
||||
|
||||
|
@ -67,64 +87,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 CustomTransform : Transform
|
||||
{
|
||||
public override string transform(string text, string[] 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 string array as `string[] context`
|
||||
the contents of which are described below:
|
||||
|
||||
1. `context[0]`
|
||||
* This contains `__FILE_FULL_PATH__` which is the full path (absolute) to the source file where `log()` was called
|
||||
2. `context[1]`
|
||||
* This contains `__FILE__` which is the path (starting at `source/` to the source file where `log()` was called
|
||||
3. `context[2]`
|
||||
* This contains a stringified version of `__LINE__` which is the line number of the call to `log()`
|
||||
4. `context[3]`
|
||||
* This contains `__MODULE__` which is the name of the module the call to `log()` appeared in
|
||||
5. `context[4]`
|
||||
* This contains `__FUNCTION__` which is the name of the function `log()` was called in
|
||||
6. `context[5]`
|
||||
* This contains `__PRETTY_FUNCTION__` which is the same as above but with type information
|
||||
7. `context[5..X]`
|
||||
* This contains optional extras that were set when the `log()` function was called with the `contextExtras` set
|
||||
* Example: `log("Hello world", contextExtras=[this])`
|
||||
|
||||
#### 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
|
||||
|
||||
LGPLv2
|
||||
LGPL v3
|
||||
|
|
9
dub.json
9
dub.json
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
"homepage": "https://deavmi.assigned.network/projects/dlog",
|
||||
"authors": [
|
||||
"Tristan B. Kildaire"
|
||||
"Tristan B. Velloza Kildaire"
|
||||
],
|
||||
"copyright": "Copyright © 2021, Tristan B. Kildaire",
|
||||
"copyright": "Copyright © 2023, Tristan B. Kildaire",
|
||||
"description": "Simple and modular logging library",
|
||||
"license": "LGPLv2",
|
||||
"license": "LGPL 3.0",
|
||||
"name": "dlog",
|
||||
"targetType": "library",
|
||||
"targetType": "library"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file-based handler which
|
||||
* writes `BasicMessage`(s)
|
||||
* to a provided file
|
||||
*/
|
||||
public class FileHandler : Handler
|
||||
{
|
||||
/**
|
||||
* 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)
|
||||
BasicMessage bmesg = cast(BasicMessage)message;
|
||||
if(bmesg)
|
||||
{
|
||||
file.write(bmesg.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logging level
|
||||
*/
|
||||
public enum Level
|
||||
{
|
||||
/**
|
||||
* Error message
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
/**
|
||||
* Informative message
|
||||
*/
|
||||
INFO,
|
||||
|
||||
/**
|
||||
* Warning message
|
||||
*/
|
||||
WARN,
|
||||
|
||||
/**
|
||||
* Debugging message
|
||||
*/
|
||||
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)
|
||||
BasicMessage bmesg = cast(BasicMessage)message;
|
||||
if(bmesg)
|
||||
{
|
||||
return bmesg.getLevel() <= *this.level;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic logger which has support
|
||||
* for log levels
|
||||
*/
|
||||
public class BasicLogger : Logger
|
||||
{
|
||||
/**
|
||||
* The current log level
|
||||
*/
|
||||
private Level level;
|
||||
|
||||
/**
|
||||
* Constructs a new `BasicLogger`
|
||||
*/
|
||||
this()
|
||||
{
|
||||
// Attach a new level-filter
|
||||
// with access to our current
|
||||
// level
|
||||
addFilter(new LevelFilter(&level));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the log level
|
||||
*
|
||||
* Params:
|
||||
* level = the new
|
||||
* level
|
||||
*/
|
||||
public final void setLevel(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the current log
|
||||
* level
|
||||
*
|
||||
* Returns: the current level
|
||||
*/
|
||||
public final Level getLevel()
|
||||
{
|
||||
return this.level;
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
module dlog.context;
|
||||
|
||||
import std.conv : to;
|
||||
|
||||
|
||||
public enum Level
|
||||
{
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
DEBUG
|
||||
}
|
||||
|
||||
/**
|
||||
* Context that is passed in with the call to log
|
||||
*/
|
||||
public class Context
|
||||
{
|
||||
private CompilationInfo lineInfo;
|
||||
private Level level;
|
||||
|
||||
this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public final void setLineInfo(CompilationInfo lineInfo)
|
||||
{
|
||||
this.lineInfo = lineInfo;
|
||||
}
|
||||
|
||||
public final CompilationInfo getLineInfo()
|
||||
{
|
||||
return lineInfo;
|
||||
}
|
||||
|
||||
public final Level getLevel()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
|
||||
public final void setLevel(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Information obtained during compilation time (if any)
|
||||
*/
|
||||
public struct CompilationInfo
|
||||
{
|
||||
public string fullFilePath;
|
||||
public string file;
|
||||
public ulong line;
|
||||
public string moduleName;
|
||||
public string functionName;
|
||||
public string prettyFunctionName;
|
||||
|
||||
/**
|
||||
* Constructs the compilation information with the provided
|
||||
* parameters
|
||||
*
|
||||
* Params:
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
this(string fullFilePath, string file, ulong line, string moduleName, string functionName, string prettyFunctionName)
|
||||
{
|
||||
this.fullFilePath = fullFilePath;
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
this.moduleName = moduleName;
|
||||
this.functionName = functionName;
|
||||
this.prettyFunctionName = prettyFunctionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens the known compile-time information into a string array
|
||||
*
|
||||
* Returns: a string[]
|
||||
*/
|
||||
public string[] toArray()
|
||||
{
|
||||
return [fullFilePath, file, to!(string)(line), moduleName, functionName, prettyFunctionName];
|
||||
}
|
||||
}
|
|
@ -1,439 +1,253 @@
|
|||
/**
|
||||
* Core module containing types pertaining to the base Logger
|
||||
* class and base MessageTransform class (along with a default
|
||||
* transform, DefaultTransform)
|
||||
*/
|
||||
/**
|
||||
* Core logging services
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.core;
|
||||
|
||||
import std.conv : to;
|
||||
import std.range : join;
|
||||
import dlog.transform : MessageTransform;
|
||||
import dlog.defaults;
|
||||
import dlog.context : Context, CompilationInfo, Level;
|
||||
import dlog.utilities : flatten;
|
||||
import std.container.slist : SList;
|
||||
import core.sync.mutex : Mutex;
|
||||
|
||||
/**
|
||||
* Logger
|
||||
*
|
||||
* Represents a logger instance
|
||||
*/
|
||||
public class Logger
|
||||
{
|
||||
/* Starting transformation */
|
||||
private MessageTransform messageTransform;
|
||||
|
||||
/* The multiple argument joiner */
|
||||
private string multiArgJoiner;
|
||||
|
||||
/**
|
||||
* Constructs a new Logger with the default
|
||||
* MessageTransform
|
||||
*
|
||||
* Params:
|
||||
* multiArgJoiner = optional joiner for segmented prints (default is " ")
|
||||
*/
|
||||
this(string multiArgJoiner = " ")
|
||||
{
|
||||
this(new DefaultTransform(), multiArgJoiner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Logger with the provided
|
||||
* custom message transform
|
||||
* Params:
|
||||
* messageTransform = the message transform to use
|
||||
* multiArgJoiner = optional joiner for segmented prints (default is " ")
|
||||
*/
|
||||
this(MessageTransform messageTransform, string multiArgJoiner = " ")
|
||||
{
|
||||
this.messageTransform = messageTransform;
|
||||
this.multiArgJoiner = multiArgJoiner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an arbitrary amount of arguments, convert each to a string
|
||||
* and return it as an array joined by the joiner
|
||||
*
|
||||
* Params:
|
||||
* segments = alias sequence
|
||||
* Returns: a string of the argumnets
|
||||
*/
|
||||
public string args(TextType...)(TextType segments)
|
||||
{
|
||||
/* The flattened components */
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string joined = join(components, multiArgJoiner);
|
||||
|
||||
return joined;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Logs the given string using the default context
|
||||
*
|
||||
* Params:
|
||||
* text = the string to log
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void log(string text, string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Use the default context `Context` */
|
||||
Context defaultContext = new Context();
|
||||
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
defaultContext.setLineInfo(compilationInfo);
|
||||
|
||||
/* Call the log */
|
||||
logc(defaultContext, text, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using the default context an arbitrary amount of arguments
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void log(TextType...)(TextType segments, string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/**
|
||||
* Grab at compile-time all arguments and generate runtime code to add them to `components`
|
||||
*/
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string messageOut = join(components, multiArgJoiner);
|
||||
|
||||
/* Call the log (with text and default context) */
|
||||
log(messageOut, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given string using the provided context
|
||||
*
|
||||
* Params:
|
||||
* context = the custom context to use
|
||||
* text = the string to log
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void logc(Context context, string text, string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
context.setLineInfo(compilationInfo);
|
||||
|
||||
/* Apply the transformation on the message */
|
||||
string transformedMesage = messageTransform.execute(text, context);
|
||||
|
||||
/* Call the underlying logger implementation */
|
||||
logImpl(transformedMesage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using the default context an arbitrary amount of arguments
|
||||
* specifically setting the context's level to ERROR
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void error(TextType...)(TextType segments,
|
||||
string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Use the default context `Context` */
|
||||
Context defaultContext = new Context();
|
||||
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
defaultContext.setLineInfo(compilationInfo);
|
||||
|
||||
/* Set the level to ERROR */
|
||||
defaultContext.setLevel(Level.ERROR);
|
||||
|
||||
/**
|
||||
* Grab at compile-time all arguments and generate runtime code to add them to `components`
|
||||
*/
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string messageOut = join(components, multiArgJoiner);
|
||||
|
||||
/* Call the log */
|
||||
logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using the default context an arbitrary amount of arguments
|
||||
* specifically setting the context's level to INFO
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void info(TextType...)(TextType segments,
|
||||
string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Use the default context `Context` */
|
||||
Context defaultContext = new Context();
|
||||
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
defaultContext.setLineInfo(compilationInfo);
|
||||
|
||||
/* Set the level to INFO */
|
||||
defaultContext.setLevel(Level.INFO);
|
||||
|
||||
/**
|
||||
* Grab at compile-time all arguments and generate runtime code to add them to `components`
|
||||
*/
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string messageOut = join(components, multiArgJoiner);
|
||||
|
||||
/* Call the log */
|
||||
logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using the default context an arbitrary amount of arguments
|
||||
* specifically setting the context's level to WARN
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void warn(TextType...)(TextType segments,
|
||||
string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Use the default context `Context` */
|
||||
Context defaultContext = new Context();
|
||||
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
defaultContext.setLineInfo(compilationInfo);
|
||||
|
||||
/* Set the level to WARN */
|
||||
defaultContext.setLevel(Level.WARN);
|
||||
|
||||
/**
|
||||
* Grab at compile-time all arguments and generate runtime code to add them to `components`
|
||||
*/
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string messageOut = join(components, multiArgJoiner);
|
||||
|
||||
/* Call the log */
|
||||
logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs using the default context an arbitrary amount of arguments
|
||||
* specifically setting the context's level to DEBUG
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
* __FILE_FULL_PATH__ = compile time usage file
|
||||
* __FILE__ = compile time usage file (relative)
|
||||
* __LINE__ = compile time usage line number
|
||||
* __MODULE__ = compile time usage module
|
||||
* __FUNCTION__ = compile time usage function
|
||||
* __PRETTY_FUNCTION__ = compile time usage function (pretty)
|
||||
*/
|
||||
public final void debug_(TextType...)(TextType segments,
|
||||
string c1 = __FILE_FULL_PATH__,
|
||||
string c2 = __FILE__, ulong c3 = __LINE__,
|
||||
string c4 = __MODULE__, string c5 = __FUNCTION__,
|
||||
string c6 = __PRETTY_FUNCTION__)
|
||||
{
|
||||
/* Use the default context `Context` */
|
||||
Context defaultContext = new Context();
|
||||
|
||||
/* Build up the line information */
|
||||
CompilationInfo compilationInfo = CompilationInfo(c1, c2, c3, c4, c5, c6);
|
||||
|
||||
/* Set the line information in the context */
|
||||
defaultContext.setLineInfo(compilationInfo);
|
||||
|
||||
/* Set the level to DEBUG */
|
||||
defaultContext.setLevel(Level.DEBUG);
|
||||
|
||||
/**
|
||||
* Grab at compile-time all arguments and generate runtime code to add them to `components`
|
||||
*/
|
||||
string[] components = flatten(segments);
|
||||
|
||||
/* Join all `components` into a single string */
|
||||
string messageOut = join(components, multiArgJoiner);
|
||||
|
||||
/* Call the log */
|
||||
logc(defaultContext, messageOut, c1, c2, c3, c4, c5, c6);
|
||||
}
|
||||
|
||||
/* You can also call using `dbg` */
|
||||
public alias dbg = debug_;
|
||||
|
||||
/**
|
||||
* Logging implementation, this is where the final
|
||||
* transformed text will be transferred to and finally
|
||||
* logged
|
||||
*
|
||||
* Params:
|
||||
* message = the message to log
|
||||
*/
|
||||
protected abstract void logImpl(string message);
|
||||
}
|
||||
|
||||
|
||||
version(unittest)
|
||||
{
|
||||
import std.meta : AliasSeq;
|
||||
import std.stdio : writeln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the DefaultLogger
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
Logger logger = new DefaultLogger();
|
||||
|
||||
alias testParameters = AliasSeq!("This is a log message", 1.1, true, [1,2,3], 'f', logger);
|
||||
|
||||
|
||||
// Test various types one-by-one
|
||||
static foreach(testParameter; testParameters)
|
||||
{
|
||||
logger.log(testParameter);
|
||||
}
|
||||
|
||||
// Test various parameters (of various types) all at once
|
||||
logger.log(testParameters);
|
||||
|
||||
// Same as above but with a custom joiner set
|
||||
logger = new DefaultLogger("(-)");
|
||||
logger.log(testParameters);
|
||||
|
||||
writeln();
|
||||
}
|
||||
|
||||
/**
|
||||
* Printing out some mixed data-types, also using a DEFAULT context
|
||||
/**
|
||||
* The base message type
|
||||
*/
|
||||
unittest
|
||||
public abstract class Message
|
||||
{
|
||||
Logger logger = new DefaultLogger();
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
logger.log(["a", "b", "c", "d"], [1, 2], true);
|
||||
|
||||
writeln();
|
||||
}
|
||||
|
||||
/**
|
||||
* Printing out some mixed data-types, also using a CUSTOM context
|
||||
/**
|
||||
* Defines the filtering
|
||||
* interface
|
||||
*/
|
||||
unittest
|
||||
public interface Filter
|
||||
{
|
||||
Logger logger = new DefaultLogger();
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
|
||||
// Create a custom context
|
||||
Context customContext = new Context();
|
||||
|
||||
// Log with the custom context
|
||||
logger.logc(customContext, logger.args(["an", "array"], 1, "hello", true));
|
||||
|
||||
writeln();
|
||||
/**
|
||||
* Filters the given message
|
||||
* returning a verdict
|
||||
* based on it
|
||||
*
|
||||
* Params:
|
||||
* message = the message
|
||||
* Returns: the verdct
|
||||
*/
|
||||
public bool filter(Message message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Printing out some mixed data-types, also using a DEFAULT context
|
||||
* but also testing out the `error()`, `warn()`, `info()` and `debug()`
|
||||
/**
|
||||
* Defines the message
|
||||
* transformation interface
|
||||
*/
|
||||
unittest
|
||||
public interface Transform
|
||||
{
|
||||
Logger logger = new DefaultLogger();
|
||||
/**
|
||||
* Transforms the given message
|
||||
*
|
||||
* Params:
|
||||
* message = the message input
|
||||
* Returns: the transformed
|
||||
* message
|
||||
*/
|
||||
public Message transform(Message message);
|
||||
}
|
||||
|
||||
// Create a default logger with the default joiner
|
||||
logger = new DefaultLogger();
|
||||
/**
|
||||
* Defines the interface
|
||||
* for handling messages
|
||||
*/
|
||||
public interface Handler
|
||||
{
|
||||
/**
|
||||
* Handles the given message
|
||||
*
|
||||
* Params:
|
||||
* message = the message to
|
||||
* handle
|
||||
*/
|
||||
public void handle(Message message);
|
||||
}
|
||||
|
||||
// Test out `error()`
|
||||
logger.error(["woah", "LEVELS!"], 69.420);
|
||||
/**
|
||||
* Defines the base logger
|
||||
* and functionality associated
|
||||
* with it
|
||||
*/
|
||||
public abstract class Logger
|
||||
{
|
||||
private SList!(Transform) transforms;
|
||||
private SList!(Filter) filters;
|
||||
private SList!(Handler) handlers;
|
||||
private Mutex lock; // Lock for attached handlers, filters and transforms
|
||||
|
||||
// Test out `info()`
|
||||
logger.info(["woah", "LEVELS!"], 69.420);
|
||||
/**
|
||||
* Constructs a new logger
|
||||
*/
|
||||
this()
|
||||
{
|
||||
this.lock = new Mutex();
|
||||
}
|
||||
|
||||
// Test out `warn()`
|
||||
logger.warn(["woah", "LEVELS!"], 69.420);
|
||||
// TODO: Handle duplicate?
|
||||
/**
|
||||
* Adds the given transform
|
||||
*
|
||||
* Params:
|
||||
* transform = the transform
|
||||
* to add
|
||||
*/
|
||||
public final void addTransform(Transform transform)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
// Test out `debug_()`
|
||||
logger.debug_(["woah", "LEVELS!"], 69.420);
|
||||
this.lock.lock();
|
||||
|
||||
writeln();
|
||||
this.transforms.insertAfter(this.transforms[], transform);
|
||||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
/**
|
||||
* Removes the given transform
|
||||
*
|
||||
* Params:
|
||||
* transform = the transform
|
||||
* to remove
|
||||
*/
|
||||
public final void removeTransform(Transform transform)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
this.transforms.linearRemoveElement(transform);
|
||||
}
|
||||
|
||||
// TODO: Handle duplicate?
|
||||
|
||||
/**
|
||||
* Adds the given filter
|
||||
*
|
||||
* Params:
|
||||
* filter = the filter
|
||||
* to add
|
||||
*/
|
||||
public final void addFilter(Filter filter)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
this.filters.insertAfter(this.filters[], filter);
|
||||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
|
||||
/**
|
||||
* Removes the given filter
|
||||
*
|
||||
* Params:
|
||||
* filter = the filter
|
||||
* to remove
|
||||
*/
|
||||
public final void removeFilter(Filter filter)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
this.filters.linearRemoveElement(filter);
|
||||
}
|
||||
|
||||
// TODO: Handle duplicate?
|
||||
|
||||
/**
|
||||
* Adds the given handler
|
||||
*
|
||||
* Params:
|
||||
* handler = the handler
|
||||
* to add
|
||||
*/
|
||||
public final void addHandler(Handler handler)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
this.handlers.insertAfter(this.handlers[], handler);
|
||||
}
|
||||
|
||||
// TODO: Hanmdle not found explicitly?
|
||||
|
||||
/**
|
||||
* Removes the given handler
|
||||
*
|
||||
* Params:
|
||||
* handler = the handler
|
||||
* to remove
|
||||
*/
|
||||
public final void removeHandler(Handler handler)
|
||||
{
|
||||
scope(exit)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
this.handlers.linearRemoveElement(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)
|
||||
{
|
||||
this.lock.unlock();
|
||||
}
|
||||
|
||||
this.lock.lock();
|
||||
|
||||
foreach(Filter filter; this.filters)
|
||||
{
|
||||
if(!filter.filter(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Message curMessage = message;
|
||||
foreach(Transform transform; this.transforms)
|
||||
{
|
||||
curMessage = transform.transform(curMessage);
|
||||
}
|
||||
|
||||
foreach(Handler handler; this.handlers)
|
||||
{
|
||||
handler.handle(curMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +1,129 @@
|
|||
/**
|
||||
* Includes defaults such as the DefaultLogger
|
||||
*/
|
||||
* Default logger
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.defaults;
|
||||
|
||||
import dlog.core : Logger;
|
||||
import dlog.transform : MessageTransform;
|
||||
import dlog.context : Context, CompilationInfo;
|
||||
import dlog.core;
|
||||
import dlog.basic : BasicMessage, FileHandler, Level, BasicLogger;
|
||||
import std.stdio : stdout;
|
||||
import std.conv : to;
|
||||
import dlog.utilities : flatten;
|
||||
import std.array :join;
|
||||
import std.datetime.systime : Clock, SysTime;
|
||||
|
||||
/**
|
||||
* DefaultLogger
|
||||
*
|
||||
* The default logger logs to standard output (fd 0)
|
||||
* The default logger logs using
|
||||
* a pretty stock-standard (non-colored)
|
||||
* message transformation and supports
|
||||
* the basic levels of logging.
|
||||
*/
|
||||
public final class DefaultLogger : Logger
|
||||
public final class DefaultLogger : BasicLogger
|
||||
{
|
||||
/**
|
||||
* The joiner for multi-argument
|
||||
* log messages
|
||||
*/
|
||||
private string multiArgJoiner;
|
||||
|
||||
/**
|
||||
* Constructs a new default logger
|
||||
*
|
||||
* Params:
|
||||
* multiArgJoiner = the joiner to use
|
||||
*/
|
||||
this(string multiArgJoiner = " ")
|
||||
{
|
||||
/* Use the DefaultTransform */
|
||||
super(multiArgJoiner);
|
||||
this.multiArgJoiner = multiArgJoiner;
|
||||
|
||||
addTransform(new DefaultTransform());
|
||||
addHandler(new FileHandler(stdout));
|
||||
}
|
||||
|
||||
protected override void logImpl(string message)
|
||||
/**
|
||||
* Logs the given message of an arbitrary amount of
|
||||
* arguments and specifically sets the level to ERROR
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
*/
|
||||
public void error(TextType...)(TextType segments)
|
||||
{
|
||||
import std.stdio : write;
|
||||
write(message);
|
||||
doLog(segments, Level.ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given message of an arbitrary amount of
|
||||
* arguments and specifically sets the level to INFO
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
*/
|
||||
public void info(TextType...)(TextType segments)
|
||||
{
|
||||
doLog(segments, Level.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given message of an arbitrary amount of
|
||||
* arguments and specifically sets the level to WARN
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
*/
|
||||
public void warn(TextType...)(TextType segments)
|
||||
{
|
||||
doLog(segments, Level.WARN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the given message of an arbitrary amount of
|
||||
* arguments and specifically sets the level to DEBUG
|
||||
*
|
||||
* Params:
|
||||
* segments = the arbitrary argumnets (alias sequence)
|
||||
*/
|
||||
public void debug_(TextType...)(TextType segments)
|
||||
{
|
||||
doLog(segments, Level.DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual logging
|
||||
* by packing up everything before
|
||||
* sending it to the `log(Message)`
|
||||
* method
|
||||
*
|
||||
* Params:
|
||||
* segments = the compile-time segments
|
||||
* level = the log level to use
|
||||
*/
|
||||
private void doLog(TextType...)(TextType segments, Level level)
|
||||
{
|
||||
/* Create a new basic message */
|
||||
BasicMessage message = new BasicMessage();
|
||||
|
||||
/* Set the level */
|
||||
message.setLevel(level);
|
||||
|
||||
/**
|
||||
* Grab all compile-time arguments and make them
|
||||
* into an array, then join them together and
|
||||
* set that text as the message's text
|
||||
*/
|
||||
message.setText(join(flatten(segments), multiArgJoiner));
|
||||
|
||||
/* Log this message */
|
||||
log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for debug_
|
||||
*/
|
||||
public alias dbg = debug_;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,40 +131,133 @@ public final class DefaultLogger : Logger
|
|||
*
|
||||
* Provides a transformation of the kind
|
||||
*
|
||||
* [date+time] (srcFile:lineNumber): message `\n`
|
||||
* [date+time] (level): message `\n`
|
||||
*/
|
||||
public final class DefaultTransform : MessageTransform
|
||||
private final class DefaultTransform : Transform
|
||||
{
|
||||
/**
|
||||
* Performs the default transformation
|
||||
* Performs the default transformation.
|
||||
* If the message is not a `BasicMessage`
|
||||
* then no transformation occurs.
|
||||
*
|
||||
* Params:
|
||||
* text = text input to transform
|
||||
* context = the context (if any)
|
||||
* Returns: the transformed text
|
||||
* message = the message to transform
|
||||
* Returns: the transformed message
|
||||
*/
|
||||
public override string transform(string text, Context ctx)
|
||||
public Message transform(Message message)
|
||||
{
|
||||
/* Extract the context */
|
||||
string[] context = ctx.getLineInfo().toArray();
|
||||
// Only handle BasicMessage(s)
|
||||
BasicMessage bmesg = cast(BasicMessage)message;
|
||||
if(bmesg is null)
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
string message;
|
||||
string text;
|
||||
|
||||
/* Date and time */
|
||||
import std.datetime.systime : Clock, SysTime;
|
||||
SysTime currTime = Clock.currTime();
|
||||
import std.conv : to;
|
||||
string timestamp = to!(string)(currTime);
|
||||
message = "["~timestamp~"]";
|
||||
text = "["~timestamp~"]";
|
||||
|
||||
/* Module information */
|
||||
message = message ~ "\t(";
|
||||
message = message ~ context[1]~":"~context[2];
|
||||
message = message ~ "): "~text;
|
||||
/* Level */
|
||||
text = text ~ "\t(";
|
||||
text = text ~ to!(string)(bmesg.getLevel());
|
||||
text = text ~ "): "~bmesg.getText();
|
||||
|
||||
/* Add trailing newline */
|
||||
message = message ~ '\n';
|
||||
text = text ~ '\n';
|
||||
|
||||
/* Store the updated text */
|
||||
bmesg.setText(text);
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
version(unittest)
|
||||
{
|
||||
import std.meta : AliasSeq;
|
||||
import std.stdio : writeln;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the DefaultLogger
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
// Test various types one-by-one
|
||||
static foreach(testParameter; testParameters)
|
||||
{
|
||||
logger.info(testParameter);
|
||||
}
|
||||
|
||||
// Test various parameters (of various types) all at once
|
||||
logger.info(testParameters);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Printing out some mixed data-types, also using a DEFAULT context
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
// Create a default logger with the default joiner
|
||||
DefaultLogger 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Printing out some mixed data-types, also using a DEFAULT context
|
||||
* but also testing out the `error()`, `warn()`, `info()` and `debug()`
|
||||
*/
|
||||
unittest
|
||||
{
|
||||
// Create a default logger with the default joiner
|
||||
DefaultLogger logger = new DefaultLogger();
|
||||
|
||||
// Set logging level to at least DEBUG
|
||||
logger.setLevel(Level.DEBUG);
|
||||
|
||||
// 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);
|
||||
|
||||
// Should not be able to see this
|
||||
logger.setLevel(Level.INFO);
|
||||
logger.debug_("Can't see me!");
|
||||
|
||||
writeln();
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
/**
|
||||
* Package definition module
|
||||
*
|
||||
* Import this to use dlog
|
||||
*/
|
||||
* The DLog logging framework
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog;
|
||||
|
||||
/**
|
||||
* Core logging services
|
||||
*/
|
||||
public import dlog.core;
|
||||
public import dlog.transform;
|
||||
public import dlog.defaults;
|
||||
public import dlog.context;
|
||||
|
||||
/**
|
||||
* Default logger
|
||||
*/
|
||||
public import dlog.defaults;
|
|
@ -1,49 +0,0 @@
|
|||
module dlog.transform;
|
||||
|
||||
import dlog.context : Context;
|
||||
|
||||
/**
|
||||
* MessageTransform
|
||||
*
|
||||
* A message transform takes in text, applies
|
||||
* a transformation to it and outputs said text
|
||||
*
|
||||
* Transforms may be chained
|
||||
*/
|
||||
public abstract class MessageTransform
|
||||
{
|
||||
/* Next transformation (if any) */
|
||||
private MessageTransform chainedTransform;
|
||||
|
||||
/**
|
||||
* The actual textual transformation.
|
||||
*
|
||||
* This is to be implemented by sub-classes
|
||||
*/
|
||||
public abstract string transform(string text, Context context);
|
||||
|
||||
/**
|
||||
* Chain a transform
|
||||
*/
|
||||
public final void chain(MessageTransform transform)
|
||||
{
|
||||
chainedTransform = transform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the transformation
|
||||
*/
|
||||
public final string execute(string text, Context context)
|
||||
{
|
||||
/* Perform the transformation */
|
||||
string transformedText = transform(text, context);
|
||||
|
||||
/* If there is a chained transformation */
|
||||
if(chainedTransform)
|
||||
{
|
||||
transformedText = chainedTransform.execute(transformedText, context);
|
||||
}
|
||||
|
||||
return transformedText;
|
||||
}
|
||||
}
|
|
@ -1,3 +1,8 @@
|
|||
/**
|
||||
* Helper functions
|
||||
*
|
||||
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
||||
*/
|
||||
module dlog.utilities;
|
||||
|
||||
import std.conv : to;
|
||||
|
|
Loading…
Reference in New Issue