A simple, functional, opinionated logger for python
I generally only need to implement simple logging in my services. For the most part, I don't need anything especially fancy, but I do need to stick to a consistent format, to help log analyzers do their job.
Currently this requires python 3 to run (developed against 3.6, but should compile under most recent versions)
There are 3 main steps:
- Generate a loging function:
log = logger.make_logger(print)
- Provide some input into logging function:
log("The program is exploding", level='error', why='Value Error')
- Observe the results:
2018-12-02T22:44:17.269137 level=error why="Value Error" msg="The program is exploding"
Note that only level and message are requird. Level, in addition, has a default value of info
. All other values
to be logged can be provided as keyword arguments (see above), which will be logged in key=value
format. The current
system time is always provided as well, to help order the events.
In addition, there are some configuration possible that will make themselves useful in certain situations.
Typically, all I need is a print that goes to standard out. You can accomplish this via this approach:
from logger import make_logger, refine_logger
from uuid import uuid4
# Generate a logger function
main_logger = make_logger()
main_logger("This is happening on the mainline")
# prints to standard out: 'dt level=info msg="This is happening on the mainline"'
ctx_logger = refine_logger(main_logger, context=uuid4())
ctx_logger("A configured logger")
# prints to standard out: 'dt level=info context="ff088e2c-b127-4d8a-ba1d-347de59d302e" msg="A configured logger"'
If you need something more robust, then there are some configuration opportunities.
If you want to ensure that consistent values are always provided with the logger, you can refine the logger via the
refine_logger
method. This will return a new logger that always have the supplied arugments when called. Note that
under the hood, this is just a call to functool's partial
log = make_logger()
rlog = refine_logger(log, confidence_level="100%")
rlog("Things are going well")
# Outputs: 2018-12-02T23:23:43.524875 level=info confidence_level=100% msg="Things are going well"
# These values is overwritable as well:
rlog("Things aren't looking so great", confidence_level="25%")
# Outputs: 2018-12-02T23:24:07.786726 level=info confidence_level=25% msg="Things aren't looking so great"
This project is meant to log to the console, but will accept any function that accepts a string as input.
# Completely untested, but in theory this would work
import os
def make_filewriter_function(output_path):
writer = open(output_path, 'a', 0) # Writing in unhuffered mode
def write_fn(s):
writer.write(s)
def close_fn():
os.close(writer)
return write_fn, close_fn
write_fn, close_fn = make_filewriter_function('/var/log/my-script-log.log')
log = logger.make_logger(write_fn)
# ...
close_fn()
If you don't like msg
and level
you can easily switch these when making the logger
If you want to add a label for the time, you can do that too
custom_log = logger.make_logger(print, message_label='message', level_label='logLevel', time_label='app_time')
custom_log("Look at that!")
# outputs: app_time=2018-12-02T23:06:59.566174 logLevel=info message="Look at that!"
By default, any value that does not have a space in it will be left unquoted, while any string that does have a space will be double-quoted
(i.e. "Expect values like this"
)
If you always make to make sure your values are quoted, you can do that by supplying force_quote=True
in the make_logger constructor.
If you have some class you want to log, you'll need to supply your own serializer. Once you have that, you can pass the serialize func
using the to_string_func
parameter.
This project uses pipenv to aid development. See https://pipenv.readthedocs.io/en/latest/ for usage.
This project utilizes pytest for running unit tests. They can be run via:
pipenv shell
cd simplog
python -m pytest
- Test & Optimize performance
- Suport custom character escapes?
- Add in support for mypy