Skip to main content

Basic console and file logging

Project description

slogpy

An opinionated and simple to use logger for Python scripts and tools

Using slogpy.slog in your code

Prep

If you are writing a tool, you should be using poetry!

poetry add slogpy

If you are adding slog to an existing script and need to get slogpy in the "old-fashioned way" you can:

pip3 install --user --upgrade slogpy

In your code

# maybe a bit weird looking, but it gets us the usage style we want
from slogpy.slog import Slog as slog

slog.initialize(module='widget') # optional, but highly recommended

slog.info('Log something at the info level')
slog.debug('log some debugging...this will only go to file unless you set the logging level')
slog.annoy('You need to add handling for this!')
slog.warn('hey, this is a warning')
slog.error('Something bad happened')
slog.fatal('Oh no, Mr. Bill! Something REALLY bad happened')

# if you want to tell the user where the log is (after any logging!)
print(f'Log written to: {slog.get_logging_path()}')

Using a progress bar with slog

If you try to just use rich.progress you will likely not get the desired result. This is due to a separate console being used. We have wrappers to solve this.

Here's how to use them:

import time

from slogpy.slog import Slog as slog
from slogpy.progress import (SlogProgress, get_progress,
    get_progress_counting, get_progress_counting_with_time)

# You can call SlogProgress() just like you would rich.progress.Progress
# or you can use one of the predefined factory methods. If you have a
# progress bar that you use often, add a factory for it in
# slogpy/slogpy/progress.py

def test_progress():
    my_iterable = ['apple', 'pear', 'orange', 'banana', 'kiwi', 'plum', 'durian', 'strawberry', 'grape', 'grapefruit']

    progress = SlogProgress()
    with progress:
        for fruit in progress.track(my_iterable, description='pick fruit'):
            slog.info(f'working on {fruit}')
            time.sleep(0.5)

    progress = get_progress_counting()
    with progress:
        for fruit in progress.track(my_iterable, description='pick fruit'):
            slog.info(f'working on {fruit}')
            time.sleep(0.5)

    progress = get_progress_counting_with_time()
    with progress:
        for tick in progress.track(range(0,10093), description='pick fruit'):
            if tick % 427 == 0:
                slog.info(f'{tick} mod 7 is 0')
            time.sleep(0.0003)


if __name__ == '__main__':
    test_progress()

Dumping local vars

There are two slog functions to help you emit your local variables:

slog.show_locals()

This is primarily meant for when you want to show some/all of your locals on the console (they will also be logged).

slog.log_locals()

This is primarily meant for when you want to log some/all of your locals, you can pass a value to level if you also want on the console (for instance, during development).

Example of both

from slogpy.slog import Slog as slog


def some_function(name, title, number=27):
    """Demonstrate show_locals and log_locals"""
    x = f'{name}, {title}'
    y = number * 2 - 12
    password = 'p@ssw0rd123'
    other_pass = '123'
    super_secret = 'codename'
    foo = dict(name=name, number=number)

    # demonstrate with pretty set and unset
    for pretty in [True, False]:
        slog.info(f'calling slog.show_locals() with {pretty=}')
        slog.show_locals([
            'name',
            'title',
            'title',
            'y',
            'foo',
            'password',
        ], obfuscate=['password'], pretty=pretty)

    # If you want all the locals, just don't include the list of names
    # ...you may still want to hide/obfuscate
    slog.info('calling slog.show_locals w/o specifying the names')
    slog.show_locals(obfuscate=['password', 'other_pass'], hide=['super_secret'])

    # Logging (usually won't go to screen as it defaults to log_level=slog.DEBUG)
    slog.info('calling slog.log_locals()')
    slog.log_locals(obfuscate=['password', 'other_pass'], hide=['super_secret'])

    # But sometimes we are developing and want to see w/o having to go to the log
    slog.info('calling slog.log_locals() and log_level=slog.INFO')
    slog.log_locals(obfuscate=['password', 'other_pass'], hide=['super_secret'], log_level=slog.INFO)
    slog.show_logging_path()


if __name__ == '__main__':
    some_function('Alice', 'Developer')

Where the heck are my logs?

Generally, they will be in the same directory from which you ran the python script (CWD). The log file will be named using YYYYmmDD_HHMMSS.log

If you called slog.initialize() with a module name (recommended), the filename will be <module>.YYYYmmDD_HHMMSS.log. You can also call slog.initialize(path=my_path) in which case my_path is used as the filename to log to. There are cases where this is better, but it should not be the norm.

If the log file already exists, slog will append to that file.

You can also set a root directory to log to with the environment variable SLOGPY_LOGPATH in which case the logs will go to <SLOGPY_LOGPATH>/<module>.YYYYmmDD_HHMMSS.log or <SLOGPY_LOGPATH>/YYYYmmDD_HHMMSS.log

Passing a tag to slog.initialize() will also affect the name of the generated log file. Passing a tag is handy when you have a tool that implements sub-commands and the like.

  • slog.initialize() -> 20240320_072842.log
  • slog.initialize(module='widget') -> widget.20240320_072842.log
  • slog.initialize(module='widget', tag='init') -> widget.20240320_072842.init.log
  • slog.initialize(tag='init') -> 20240320_072842.init.log

Using tags with slog and click

@click.group()
# other options and variables here
@click.option('-v', '--verbose', default=False, is_flag=True)
@click.pass_context
def widget_cmd_group(ctx, verbose):
    """does stuff"""
    ctx.ensure_object(dict)
    ctx.obj['log_level'] = slog.DEBUG if verbose else slog.INFO
    # other code goes here


@widget_cmd_group.command('<command>')
@click.pass_context
def widget_<command>(ctx):
    """widget <command>"""
    slog.initialize(module='widget', tag='<command>', log_level=ctx.obj['log_level'])
    # other code goes here

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slogpy-0.1.0.dev0.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

slogpy-0.1.0.dev0-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file slogpy-0.1.0.dev0.tar.gz.

File metadata

  • Download URL: slogpy-0.1.0.dev0.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.0.0 CPython/3.12.2

File hashes

Hashes for slogpy-0.1.0.dev0.tar.gz
Algorithm Hash digest
SHA256 33fd79347dc4c49b64f5f4a58b03e7d2934f35a38ec41c0cefd2b1090d267ed2
MD5 baa3452086210c99be8f7b8b4eef3560
BLAKE2b-256 07e9d63418fb8e4fd493c1b577f805e020d78bb4e124f8b339cedb0ffd0b7132

See more details on using hashes here.

File details

Details for the file slogpy-0.1.0.dev0-py3-none-any.whl.

File metadata

  • Download URL: slogpy-0.1.0.dev0-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.0.0 CPython/3.12.2

File hashes

Hashes for slogpy-0.1.0.dev0-py3-none-any.whl
Algorithm Hash digest
SHA256 89ace759d7fd86079206ad434679dc0dd334cad6791d25eff56044e96e3dc9bb
MD5 b866f303597d5890b634e7ccc73f90f9
BLAKE2b-256 d86cceb43e5d348b77486cb5ad0fd165e5d4b09fc1494ff29fd633d5909704bf

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page