M .hgignore +8 -1
@@ 1,3 1,10 @@
syntax: glob
.idea
-docs/build
No newline at end of file
+.pypirc
+docs/build
+build
+dist
+simple_log.egg-info
+.DS_Store
+*.pyc
+*.log
M README.md +28 -9
@@ 1,15 1,15 @@
Simple Logging
===============================
-* version number: 0.0.1
-* date: 2016.11.10
+* version number: 0.0.2
+* date: 2016.11.23
* author: Andrew J. Todd esq.
Overview
--------
A python module to allow you to log. It's a wrapper around the standard logging module with sensible defaults and
-as little configuration as possible.
+as little configuration as possible. Documentation is on [halfcooked.com](https://halfcooked.com/code/simple_log/)
Installation
------------
@@ 36,22 36,41 @@ We try and stay true to the name and mak
If you want to have multiple logs just pass a name to `get_log`
- >>> second_log = get_log('two')
+ >>> first_log = get_log("first")
+ >>> first_log.info("Information message to first log")
+ 2016.11.10 22:27:30 INFO:: Information message to first log
+ >>> second_log = get_log("two")
>>> second_log.debug("This is a debug message")
+ 2016.11.10 22:28:02 DEBUG:: This is a debug message
-By default the logging level is set to `'INFO'` (the standard module defaults to `'WARN'`). See the
+By default the logging level is set to `'INFO'` (the standard module defaults to `'WARNING'`). See the
[logging tutorial](https://docs.python.org/3/howto/logging.html#logging-basic-tutorial) for information on logging
-levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `set_level`
-function
+levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `setLevel`
+method on your log object
- >>> from simple_log import get_log, set_level
+ >>> from simple_log import get_log
>>> my_log = get_log('test_log')
>>> my_log.debug('This is the first debug message')
...
- >>> set_level('test_log', 'DEBUG')
+ >>> my_log.setLevel('DEBUG')
>>> my_log.debug('This is the second debug message')
2016.11.10 22:34:55 DEBUG:: This is the second debug message
+The log level values that you can pass to `setLevel` are `'DEBUG'`, `'INFO'` (the default), `'WARNING'`, `'ERROR'` or
+`'CRITICAL'`.
+
+If you would like your log messages written to a file as well as the screen use the `add_file` function
+
+ >>> from simple_log import get_log, add_file
+ >>> my_log = get_log('test_log')
+ >>> my_log.info('This is an information message')
+ 2016.11.12 22:48:10 INFO:: This is an information message
+ >>> add_file('test_log', 'test_log.log')
+ >>> my_log.info('This is another information message')
+ 2016.11.12 22:49:30 INFO:: This is another information message
+ >>> cat test_log.log
+ 2016.11.12 22:49:30 INFO:: This is another information message
+
Contributing
------------
M docs/source/conf.py +6 -2
@@ 31,8 31,12 @@ sys.path.insert(0, os.path.abspath('../.
# ones.
extensions = [
'sphinx.ext.autodoc',
+ 'sphinx.ext.intersphinx',
]
+# Directive to get the Python standard documents
+intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ 57,9 61,9 @@ author = 'Andy Todd'
# built documents.
#
# The short X.Y version.
-version = '0.0.1'
+version = '0.0.2'
# The full version, including alpha/beta/rc tags.
-release = '0.0.1'
+release = '0.0.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
A => docs/source/distributing.rst +19 -0
@@ 0,0 1,19 @@
+Distributing
+============
+
+These are the steps to package and upload simple_log to `pypi <https://pypi.python.org/pypi>`_
+
+1. Make changes and commit to mercurial
+
+2. Produce source and wheel distribution packages::
+
+ $ python setup.py sdist
+ $ python setup.py bdist_wheel
+
+3. Upload to pypi::
+
+ $ python setup.py upload -r pypi
+
+Or use twine::
+
+ $ twine upload -r pypi dist/*
No newline at end of file
M docs/source/index.rst +1 -0
@@ 9,6 9,7 @@ Contents:
:maxdepth: 2
simplelog
+ distributing
Indices and tables
M docs/source/simplelog.rst +40 -7
@@ 26,25 26,58 @@ We try and stay true to the name and mak
If you want to have multiple logs just pass a name to `get_log`
+ >>> first_log = get_log("first")
+ >>> first_log.info("Information message to first log")
+ 2016.11.10 22:27:30 INFO:: Information message to first log
>>> second_log = get_log('two')
>>> second_log.debug("This is a debug message")
+ 2016.11.10 22:28:02 DEBUG:: This is a debug message
-By default the logging level is set to `'INFO'` (the standard module defaults to `'WARN'`). See the
+By default the logging level is set to `'INFO'` (the standard module defaults to `'WARNING'`). See the
`logging tutorial <https://docs.python.org/3/howto/logging.html#logging-basic-tutorial>`_ for information on logging
-levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `set_level`
-function
+levels. If you would like to change the logging level, for instance to display 'DEBUG' messages use the `setLevel`
+method on your log object
- >>> from simple_log import get_log, set_level
+ >>> from simple_log import get_log
>>> my_log = get_log('test_log')
>>> my_log.debug('This is the first debug message')
...
- >>> set_level('test_log', 'DEBUG')
+ >>> my_log.setLevel('test_log', 'DEBUG')
>>> my_log.debug('This is the second debug message')
2016.11.10 22:34:55 DEBUG:: This is the second debug message
+If you would like your log messages written to a file as well as the screen use the `add_file` function
+
+ >>> from simple_log import get_log, add_file
+ >>> my_log = get_log('test_log')
+ >>> my_log.info('This is an information message')
+ 2016.11.12 22:48:10 INFO:: This is an information message
+ >>> add_file('test_log', 'test_log.log')
+ >>> my_log.info('This is another information message')
+ 2016.11.12 22:49:30 INFO:: This is another information message
+ >>> cat test_log.log
+ 2016.11.12 22:49:30 INFO:: This is another information message
+
+the `add_file` function tries to be clever and should work if you pass it either the name of a log that was created
+using `get_log` or if you pass in the log object itself.
+
+ >>> from simple_log import get_log, add_file
+ >>> my_log = get_log('test_log')
+ >>> add_file(my_log, 'test_log.log')
+ >>> my_log.info('Information message to file')
+ 2016.11.23 19:54:10 INFO:: Information message to file
+ >>> cat test_log.log
+ 2016.11.23 19:54:10 INFO:: Information message to file
+
Code Structure
--------------
-This package is made up of one Python module called `simple_log.py`. It has two functions `get_log` and `set_level`.
+The only dependency for `simple_log` is the Python standard library :py:mod:`logging` module
+
+This package is made up of one Python module called `log_utilities.py` within the `simple_log` module. It has two
+functions
-The only dependency is the Python standard library `logging module <https://docs.python.org/3/library/logging.html>`_
+.. autofunction:: simple_log.get_log
+
+.. autofunction:: simple_log.add_file
+
M requirements.txt +5 -5
@@ 1,6 1,6 @@
# Dev/Deployment
-sphinx
-sphinx_rtd_theme
-pytest
-coverage
-pypi-publisher
No newline at end of file
+# sphinx
+# sphinx_rtd_theme
+# pytest
+# coverage
+# pypi-publisher
No newline at end of file
M setup.py +4 -4
@@ 2,7 2,7 @@ from setuptools import setup, find_packa
from codecs import open
from os import path
-__version__ = '0.0.1'
+__version__ = '0.0.2'
here = path.abspath(path.dirname(__file__))
@@ 22,9 22,9 @@ setup(
version=__version__,
description='A python module with sensible defaults for logging',
long_description=long_description,
- url='https://github.com/andy47/simple_log',
- download_url='https://github.com/andy47/simple_log/tarball/' + __version__,
- license='BSD',
+ url='https://bitbucket.org/andy47/simplelog',
+ download_url='https://bitbucket.org/andy47/simplelog/downloads/',
+ license='MIT',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
M simple_log/__init__.py +1 -1
@@ 1,1 1,1 @@
-from simple_log.log_utilities import get_log, set_level
No newline at end of file
+from simple_log.log_utilities import get_log, add_file
No newline at end of file
M simple_log/log_utilities.py +20 -10
@@ 5,8 5,8 @@ Written in and tested against Python 3 o
Licensed under the MIT license - https://opensource.org/licenses/MIT
"""
-__version__ = (0, 1, 0)
-__date__ = (2016, 9, 21)
+__version__ = (0, 0, 2)
+__date__ = (2016, 11, 11)
__author__ = "Andrew J Todd esq. <andy47@halfcooked.com>"
import logging
@@ 18,6 18,14 @@ logs = {}
def get_log(logname=None):
+ """Return a log object called `<logname>`
+
+ If you don't specify a `<logname>` your returned object will have the name `default`
+
+ Log objects returned by this function have a logging level of `'INFO'` and a
+ sensible message output format including the current date and time and then the
+ log level followed by the log message.
+ """
if not logname:
log_name = 'default'
else:
@@ 35,13 43,15 @@ def get_log(logname=None):
return logger
-def set_level(log_name, level):
- """Set the level on <log_name> to <level>
+def add_file(log_name, file_name):
+ """Add a file handler to log messages directed to `<log_name>` to `<file_name>`
- See the standard documentation for the valid list of levels. They are all implemented as module attributes so
- we just getattr in the call to setLevel
+ We can accept either a log object or the name of a log object in the `<log_name>` parameter
"""
- if log_name in logs:
- logs[log_name].setLevel(getattr(logging, level))
- else:
- raise AttributeError('{} is not a valid log'.format(log_name))
+ file_handler = logging.FileHandler(file_name, "a")
+ file_formatter = logging.Formatter(MESSAGE_FORMAT, DATE_FORMAT)
+ file_handler.setFormatter(file_formatter)
+ if type(log_name) is logging.Logger:
+ log_name.addHandler(file_handler)
+ elif log_name in logs:
+ logs[log_name].addHandler(file_handler)
M tests/test_simple_log.py +33 -4
@@ 6,8 6,10 @@ These unit tests are designed to be run
__author__ = "Andrew J Todd esq. <andy47@halfcooked.com>"
import logging
-import py.test
-from simple_log import get_log, set_level
+import os
+import tempfile
+
+from simple_log import get_log, add_file
def test_get_log_returns_something():
@@ 35,9 37,36 @@ def test_repeat_returns_same_log():
assert first is second
-def skip_test_set_level():
+def test_file_output_with_log():
+ """If we specify a file name log messages should be written to it"""
+ tdir = tempfile.mkdtemp()
+ log_name = 'TEST_LOG'
+ log_file_name = os.path.join(tdir, log_name + '.log')
+ # log_file_name = log_name + '.log'
+ test_log = get_log()
+ add_file(test_log, log_file_name)
+ test_log.info("Information test message")
+ with open(log_file_name, 'r') as f:
+ contents = f.readlines()
+ assert "Information test message" in ''.join(contents)
+
+
+def test_file_output_with_log_name():
+ tdir = tempfile.mkdtemp()
+ log_name = 'TEST_LOG'
+ log_file_name = os.path.join(tdir, log_name + '.log')
+ # log_file_name = log_name + '.log'
+ test_log = get_log(log_name)
+ add_file(log_name, log_file_name)
+ test_log.info("Information test message")
+ with open(log_file_name, 'r') as f:
+ contents = f.readlines()
+ assert "Information test message" in ''.join(contents)
+
+
+def test_set_level():
log_name = 'TEST_LOG'
test_log = get_log(log_name)
- set_level(log_name, 'DEBUG')
+ test_log.setLevel('DEBUG')
assert test_log.level == logging.DEBUG