clog/README.md
Ethan Smith-Coss 662ebe5e56
Update 'README.md'
Appended section for testing, and where to locate the testing documentation.

Signed-off-by: Ethan Smith-Coss <ethan.sc@closedless.xyz>
2022-05-16 17:35:58 +01:00

216 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

![clog logo](./assets/clog.svg)
# CLog ClosedLess Logger
Logging as simple as putting on a shoe.
## What is CLog?
Clog (stylised as 'CLog') is a basic Python logging module which builds on top
of the built-in `print` function to provide richer outputs for general
information and debugging, to a display console, or in-depth output written to
a dedicated file.
Logging should be simple, and effective. That's why Clog is designed to do what
it does best, as simply and effectively for developers, without adding an
abundance of features. Logging can be performed simply by invoking pseudolog
methods with a message to be written out to a file (and written to console
using the `.withConsole()` method), or as advanced as calling the `printLog`
method with additional parameters found in the built-in `print` function.
It can be difficult to identify what text is debug information, warnings or
errors, from the remaining output. With this, Clog implements terminal
highlighting depending on the level which logging information is written at.
Any level which is identified as `DEBUG` or higher, is also written to the
`STDERR` pipe.
"CLog" officially stands for, 'ClosedLess Logger'. But it may be colloqually
known as some of the following:
- Cohesive Logging.
- Console Logging, and more.
- 'Clog' (noun) - a shoe with a thick wooden sole.
## How Does it Work?
As described, Clog merely acts as a wrapper over the built-in `print` function
and does not use system-level standard PIPE write operations directly. With
this, Clog exposes all keyword optional parameters of the `print` function, so
output can be modified respectfully to the `print` function.
Clog is a Singleton+Factory pattern class. This means that once the class has
been constructed by a user, another instance invoked will simply return the
defined, currently existing instance. Using a Singleton pattern allows for the
integrity of a class' state. Something like the defined logging output file
cannot ever be changed after the instance is initially constructed. This in
turn means that every instance created for the logging system will never be
out-of-sync from any other instance created. It is a Factory pattern since the class' state can be modified and returned from an invoked method.
The core wrapper responsible for invoking the `print` function with specified
optional parameters, is a private helper method called `__printLog__` of the
`Logger` class. All other methods defined in the class, in some way, call this
helper wrapper method.
Given a `TextIOWrapper` object (or of similar with the `SupportsWrite` type),
a `print` function can write directly to that object when passed as the `file=`
parameter. This introduces the `printLog2File` method, which is similar to the
`printLog` method; however, `printLog2File` is appropriate for handling strings
as pathspecs, in addition to `TextIOWrapper` objects.
It may come to reason to allow for `printLog` to handle this ability as well,
which would make `printLog2File` redundant. Well, `printLog2File` has a lot of
dedicated functionality specifically for writing to a file, which would
otherwise make `printLog` bulky and confusing. For example, `printLog2File`
allows for timestamped messages, simple stacktraces to identify what method
invoked a log call, and to ensure that ANSI escape codes (used for terminal
highlighting) are not written to file, and a lot more.
The use of pseudolog methods can make logging very simple and short, but with
the ability to not only write to file, but also to the console, lazily. A
pseudolog (or, `Logger` pseudonyms) method is a type of `Logger` method which
implies a defined level of logging. There are three pseudologs: `debug`,
`warn`, and `error`. Each of these methods are decorated with the `classmethod`
decorator to allow for the defined Singleton instance to be modified and return
itself after being invoked. From this, the `.withConsole()` method may be
invoked directly after calling a pseudolog, or later if the returned instance
from the pseudolog is stored. This is why pseudologs are considered lazy since
the call to write to console can be performed later as the information is
stored by the class' state.
**But be careful!** Pseudologs invoked with `.withConsole()` will write the
last-most message written to the file, to the console, meaning if another
pseudolog is invoked, the class state is changed, and the previous message
stored will be lost.
## How to Use CLog
Clog is very simple to use in any project you wish to start making use of
logging systems within your code base. Simply import the package, or bring
the `Logger` class into scope directly.
```py
import clog
logger = Logger()
logger.printLog("Hello, Word!")
# NOTE: 'printLog' is a static method of 'Logger'
```
or
```py
import clog
# or: from clog import Logger
clog.Logger.printLog("Hello, World!")
# or Logger.printLog(...) if brought into scope
```
### Logging based on success of code
The following is an example whereby similar behaviour can be found using the
built-in `print(..., end="")` to prevent a newline character from being
printed. The following `printLog` call afterwards will print the message and a
newline character. This allows for acknowledgement of a process about to
perform a given task, with a success statement attached to the end of the line.
```py
import os
import sys
import clog
logger = Logger(out_f="my_log_file.txt") # custom file path
my_file = "some_important_file.txt"
logger.printLog("Checking if file exists...", end="")
logger.printLog2File(f"Ensuring that file is present: {my_file}")
if not os.path.exists(my_file):
logger.printLog2File("File could not be found in the current",
"working directory. Exiting (1)...")
logger.printLog("failed.")
sys.exit(1)
logger.printLog("ok.")
logger.printLog2File("File was successfully found.")
```
### Logging with pseudologs
Sometimes it's conventient to write one message, and have it logged both to a
file, and the terminal. By using a pseudolog method, logging is guaranteed to
be written to file, and optionally the same message can be sent to the
terminal with highlighting and identical formatting using the `.withConsole()`
method.
```py
import clog
# pseudologs require accessing an instance
logger = Logger()
logger.debug("Checking integrity of operations...").withConsole()
if 1 + 1 == 2:
logger.debug("Mathematics is not broken.").withConsole()
else:
logger.error("Mathematics is broken!!").withConsole()
```
## Why Use CLog and not Another Library
It's difficult to justify a dependence into your project, especially when you
can either perform that dependence yourself, or use a different method to solve
the problem. However, here are some reasons to consider using Clog yourself:
- CLog provides you with the basic logging tools out of the box.
- Additional features, such as control over logging elevation and colouring of
text.
- CLog uses built-in functions and no external dependences (ex. testing/dev).
- There is guarantee over where information is logged to, and correctly.
- It's simple and effective to use (well documented).
- Guarantees backwards compatibility to version 3.6 and sooner.
- CLog is completely open-source under the GNU GPLv3 or later. You can inspect
the code base at will, and completely integrate the code base into your
project (with respect to the defined licence agreement).
## Comparison Between `clog` and `logging`
You might be wondering which is better: using a library that makes your code
dependent on the maintanence and upkeep of an external library; or, using a
built-in library that comes with the installation. Well it depends on the
use-case and preferences, but here are some overviews between both libraries:
- `logging` is built-in, whereas `clog` is a dependency.
- `logging` provides the ability to have multiple logging instances. `clog` is
a Singleton pattern, which can be re-instantiated if desired.
- `logging` has basic and advanced features depending on how much you want to
get out of a logging utility. `clog` just works as its intended.
- Basic output from `logging` is simple, but ugly. `clog` provides a clear
indication of log elevation using terminal hightlighting, as well as conforming
to defined standards, such as ISO 8601 timestamps in log files out-of-the-box.
- `logging` requires configuration to get more advanced behaviour. `clog`
provides majority of this behaviour without configuration, and further
configuration can be performed when logging.
- `logging` provides "handlers" for separate stream and file outputs. `clog`
handles this under-the-hood, but allows for changing between stream and file
pipes where applicable. `clog` can also do both at the same time if desired.
- Tracebacks can be essential to debugging problems, and both libraries
provide a traceback. `logging` provides this functionality during an exception
being raised. `clog` provides a basic traceback with every messaged written to
a log file (unless disabled on write), stating where the call was made to log.
- `logging` provides handlers to write to more than just streams and files. It
can write to sockets, emails, OS event systems, and more. `clog` only handles
stream and file writes; however, if `print()` can write to it, so can `clog`.
These are just some key comparisons between both libraries, and whether or not
each point is advantageous for one library or the other depends on the
developer's use-case it does not mean one is objectively better than the
other. Which library is used depends on the use-case, but both could be used.
## Testing
If you wish to perform your own testing on the code base, create new unit
tests for new features to the project, or create more unit tests for existing
functionality, then consult the `TESTING.md` documentation file under `testing`
on how to go about this. Optionally, if you wish to understand how automated
testing is performed for Clog, you may also consult the same file for more
information.
## Licence
This project is licensed under the GNU GPLv3 or later licence agreement, which
hereby states that this software does not come with warranty or liability.
For more, please consult the `LICENCE.txt` file.
## Author
Ethan Smith-Coss (`ethan.sc@closedless.xyz`)
Copyright (c) 2022-23