Codebase list logbook / upstream/0.6.0
Imported Upstream version 0.6.0 Agustin Henze 9 years ago
87 changed file(s) with 10558 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 .ropeproject
1 .tox
2 docs/_build
3 logbook/_speedups.c
4 logbook/_speedups.so
5 Logbook.egg-info
6 dist
7 *.pyc
8 env
9 env*
10 .coverage
11 cover
12 build
13 .vagrant
14 flycheck-*
0 \.pyc$
1 \.egg-info$
2 docs/_build
3 \.ropeproject
0 language: python
1 python:
2 - "2.6"
3 - "2.7"
4 - "3.3"
5 - "pypy"
6
7 install:
8 # this fixes SemLock issues on travis
9 - "sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm"
10 - "sudo apt-get install libzmq3-dev redis-server"
11 - "python scripts/pypi_mirror_setup.py http://a.pypi.python.org/simple"
12 - "pip install cython redis"
13 - "make test_setup"
14 - "python setup.py develop"
15
16 env:
17 - COMMAND="make test"
18 - COMMAND="make cybuild test"
19
20 script: "$COMMAND"
21
22 matrix:
23 exclude:
24 - python: "pypy"
25 env: COMMAND="make cybuild test"
26
27 notifications:
28 email:
29 recipients:
30 - vmalloc@gmail.com
31 irc:
32 channels:
33 - "chat.freenode.net#pocoo"
34 on_success: change
35 on_failure: always
36 use_notice: true
37 skip_join: true
0 Logbook is written and maintained by the Logbook Team and various
1 contributors:
2
3 Lead Developers:
4
5 - Armin Ronacher <armin.ronacher@active-4.com>
6 - Georg Brandl
7
8 Contributors:
9
10 - Ronny Pfannschmidt
11 - Daniel Neuhäuser
12 - Kenneth Reitz
13 - Valentine Svensson
14 - Roman Valls Guimera
15 - Guillermo Carrasco Hernández
16 - Raphaël Vinot
0 Logbook Changelog
1 =================
2
3 Here you can see the full list of changes between each Logbook release.
4
5 Version 0.6.0
6 -------------
7
8 Released on October 3rd 2013. Codename "why_not_production_ready"
9
10 - Added Redis handler (Thanks a lot @guillermo-carrasco for this PR)
11 - Fixed email encoding bug (Thanks Raphaël Vinot)
12
13 Version 0.5.0
14 -------------
15
16 Released on August 10th 2013.
17
18 - Drop 2.5, 3.2 support, code cleanup
19 - The exc_info argument now accepts `True`, like in the standard logging module
20
21 Version 0.4.2
22 -------------
23
24 Released on June 2nd 2013.
25
26 - Fixed Python 3.x compatibility, including speedups
27 - Dropped Python 2.4 support. Python 2.4 support caused a lot of hacks in the code and introduced duplication to the test code. In addition, it is impossible to cover 2.4-3.x with a single tox installation, which may introduce unwitting code breakage. Travis also does not support Python 2.4 so the chances of accidentally breaking this support were very high as it was...
28
29
30 Version 0.4.1
31 -------------
32
33 Released on December 12th. Codename "121212"
34
35 - Fixed several outstanding encoding problems, thanks to @dvarazzo.
36 - Merged in minor pull requests (see https://github.com/mitsuhiko/logbook/pulls?&state=closed)
37
38 Version 0.4
39 -----------
40
41 Released on October 24th. Codename "Phoenix"
42
43 - Added preliminary RabbitMQ and CouchDB support.
44 - Added :class:`logbook.notifiers.NotifoHandler`
45 - `channel` is now documented to be used for filtering purposes if
46 wanted. Previously this was an opaque string that was not intended
47 for filtering of any kind.
48
49 Version 0.3
50 -----------
51
52 Released on October 23rd. Codename "Informant"
53
54 - Added :class:`logbook.more.ColorizingStreamHandlerMixin` and
55 :class:`logbook.more.ColorizedStderrHandler`
56 - Deprecated :class:`logbook.RotatingFileHandlerBase` because the
57 interface was not flexible enough.
58 - Provided basic Python 3 compatibility. This did cause a few smaller
59 API changes that caused minimal changes on Python 2 as well. The
60 deprecation of the :class:`logbook.RotatingFileHandlerBase` was a
61 result of this.
62 - Added support for Python 2.4
63 - Added batch emitting support for handlers which now makes it possible
64 to use the :class:`logbook.more.FingersCrossedHandler` with the
65 :class:`logbook.MailHandler`.
66 - Moved the :class:`~logbook.FingersCrossedHandler` handler into the
67 base package. The old location stays importable for a few releases.
68 - Added :class:`logbook.GroupHandler` that buffers records until the
69 handler is popped.
70 - Added :class:`logbook.more.ExternalApplicationHandler` that executes
71 an external application for each log record emitted.
72
73 Version 0.2.1
74 -------------
75
76 Bugfix release, Released on September 22nd.
77
78 - Fixes Python 2.5 compatibility.
79
80 Version 0.2
81 -----------
82
83 Released on September 21st. Codename "Walls of Text"
84
85 - Implemented default with statement for handlers which is an
86 alias for `threadbound`.
87 - `applicationbound` and `threadbound` return the handler now.
88 - Implemented channel recording on the log records.
89 - The :class:`logbook.more.FingersCrossedHandler` now is set to
90 `ERROR` by default and has the ability to create new loggers
91 from a factory function.
92 - Implemented maximum buffer size for the
93 :class:`logbook.more.FingersCrossedHandler` as well as a lock
94 for thread safety.
95 - Added ability to filter for context.
96 - Moved bubbling flags and filters to the handler object.
97 - Moved context processors on their own stack.
98 - Removed the `iter_context_handlers` function.
99 - Renamed `NestedHandlerSetup` to :class:`~logbook.NestedSetup`
100 because it can now also configure processors.
101 - Added the :class:`logbook.Processor` class.
102 - There is no difference between logger attached handlers and
103 context specific handlers any more.
104 - Added a function to redirect warnings to logbook
105 (:func:`logbook.compat.redirected_warnings`).
106 - Fixed and improved :class:`logbook.LoggerGroup`.
107 - The :class:`logbook.TestHandler` now keeps the record open
108 for further inspection.
109 - The traceback is now removed from a log record when the record
110 is closed. The formatted traceback is a cached property
111 instead of a function.
112 - Added ticketing handlers that send logs directly into a database.
113 - Added MongoDB backend for ticketing handlers
114 - Added a :func:`logbook.base.dispatch_record` function to dispatch
115 records to handlers independently of a logger (uses the default
116 record dispatching logic).
117 - Renamed `logger_name` to `channel`.
118 - Added a multi processing log handler
119 (:class:`logbook.more.MultiProcessingHandler`).
120 - Added a twitter handler.
121 - Added a ZeroMQ handler.
122 - Added a Growl handler.
123 - Added a Libnotify handler.
124 - Added a monitoring file handler.
125 - Added a handler wrapper that moves the actual handling into a
126 background thread.
127 - The mail handler can now be configured to deliver each log record
128 not more than n times in m seconds.
129 - Added support for Python 2.5
130 - Added a :class:`logbook.queues.SubscriberGroup` to deal with multiple
131 subscribers.
132 - Added a :class:`logbook.compat.LoggingHandler` for redirecting logbook
133 log calls to the standard library's :mod:`logging` module.
134
135 Version 0.1
136 -----------
137
138 First public release.
0 Copyright (c) 2010 by the Logbook Team, see AUTHORS for more details.
1
2 Some rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10
11 * Redistributions in binary form must reproduce the above
12 copyright notice, this list of conditions and the following
13 disclaimer in the documentation and/or other materials provided
14 with the distribution.
15
16 * The names of the contributors may not be used to endorse or
17 promote products derived from this software without specific
18 prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 include MANIFEST.in Makefile CHANGES logbook/_speedups.c logbook/_speedups.pyx tox.ini
1 include scripts/test_setup.py
2 recursive-include tests *
3
0 all: clean-pyc test
1
2 clean-pyc:
3 find . -name '*.pyc' -exec rm -f {} +
4 find . -name '*.pyo' -exec rm -f {} +
5 find . -name '*~' -exec rm -f {} +
6
7 test_setup:
8 @python scripts/test_setup.py
9
10 test:
11 @nosetests -w tests
12
13 toxtest:
14 @tox
15
16 vagrant_toxtest:
17 @vagrant up
18 @vagrant ssh --command "rsync -avP --delete --exclude=_build --exclude=.tox /vagrant/ ~/src/ && cd ~/src/ && tox"
19
20 bench:
21 @python benchmark/run.py
22
23 upload-docs: docs
24 python setup.py upload_docs
25
26 docs:
27 make -C docs html SPHINXOPTS=-Aonline=1
28
29 release: upload-docs
30 python scripts/make-release.py
31
32 logbook/_speedups.so: logbook/_speedups.pyx
33 cython logbook/_speedups.pyx
34 python setup.py build
35 cp build/*/logbook/_speedups*.so logbook
36
37 cybuild: logbook/_speedups.so
38
39 .PHONY: test upload-docs clean-pyc cybuild bench all docs
0 Welcome to Logbook
1 ==================
2
3 .. image:: https://secure.travis-ci.org/mitsuhiko/logbook.png
4 :target: https://travis-ci.org/mitsuhiko/logbook
5
6 .. image:: https://pypip.in/d/Logbook/badge.png
7 :target: https://crate.io/packages/Logbook
8
9 .. image:: https://pypip.in/v/Logbook/badge.png
10 :target: https://crate.io/packages/Logbook
11
12 Logbook is a nice logging replacement.
13
14 It should be easy to setup, use and configure and support web applications :)
15
16 For more information look at http://logbook.pocoo.org/
0 # -*- mode: ruby -*-
1 # vi: set ft=ruby :
2 PYTHON_VERSIONS = ["python2.6", "python2.7", "python3.3"]
3
4 Vagrant::Config.run do |config|
5 config.vm.define :box do |config|
6 config.vm.box = "precise64"
7 config.vm.box_url = "http://files.vagrantup.com/precise64.box"
8 config.vm.host_name = "box"
9 config.vm.provision :shell, :inline => "sudo apt-get -y update"
10 config.vm.provision :shell, :inline => "sudo apt-get install -y python-software-properties"
11 config.vm.provision :shell, :inline => "sudo add-apt-repository -y ppa:fkrull/deadsnakes"
12 config.vm.provision :shell, :inline => "sudo apt-get update"
13 PYTHON_VERSIONS.each { |python_version|
14 config.vm.provision :shell, :inline => "sudo apt-get install -y " + python_version + " " + python_version + "-dev"
15 }
16 config.vm.provision :shell, :inline => "sudo apt-get install -y libzmq-dev wget libbluetooth-dev libsqlite3-dev"
17 config.vm.provision :shell, :inline => "wget http://python-distribute.org/distribute_setup.py -O /tmp/distribute_setup.py"
18 PYTHON_VERSIONS.each { |python_executable|
19 config.vm.provision :shell, :inline => python_executable + " /tmp/distribute_setup.py"
20 }
21 config.vm.provision :shell, :inline => "sudo easy_install tox==1.2"
22 config.vm.provision :shell, :inline => "sudo easy_install virtualenv==1.6.4"
23 end
24 end
0 """Tests with frame introspection disabled"""
1 from logbook import Logger, NullHandler, Flags
2
3
4 log = Logger('Test logger')
5
6
7 class DummyHandler(NullHandler):
8 blackhole = False
9
10
11 def run():
12 with Flags(introspection=False):
13 with DummyHandler() as handler:
14 for x in xrange(500):
15 log.warning('this is not handled')
0 """Tests with the whole logger disabled"""
1 from logbook import Logger
2
3
4 log = Logger('Test logger')
5 log.disabled = True
6
7
8 def run():
9 for x in xrange(500):
10 log.warning('this is not handled')
0 """Tests with stack frame introspection enabled"""
1 from logbook import Logger, NullHandler, Flags
2
3
4 log = Logger('Test logger')
5
6
7 class DummyHandler(NullHandler):
8 blackhole = False
9
10
11 def run():
12 with Flags(introspection=True):
13 with DummyHandler() as handler:
14 for x in xrange(500):
15 log.warning('this is not handled')
0 """Benchmarks the file handler"""
1 from logbook import Logger, FileHandler
2 from tempfile import NamedTemporaryFile
3
4
5 log = Logger('Test logger')
6
7
8 def run():
9 f = NamedTemporaryFile()
10 with FileHandler(f.name) as handler:
11 for x in xrange(500):
12 log.warning('this is handled')
0 """Benchmarks the file handler with unicode"""
1 from logbook import Logger, FileHandler
2 from tempfile import NamedTemporaryFile
3
4
5 log = Logger('Test logger')
6
7
8 def run():
9 f = NamedTemporaryFile()
10 with FileHandler(f.name) as handler:
11 for x in xrange(500):
12 log.warning(u'this is handled \x6f')
0 """Test with no handler active"""
1 from logbook import Logger
2
3
4 def run():
5 for x in xrange(500):
6 Logger('Test')
0 """Benchmarks too low logger levels"""
1 from logbook import Logger, StreamHandler, ERROR
2 from cStringIO import StringIO
3
4
5 log = Logger('Test logger')
6 log.level = ERROR
7
8
9 def run():
10 out = StringIO()
11 with StreamHandler(out):
12 for x in xrange(500):
13 log.warning('this is not handled')
0 """Tests logging file handler in comparison"""
1 from logging import getLogger, FileHandler
2 from tempfile import NamedTemporaryFile
3
4
5 log = getLogger('Testlogger')
6
7
8 def run():
9 f = NamedTemporaryFile()
10 handler = FileHandler(f.name)
11 log.addHandler(handler)
12 for x in xrange(500):
13 log.warning('this is handled')
0 """Tests logging file handler in comparison"""
1 from logging import getLogger, FileHandler
2 from tempfile import NamedTemporaryFile
3
4
5 log = getLogger('Testlogger')
6
7
8 def run():
9 f = NamedTemporaryFile()
10 handler = FileHandler(f.name)
11 log.addHandler(handler)
12 for x in xrange(500):
13 log.warning(u'this is handled \x6f')
0 """Test with no handler active"""
1 from logging import getLogger
2
3
4 root_logger = getLogger()
5
6
7 def run():
8 for x in xrange(500):
9 getLogger('Test')
10 del root_logger.manager.loggerDict['Test']
0 """Tests with a logging handler becoming a noop for comparison"""
1 from logging import getLogger, StreamHandler, ERROR
2 from cStringIO import StringIO
3
4
5 log = getLogger('Testlogger')
6 log.setLevel(ERROR)
7
8
9 def run():
10 out = StringIO()
11 handler = StreamHandler(out)
12 log.addHandler(handler)
13 for x in xrange(500):
14 log.warning('this is not handled')
0 """Tests with a logging handler becoming a noop for comparison"""
1 from logging import getLogger, StreamHandler, ERROR
2 from cStringIO import StringIO
3
4
5 log = getLogger('Testlogger')
6
7
8 def run():
9 out = StringIO()
10 handler = StreamHandler(out)
11 handler.setLevel(ERROR)
12 log.addHandler(handler)
13 for x in xrange(500):
14 log.warning('this is not handled')
0 """Tests with a filter disabling a handler for comparsion in logging"""
1 from logging import getLogger, StreamHandler, Filter
2 from cStringIO import StringIO
3
4
5 log = getLogger('Testlogger')
6
7
8 class DisableFilter(Filter):
9 def filter(self, record):
10 return False
11
12
13 def run():
14 out = StringIO()
15 handler = StreamHandler(out)
16 handler.addFilter(DisableFilter())
17 log.addHandler(handler)
18 for x in xrange(500):
19 log.warning('this is not handled')
0 """Tests the stream handler in logging"""
1 from logging import Logger, StreamHandler
2 from cStringIO import StringIO
3
4
5 log = Logger('Test logger')
6
7
8 def run():
9 out = StringIO()
10 log.addHandler(StreamHandler(out))
11 for x in xrange(500):
12 log.warning('this is not handled')
13 assert out.getvalue().count('\n') == 500
0 """Test with no handler active"""
1 from logbook import Logger, StreamHandler, NullHandler, ERROR
2 from cStringIO import StringIO
3
4
5 log = Logger('Test logger')
6
7
8 def run():
9 out = StringIO()
10 with NullHandler():
11 with StreamHandler(out, level=ERROR) as handler:
12 for x in xrange(500):
13 log.warning('this is not handled')
14 assert not out.getvalue()
0 from logbook import Logger, StreamHandler, NullHandler
1 from cStringIO import StringIO
2
3
4 log = Logger('Test logger')
5
6
7 def run():
8 out = StringIO()
9 with NullHandler():
10 with StreamHandler(out, filter=lambda r, h: False) as handler:
11 for x in xrange(500):
12 log.warning('this is not handled')
13 assert not out.getvalue()
0 """Like the filter test, but with the should_handle implemented"""
1 from logbook import Logger, StreamHandler, NullHandler
2 from cStringIO import StringIO
3
4
5 log = Logger('Test logger')
6
7
8 class CustomStreamHandler(StreamHandler):
9 def should_handle(self, record):
10 return False
11
12
13 def run():
14 out = StringIO()
15 with NullHandler():
16 with CustomStreamHandler(out) as handler:
17 for x in xrange(500):
18 log.warning('this is not handled')
19 assert not out.getvalue()
0 """Tests redirects from logging to logbook"""
1 from logging import getLogger
2 from logbook import StreamHandler
3 from logbook.compat import redirect_logging
4 from cStringIO import StringIO
5
6
7 redirect_logging()
8 log = getLogger('Test logger')
9
10
11 def run():
12 out = StringIO()
13 with StreamHandler(out):
14 for x in xrange(500):
15 log.warning('this is not handled')
16 assert out.getvalue().count('\n') == 500
0 """Tests redirects from logging to logbook"""
1 from logging import getLogger, StreamHandler
2 from logbook.compat import LoggingHandler
3 from cStringIO import StringIO
4
5
6 log = getLogger('Test logger')
7
8
9 def run():
10 out = StringIO()
11 log.addHandler(StreamHandler(out))
12 with LoggingHandler():
13 for x in xrange(500):
14 log.warning('this is not handled')
15 assert out.getvalue().count('\n') == 500
0 """Tests basic stack manipulation performance"""
1 from logbook import Handler, NullHandler, StreamHandler, FileHandler, \
2 ERROR, WARNING
3 from tempfile import NamedTemporaryFile
4 from cStringIO import StringIO
5
6
7 def run():
8 f = NamedTemporaryFile()
9 out = StringIO()
10 with NullHandler():
11 with StreamHandler(out, level=WARNING):
12 with FileHandler(f.name, level=ERROR):
13 for x in xrange(100):
14 list(Handler.stack_manager.iter_context_objects())
0 """Tests the stream handler"""
1 from logbook import Logger, StreamHandler
2 from cStringIO import StringIO
3
4
5 log = Logger('Test logger')
6
7
8 def run():
9 out = StringIO()
10 with StreamHandler(out) as handler:
11 for x in xrange(500):
12 log.warning('this is not handled')
13 assert out.getvalue().count('\n') == 500
0 """Tests the test handler"""
1 from logbook import Logger, TestHandler
2
3
4 log = Logger('Test logger')
5
6
7 def run():
8 with TestHandler() as handler:
9 for x in xrange(500):
10 log.warning('this is not handled')
0 #!/usr/bin/env python
1 """
2 Runs the benchmarks
3 """
4 import sys
5 import os
6 import re
7 from subprocess import Popen
8
9 try:
10 from pkg_resources import get_distribution
11 version = get_distribution('Logbook').version
12 except Exception:
13 version = 'unknown version'
14
15
16 _filename_re = re.compile(r'^bench_(.*?)\.py$')
17 bench_directory = os.path.abspath(os.path.dirname(__file__))
18
19
20 def list_benchmarks():
21 result = []
22 for name in os.listdir(bench_directory):
23 match = _filename_re.match(name)
24 if match is not None:
25 result.append(match.group(1))
26 result.sort(key=lambda x: (x.startswith('logging_'), x.lower()))
27 return result
28
29
30 def run_bench(name):
31 sys.stdout.write('%-32s' % name)
32 sys.stdout.flush()
33 Popen([sys.executable, '-mtimeit', '-s',
34 'from bench_%s import run' % name,
35 'run()']).wait()
36
37
38 def main():
39 print '=' * 80
40 print 'Running benchmark with Logbook %s' % version
41 print '-' * 80
42 os.chdir(bench_directory)
43 for bench in list_benchmarks():
44 run_bench(bench)
45 print '-' * 80
46
47
48 if __name__ == '__main__':
49 main()
0 # Makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 PAPER =
7 BUILDDIR = _build
8
9 # Internal variables.
10 PAPEROPT_a4 = -D latex_paper_size=a4
11 PAPEROPT_letter = -D latex_paper_size=letter
12 ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
13
14 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
15
16 help:
17 @echo "Please use \`make <target>' where <target> is one of"
18 @echo " html to make standalone HTML files"
19 @echo " dirhtml to make HTML files named index.html in directories"
20 @echo " singlehtml to make a single large HTML file"
21 @echo " pickle to make pickle files"
22 @echo " json to make JSON files"
23 @echo " htmlhelp to make HTML files and a HTML help project"
24 @echo " qthelp to make HTML files and a qthelp project"
25 @echo " devhelp to make HTML files and a Devhelp project"
26 @echo " epub to make an epub"
27 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
28 @echo " latexpdf to make LaTeX files and run them through pdflatex"
29 @echo " text to make text files"
30 @echo " man to make manual pages"
31 @echo " changes to make an overview of all changed/added/deprecated items"
32 @echo " linkcheck to check all external links for integrity"
33 @echo " doctest to run all doctests embedded in the documentation (if enabled)"
34
35 clean:
36 -rm -rf $(BUILDDIR)/*
37
38 html:
39 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
40 @echo
41 @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
42
43 dirhtml:
44 $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
45 @echo
46 @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
47
48 singlehtml:
49 $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
50 @echo
51 @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
52
53 pickle:
54 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
55 @echo
56 @echo "Build finished; now you can process the pickle files."
57
58 json:
59 $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
60 @echo
61 @echo "Build finished; now you can process the JSON files."
62
63 htmlhelp:
64 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
65 @echo
66 @echo "Build finished; now you can run HTML Help Workshop with the" \
67 ".hhp project file in $(BUILDDIR)/htmlhelp."
68
69 qthelp:
70 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
71 @echo
72 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
73 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
74 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Logbook.qhcp"
75 @echo "To view the help file:"
76 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Logbook.qhc"
77
78 devhelp:
79 $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
80 @echo
81 @echo "Build finished."
82 @echo "To view the help file:"
83 @echo "# mkdir -p $$HOME/.local/share/devhelp/Logbook"
84 @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Logbook"
85 @echo "# devhelp"
86
87 epub:
88 $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
89 @echo
90 @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
91
92 latex:
93 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
94 @echo
95 @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
96 @echo "Run \`make' in that directory to run these through (pdf)latex" \
97 "(use \`make latexpdf' here to do that automatically)."
98
99 latexpdf:
100 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
101 @echo "Running LaTeX files through pdflatex..."
102 make -C $(BUILDDIR)/latex all-pdf
103 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
104
105 text:
106 $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
107 @echo
108 @echo "Build finished. The text files are in $(BUILDDIR)/text."
109
110 man:
111 $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
112 @echo
113 @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
114
115 changes:
116 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
117 @echo
118 @echo "The overview file is in $(BUILDDIR)/changes."
119
120 linkcheck:
121 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
122 @echo
123 @echo "Link check complete; look for any errors in the above output " \
124 "or in $(BUILDDIR)/linkcheck/output.txt."
125
126 doctest:
127 $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
128 @echo "Testing of doctests in the sources finished, look at the " \
129 "results in $(BUILDDIR)/doctest/output.txt."
0 Core Interface
1 ==============
2
3 This implements the core interface.
4
5 .. module:: logbook
6
7 .. autoclass:: Logger
8 :members:
9 :inherited-members:
10
11 .. autoclass:: LoggerGroup
12 :members:
13
14 .. autoclass:: LogRecord
15 :members:
16
17 .. autoclass:: Flags
18 :members:
19 :inherited-members:
20
21 .. autoclass:: Processor
22 :members:
23 :inherited-members:
24
25 .. autofunction:: get_level_name
26
27 .. autofunction:: lookup_level
28
29 .. data:: CRITICAL
30 ERROR
31 WARNING
32 INFO
33 DEBUG
34 NOTSET
35
36 The log level constants
0 Compatibility
1 =============
2
3 This documents compatibility support with existing systems such as
4 :mod:`logging` and :mod:`warnings`.
5
6 .. module:: logbook.compat
7
8 Logging Compatibility
9 ---------------------
10
11 .. autofunction:: redirect_logging
12
13 .. autofunction:: redirected_logging
14
15 .. autoclass:: RedirectLoggingHandler
16 :members:
17
18 .. autoclass:: LoggingHandler
19 :members:
20
21
22 Warnings Compatibility
23 ----------------------
24
25 .. autofunction:: redirect_warnings
26
27 .. autofunction:: redirected_warnings
0 Handlers
1 ========
2
3 This documents the base handler interface as well as the provided core
4 handlers. There are additional handlers for special purposes in the
5 :mod:`logbook.more`, :mod:`logbook.ticketing` and :mod:`logbook.queues`
6 modules.
7
8 .. module:: logbook
9
10 Base Interface
11 --------------
12
13 .. autoclass:: Handler
14 :members:
15 :inherited-members:
16
17 .. autoclass:: NestedSetup
18 :members:
19
20 .. autoclass:: StringFormatter
21 :members:
22
23 Core Handlers
24 -------------
25
26 .. autoclass:: StreamHandler
27 :members:
28
29 .. autoclass:: FileHandler
30 :members:
31
32 .. autoclass:: MonitoringFileHandler
33 :members:
34
35 .. autoclass:: StderrHandler
36 :members:
37
38 .. autoclass:: RotatingFileHandler
39 :members:
40
41 .. autoclass:: TimedRotatingFileHandler
42 :members:
43
44 .. autoclass:: TestHandler
45 :members:
46
47 .. autoclass:: MailHandler
48 :members:
49
50 .. autoclass:: GMailHandler
51 :members:
52
53 .. autoclass:: SyslogHandler
54 :members:
55
56 .. autoclass:: NTEventLogHandler
57 :members:
58
59 .. autoclass:: NullHandler
60 :members:
61
62 .. autoclass:: WrapperHandler
63 :members:
64
65 .. autofunction:: create_syshandler
66
67 Special Handlers
68 ----------------
69
70 .. autoclass:: FingersCrossedHandler
71 :members:
72
73 .. autoclass:: GroupHandler
74 :members:
75
76 Mixin Classes
77 -------------
78
79 .. autoclass:: StringFormatterHandlerMixin
80 :members:
81
82 .. autoclass:: HashingHandlerMixin
83 :members:
84
85 .. autoclass:: LimitingHandlerMixin
86 :members:
0 API Documentation
1 =================
2
3 This part of the documentation documents all the classes and functions
4 provided by Logbook.
5
6 .. toctree::
7
8 base
9 handlers
10 utilities
11 queues
12 ticketing
13 more
14 notifiers
15 compat
16 internal
0 Internal API
1 ============
2
3 This documents the internal API that might be useful for more advanced
4 setups or custom handlers.
5
6 .. module:: logbook.base
7
8 .. autofunction:: dispatch_record
9
10 .. autoclass:: StackedObject
11 :members:
12
13 .. autoclass:: RecordDispatcher
14 :members:
15
16 .. autoclass:: LoggerMixin
17 :members:
18 :inherited-members:
19
20 .. module:: logbook.handlers
21
22 .. autoclass:: RotatingFileHandlerBase
23 :members:
24
25 .. autoclass:: StringFormatterHandlerMixin
26 :members:
0 The More Module
1 ===============
2
3 The more module implements special handlers and other things that are
4 beyond the scope of Logbook itself or depend on external libraries.
5 Additionally there are some handlers in :mod:`logbook.ticketing`,
6 :mod:`logbook.queues` and :mod:`logbook.notifiers`.
7
8 .. module:: logbook.more
9
10 Tagged Logging
11 --------------
12
13 .. autoclass:: TaggingLogger
14 :members:
15 :inherited-members:
16
17 .. autoclass:: TaggingHandler
18 :members:
19
20 Special Handlers
21 ----------------
22
23 .. autoclass:: TwitterHandler
24 :members:
25
26 .. autoclass:: ExternalApplicationHandler
27 :members:
28
29 .. autoclass:: ExceptionHandler
30 :members:
31
32 Colorized Handlers
33 ------------------
34
35 .. versionadded:: 0.3
36
37 .. autoclass:: ColorizedStderrHandler
38
39 .. autoclass:: ColorizingStreamHandlerMixin
40 :members:
41
42 Other
43 -----
44
45 .. autoclass:: JinjaFormatter
46 :members:
0 .. _notifiers:
1
2 The Notifiers Module
3 ====================
4
5 The notifiers module implements special handlers for various platforms
6 that depend on external libraries.
7 The more module implements special handlers and other things that are
8 beyond the scope of Logbook itself or depend on external libraries.
9
10 .. module:: logbook.notifiers
11
12 .. autofunction:: create_notification_handler
13
14 OSX Specific Handlers
15 ---------------------
16
17 .. autoclass:: GrowlHandler
18 :members:
19
20 Linux Specific Handlers
21 -----------------------
22
23 .. autoclass:: LibNotifyHandler
24 :members:
25
26 Other Services
27 --------------
28
29 .. autoclass:: BoxcarHandler
30 :members:
31
32 .. autoclass:: NotifoHandler
33 :members:
34
35 Base Interface
36 --------------
37
38 .. autoclass:: NotificationBaseHandler
39 :members:
0 Queue Support
1 =============
2
3 The queue support module makes it possible to add log records to a queue
4 system. This is useful for distributed setups where you want multiple
5 processes to log to the same backend. Currently supported are ZeroMQ as
6 well as the :mod:`multiprocessing` :class:`~multiprocessing.Queue` class.
7
8 .. module:: logbook.queues
9
10 ZeroMQ
11 ------
12
13 .. autoclass:: ZeroMQHandler
14 :members:
15
16 .. autoclass:: ZeroMQSubscriber
17 :members:
18 :inherited-members:
19
20 Redis
21 -----
22
23 .. autoclass:: RedisHandler
24 :members:
25
26 MultiProcessing
27 ---------------
28
29 .. autoclass:: MultiProcessingHandler
30 :members:
31
32 .. autoclass:: MultiProcessingSubscriber
33 :members:
34 :inherited-members:
35
36 Other
37 -----
38
39 .. autoclass:: ThreadedWrapperHandler
40 :members:
41
42 .. autoclass:: SubscriberGroup
43 :members:
44
45 Base Interface
46 --------------
47
48 .. autoclass:: SubscriberBase
49 :members:
50
51 .. autoclass:: ThreadController
52 :members:
53
54 .. autoclass:: TWHThreadController
55 :members:
0 Ticketing Support
1 =================
2
3 This documents the support classes for ticketing. With ticketing handlers
4 log records are categorized by location and for every emitted log record a
5 count is added. That way you know how often certain messages are
6 triggered, at what times and when the last occurrence was.
7
8 .. module:: logbook.ticketing
9
10 .. autoclass:: TicketingBaseHandler
11 :members:
12
13 .. autoclass:: TicketingHandler
14 :members:
15
16 .. autoclass:: BackendBase
17 :members:
18
19 .. autoclass:: SQLAlchemyBackend
20
21 .. autoclass:: MongoDBBackend
0 Utilities
1 =========
2
3 This documents general purpose utility functions available in Logbook.
4
5 .. module:: logbook
6
7 .. autofunction:: debug
8
9 .. autofunction:: info
10
11 .. autofunction:: warn
12
13 .. autofunction:: warning
14
15 .. autofunction:: notice
16
17 .. autofunction:: error
18
19 .. autofunction:: exception
20
21 .. autofunction:: catch_exceptions
22
23 .. autofunction:: critical
24
25 .. autofunction:: log
26
27 .. autofunction:: set_datetime_format
0 .. include:: ../CHANGES
0 .. _logging-compat:
1
2 Logging Compatibility
3 =====================
4
5 Logbook provides backwards compatibility with the logging library. When
6 activated, the logging library will transparently redirect all the logging calls
7 to your Logbook logging setup.
8
9 Basic Setup
10 -----------
11
12 If you import the compat system and call the
13 :func:`~logbook.compat.redirect_logging` function, all logging calls that happen
14 after this call will transparently be redirected to Logbook::
15
16 from logbook.compat import redirect_logging
17 redirect_logging()
18
19 This also means you don't have to call :func:`logging.basicConfig`:
20
21 >>> from logbook.compat import redirect_logging
22 >>> redirect_logging()
23 >>> from logging import getLogger
24 >>> log = getLogger('My Logger')
25 >>> log.warn('This is a warning')
26 [2010-07-25 00:24] WARNING: My Logger: This is a warning
27
28 Advanced Setup
29 --------------
30
31 The way this is implemented is with a
32 :class:`~logbook.compat.RedirectLoggingHandler`. This class is a handler for
33 the old logging system that sends records via an internal logbook logger to the
34 active logbook handlers. This handler can then be added to specific logging
35 loggers if you want:
36
37 >>> from logging import getLogger
38 >>> mylog = getLogger('My Log')
39 >>> from logbook.compat import RedirectLoggingHandler
40 >>> mylog.addHandler(RedirectLoggingHandler())
41 >>> otherlog = getLogger('Other Log')
42 >>> otherlog.warn('logging is deprecated')
43 No handlers could be found for logger "Other Log"
44 >>> mylog.warn('but logbook is awesome')
45 [2010-07-25 00:29] WARNING: My Log: but logbook is awesome
46
47 Reverse Redirects
48 -----------------
49
50 You can also redirect logbook records to logging, so the other way round.
51 For this you just have to activate the
52 :class:`~logbook.compat.LoggingHandler` for the thread or application::
53
54 from logbook import Logger
55 from logbook.compat import LoggingHandler
56
57 log = Logger('My app')
58 with LoggingHandler():
59 log.warn('Going to logging')
0 # -*- coding: utf-8 -*-
1 #
2 # Logbook documentation build configuration file, created by
3 # sphinx-quickstart on Fri Jul 23 16:54:49 2010.
4 #
5 # This file is execfile()d with the current directory set to its containing dir.
6 #
7 # Note that not all possible configuration values are present in this
8 # autogenerated file.
9 #
10 # All configuration values have a default; values that are commented out
11 # serve to show the default.
12
13 import sys, os
14
15 # If extensions (or modules to document with autodoc) are in another directory,
16 # add these directories to sys.path here. If the directory is relative to the
17 # documentation root, use os.path.abspath to make it absolute, like shown here.
18 sys.path.extend((os.path.abspath('.'), os.path.abspath('..')))
19
20 # -- General configuration -----------------------------------------------------
21
22 # If your documentation needs a minimal Sphinx version, state it here.
23 #needs_sphinx = '1.0'
24
25 # Add any Sphinx extension module names here, as strings. They can be extensions
26 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
27 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx']
28
29 # Add any paths that contain templates here, relative to this directory.
30 templates_path = ['_templates']
31
32 # The suffix of source filenames.
33 source_suffix = '.rst'
34
35 # The encoding of source files.
36 #source_encoding = 'utf-8-sig'
37
38 # The master toctree document.
39 master_doc = 'index'
40
41 # General information about the project.
42 project = u'Logbook'
43 copyright = u'2010, Armin Ronacher, Georg Brandl'
44
45 # The version info for the project you're documenting, acts as replacement for
46 # |version| and |release|, also used in various other places throughout the
47 # built documents.
48 #
49 # The short X.Y version.
50 version = '0.6.1-dev'
51 # The full version, including alpha/beta/rc tags.
52 release = '0.6.1-dev'
53
54 # The language for content autogenerated by Sphinx. Refer to documentation
55 # for a list of supported languages.
56 #language = None
57
58 # There are two options for replacing |today|: either, you set today to some
59 # non-false value, then it is used:
60 #today = ''
61 # Else, today_fmt is used as the format for a strftime call.
62 #today_fmt = '%B %d, %Y'
63
64 # List of patterns, relative to source directory, that match files and
65 # directories to ignore when looking for source files.
66 exclude_patterns = ['_build']
67
68 # The reST default role (used for this markup: `text`) to use for all documents.
69 #default_role = None
70
71 # If true, '()' will be appended to :func: etc. cross-reference text.
72 #add_function_parentheses = True
73
74 # If true, the current module name will be prepended to all description
75 # unit titles (such as .. function::).
76 #add_module_names = True
77
78 # If true, sectionauthor and moduleauthor directives will be shown in the
79 # output. They are ignored by default.
80 #show_authors = False
81
82 # The name of the Pygments (syntax highlighting) style to use.
83 pygments_style = 'sphinx'
84
85 # A list of ignored prefixes for module index sorting.
86 #modindex_common_prefix = []
87
88
89 # -- Options for HTML output ---------------------------------------------------
90
91 # The theme to use for HTML and HTML Help pages. See the documentation for
92 # a list of builtin themes.
93 html_theme = 'sheet'
94
95 # Theme options are theme-specific and customize the look and feel of a theme
96 # further. For a list of options available for each theme, see the
97 # documentation.
98 html_theme_options = {
99 'nosidebar': True,
100 }
101
102 # Add any paths that contain custom themes here, relative to this directory.
103 html_theme_path = ['.']
104
105 # The name for this set of Sphinx documents. If None, it defaults to
106 # "<project> v<release> documentation".
107 html_title = "Logbook"
108
109 # A shorter title for the navigation bar. Default is the same as html_title.
110 html_short_title = "Logbook " + release
111
112 # The name of an image file (relative to this directory) to place at the top
113 # of the sidebar.
114 #html_logo = None
115
116 # The name of an image file (within the static path) to use as favicon of the
117 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
118 # pixels large.
119 #html_favicon = None
120
121 # Add any paths that contain custom static files (such as style sheets) here,
122 # relative to this directory. They are copied after the builtin static files,
123 # so a file named "default.css" will overwrite the builtin "default.css".
124 #html_static_path = ['_static']
125
126 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
127 # using the given strftime format.
128 #html_last_updated_fmt = '%b %d, %Y'
129
130 # If true, SmartyPants will be used to convert quotes and dashes to
131 # typographically correct entities.
132 #html_use_smartypants = True
133
134 # Custom sidebar templates, maps document names to template names.
135 #html_sidebars = {}
136
137 # Additional templates that should be rendered to pages, maps page names to
138 # template names.
139 #html_additional_pages = {}
140
141 # If false, no module index is generated.
142 #html_domain_indices = True
143
144 # If false, no index is generated.
145 #html_use_index = True
146
147 # If true, the index is split into individual pages for each letter.
148 #html_split_index = False
149
150 # If true, links to the reST sources are added to the pages.
151 #html_show_sourcelink = True
152
153 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
154 #html_show_sphinx = True
155
156 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
157 #html_show_copyright = True
158
159 html_add_permalinks = False
160
161 # If true, an OpenSearch description file will be output, and all pages will
162 # contain a <link> tag referring to it. The value of this option must be the
163 # base URL from which the finished HTML is served.
164 #html_use_opensearch = ''
165
166 # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
167 #html_file_suffix = ''
168
169 # Output file base name for HTML help builder.
170 htmlhelp_basename = 'Logbookdoc'
171
172
173 # -- Options for LaTeX output --------------------------------------------------
174
175 # The paper size ('letter' or 'a4').
176 #latex_paper_size = 'letter'
177
178 # The font size ('10pt', '11pt' or '12pt').
179 #latex_font_size = '10pt'
180
181 # Grouping the document tree into LaTeX files. List of tuples
182 # (source start file, target name, title, author, documentclass [howto/manual]).
183 latex_documents = [
184 ('index', 'Logbook.tex', u'Logbook Documentation',
185 u'Armin Ronacher, Georg Brandl', 'manual'),
186 ]
187
188 # The name of an image file (relative to this directory) to place at the top of
189 # the title page.
190 #latex_logo = None
191
192 # For "manual" documents, if this is true, then toplevel headings are parts,
193 # not chapters.
194 #latex_use_parts = False
195
196 # If true, show page references after internal links.
197 #latex_show_pagerefs = False
198
199 # If true, show URL addresses after external links.
200 #latex_show_urls = False
201
202 # Additional stuff for the LaTeX preamble.
203 #latex_preamble = ''
204
205 # Documents to append as an appendix to all manuals.
206 #latex_appendices = []
207
208 # If false, no module index is generated.
209 #latex_domain_indices = True
210
211
212 # -- Options for manual page output --------------------------------------------
213
214 # One entry per manual page. List of tuples
215 # (source start file, name, description, authors, manual section).
216 man_pages = [
217 ('index', 'logbook', u'Logbook Documentation',
218 [u'Armin Ronacher, Georg Brandl'], 1)
219 ]
220
221 intersphinx_mapping = {
222 'http://docs.python.org': None
223 }
0 Design Principles
1 =================
2
3 .. currentmodule:: logbook
4
5 Logbook is a logging library that breaks many expectations people have in
6 logging libraries to support paradigms we think are more suitable for
7 modern applications than the traditional Java inspired logging system that
8 can also be found in the Python standard library and many more programming
9 languages.
10
11 This section of the documentation should help you understand the design of
12 Logbook and why it was implemented like this.
13
14 No Logger Registry
15 ------------------
16
17 Logbook is unique in that it has the concept of logging channels but that
18 it does not keep a global registry of them. In the standard library's
19 logging module a logger is attached to a tree of loggers that are stored
20 in the logging module itself as global state.
21
22 In logbook a logger is just an opaque object that might or might not have
23 a name and attached information such as log level or customizations, but
24 the lifetime and availability of that object is controlled by the person
25 creating that logger.
26
27 The registry is necessary for the logging library to give the user the
28 ability to configure these loggers.
29
30 Logbook has a completely different concept of dispatching from loggers to
31 the actual handlers which removes the requirement and usefulness of such a
32 registry. The advantage of the logbook system is that it's a cheap
33 operation to create a logger and that a logger can easily be garbage
34 collected to remove all traces of it.
35
36 Instead Logbook moves the burden of delivering a log record from the log
37 channel's attached log to an independent entity that looks at the context
38 of the execution to figure out where to deliver it.
39
40 Context Sensitive Handler Stack
41 -------------------------------
42
43 Python has two builtin ways to express implicit context: processes and
44 threads. What this means is that if you have a function that is passed no
45 arguments at all, you can figure out what thread called the function and
46 what process you are sitting in. Logbook supports this context
47 information and lets you bind a handler (or more!) for such a context.
48
49 This is how this works: there are two stacks available at all times in
50 Logbook. The first stack is the process wide stack. It is manipulated
51 with :class:`Handler.push_application` and
52 :class:`Handler.pop_application` (and of course the context manager
53 :class:`Handler.applicationbound`). Then there is a second stack which is
54 per thread. The manipulation of that stack happens with
55 :class:`Handler.push_thread`, :class:`Handler.pop_thread` and the
56 :class:`Handler.threadbound` contextmanager.
57
58 Let's take a WSGI web application as first example. When a request comes
59 in your WSGI server will most likely do one of the following two things:
60 either spawn a new Python process (or reuse a process in a pool), or
61 create a thread (or again, reuse something that already exists). Either
62 way, we can now say that the context of process id and thread id is our
63 playground. For this context we can define a log handler that is active
64 in this context only for a certain time. In pseudocode this would look
65 like this::
66
67 def my_application(environ, start_response):
68 my_handler = FileHandler(...)
69 my_handler.push_thread()
70 try:
71 # whatever happens here in terms of logging is handled
72 # by the `my_handler` handler.
73 ...
74 finally:
75 my_handler.pop_thread()
76
77 Because this is a lot to type, you can also use the `with` statement to do
78 the very same::
79
80 def my_application(environ, start_response):
81 with FileHandler(...).threadbound() as my_handler:
82 # whatever happens here in terms of logging is handled
83 # by the `my_handler` handler.
84 ...
85
86 Additionally there is another place where you can put handlers: directly
87 onto a logging channel (for example on a :class:`Logger`).
88
89 This stack system might seem like overkill for a traditional system, but
90 it allows complete decoupling from the log handling system and other
91 systems that might log messages.
92
93 Let's take a GUI application rather than a web application. You have an
94 application that starts up, shuts down and at any point in between might
95 fail or log messages. The typical default behaviour here would be to log
96 into a logfile. Fair enough, that's how these applications work.
97
98 But what's the point in logging if not even a single warning happened?
99 The traditional solution with the logging library from Python is to set
100 the level high (like `ERROR` or `WARNING`) and log into a file. When
101 things break, you have a look at the file and hope it contains enough
102 information.
103
104 When you are in full control of the context of execution with a stack based
105 system like Logbook has, there is a lot more you can do.
106
107 For example you could immediately after your application boots up
108 instanciate a :class:`~logbook.FingersCrossedHandler`. This handler
109 buffers *all* log records in memory and does not emit them at all. What's
110 the point? That handler activates when a certain threshold is reached.
111 For example, when the first warning occurs you can write the buffered
112 messages as well as the warning that just happened into a logfile and
113 continue logging from that point. Because there is no point in logging
114 when you will never look at that file anyways.
115
116 But that alone is not the killer feature of a stack. In a GUI application
117 there is the point where we are still initializing the windowing system.
118 So a file is the best place to log messages. But once we have the GUI
119 initialized, it would be very helpful to show error messages to a user in
120 a console window or a dialog. So what we can do is to initialize at that
121 point a new handler that logs into a dialog.
122
123 When then a long running tasks in the GUI starts we can move that into a
124 separate thread and intercept all the log calls for that thread into a
125 separate window until the task succeeded.
126
127 Here such a setup in pseudocode::
128
129 from logbook import FileHandler, WARNING
130 from logbook import FingersCrossedHandler
131
132 def main():
133 # first we set up a handler that logs everything (including debug
134 # messages, but only starts doing that when a warning happens
135 default_handler = FingersCrossedHandler(FileHandler(filename,
136 delay=True),
137 WARNING)
138 # this handler is now activated as the default handler for the
139 # whole process. We do not bubble up to the default handler
140 # that logs to stderr.
141 with default_handler.applicationbound(bubble=False):
142 # now we initialize the GUI of the application
143 initialize_gui()
144 # at that point we can hook our own logger in that intercepts
145 # errors and displays them in a log window
146 with gui.log_handler.applicationbound():
147 # run the gui mainloop
148 gui.mainloop()
149
150 This stack can also be used to inject additional information automatically
151 into log records. This is also used to replace the need for custom log
152 levels.
153
154 No Custom Log Levels
155 --------------------
156
157 This change over logging was controversial, even under the two original
158 core developers. There clearly are use cases for custom log levels, but
159 there is an inherent problem with then: they require a registry. If you
160 want custom log levels, you will have to register them somewhere or parts
161 of the system will not know about them. Now we just spent a lot of time
162 ripping out the registry with a stack based approach to solve delivery
163 problems, why introduce a global state again just for log levels?
164
165 Instead we looked at the cases where custom log levels are useful and
166 figured that in most situations custom log levels are used to put
167 additional information into a log entry. For example it's not uncommon to
168 have separate log levels to filter user input out of a logfile.
169
170 We instead provide powerful tools to inject arbitrary additional data into
171 log records with the concept of log processors.
172
173 So for example if you want to log user input and tag it appropriately you
174 can override the :meth:`Logger.process_record` method::
175
176 class InputLogger(Logger):
177 def process_record(self, record):
178 record.extra['kind'] = 'input'
179
180 A handler can then use this information to filter out input::
181
182 def no_input(record, handler):
183 return record.extra.get('kind') != 'input'
184
185 with MyHandler().threadbound(filter=no_input):
186 ...
187
188 Injecting Context-Sensitive Information
189 ---------------------------------------
190
191 For many situations it's not only necessary to inject information on a
192 per-channel basis but also for all logging calls from a given context.
193 This is best explained for web applications again. If you have some
194 libraries doing logging in code that is triggered from a request you might
195 want to record the URL of that request for each log record so that you get
196 an idea where a specific error happened.
197
198 This can easily be accomplished by registering a custom processor when
199 binding a handler to a thread::
200
201 def my_application(environ, start_reponse):
202 def inject_request_info(record, handler):
203 record.extra['path'] = environ['PATH_INFO']
204 with Processor(inject_request_info).threadbound():
205 with my_handler.threadbound():
206 # rest of the request code here
207 ...
208
209 Logging Compatibility
210 ---------------------
211
212 The last pillar of logbook's design is the compatibility with the standard
213 libraries logging system. There are many libraries that exist currently
214 that log information with the standard libraries logging module. Having
215 two separate logging systems in the same process is countrproductive and
216 will cause separate logfiles to appear in the best case or complete chaos
217 in the worst.
218
219 Because of that, logbook provides ways to transparently redirect all
220 logging records into the logbook stack based record delivery system. That
221 way you can even continue to use the standard libraries logging system to
222 emit log messages and can take the full advantage of logbook's powerful
223 stack system.
224
225 If you are curious, have a look at :ref:`logging-compat`.
0 The Design Explained
1 ====================
2
3 This part of the documentation explains the design of Logbook in detail.
4 This is not strictly necessary to make use of Logbook but might be helpful
5 when writing custom handlers for Logbook or when using it in a more
6 complex environment.
7
8 Dispatchers and Channels
9 ------------------------
10
11 Logbook does not use traditional loggers, instead a logger is internally
12 named as :class:`~logbook.base.RecordDispatcher`. While a logger also has
13 methods to create new log records, the base class for all record
14 dispatchers itself only has ways to dispatch :class:`~logbook.LogRecord`\s
15 to the handlers. A log record itself might have an attribute that points
16 to the dispatcher that was responsible for dispatching, but it does not
17 have to be.
18
19 If a log record was created from the builtin :class:`~logbook.Logger` it
20 will have the channel set to the name of the logger. But that itself is
21 no requirement. The only requirement for the channel is that it's a
22 string with some human readable origin information. It could be
23 ``'Database'`` if the database issued the log record, it could be
24 ``'Process-4223'`` if the process with the pid 4223 issued it etc.
25
26 For example if you are logging from the :func:`logbook.log` function they
27 will have a cannel set, but no dispatcher:
28
29 >>> from logbook import TestHandler, warn
30 >>> handler = TestHandler()
31 >>> handler.push_application()
32 >>> warn('This is a warning')
33 >>> handler.records[0].channel
34 'Generic'
35 >>> handler.records[0].dispatcher is None
36 True
37
38 If you are logging from a custom logger, the channel attribute points to
39 the logger for as long this logger class is not garbage collected:
40
41 >>> from logbook import Logger, TestHandler
42 >>> logger = Logger('Console')
43 >>> handler = TestHandler()
44 >>> handler.push_application()
45 >>> logger.warn('A warning')
46 >>> handler.records[0].dispatcher is logger
47 True
48
49 You don't need a record dispatcher to dispatch a log record though. The
50 default dispatching can be triggered from a function
51 :func:`~logbook.base.dispatch_record`:
52
53 >>> from logbook import dispatch_record, LogRecord, INFO
54 >>> record = LogRecord('My channel', INFO, 'Hello World!')
55 >>> dispatch_record(record)
56 [2010-09-04 15:56] INFO: My channel: Hello World!
57
58 It is pretty common for log records to be created without a dispatcher.
59 Here some common use cases for log records without a dispatcher:
60
61 - log records that were redirected from a different logging system
62 such as the standard library's :mod:`logging` module or the
63 :mod:`warnings` module.
64 - log records that came from different processes and do not have a
65 dispatcher equivalent in the current process.
66 - log records that came from over the network.
67
68 The Log Record Container
69 ------------------------
70
71 The :class:`~logbook.LogRecord` class is a simple container that
72 holds all the information necessary for a log record. Usually they are
73 created from a :class:`~logbook.Logger` or one of the default log
74 functions (:func:`logbook.warn` etc.) and immediately dispatched to the
75 handlers. The logger will apply some additional knowledge to figure out
76 where the record was created from and if a traceback information should be
77 attached.
78
79 Normally if log records are dispatched they will be closed immediately
80 after all handlers had their chance to write it down. On closing, the
81 interpreter frame and traceback object will be removed from the log record
82 to break up circular dependencies.
83
84 Sometimes however it might be necessary to keep log records around for a
85 longer time. Logbook provides three different ways to accomplish that:
86
87 1. Handlers can set the :attr:`~logbook.LogRecord.keep_open` attribute of
88 a log record to `True` so that the record dispatcher will not close
89 the object. This is for example used by the
90 :class:`~logbook.TestHandler` so that unittests can still access
91 interpreter frames and traceback objects if necessary.
92 2. Because some information on the log records depends on the interpreter
93 frame (such as the location of the log call) it is possible to pull
94 that related information directly into the log record so that it can
95 safely be closed without losing that information (see
96 :meth:`~logbook.LogRecord.pull_information`).
97 3. Last but not least, log records can be converted to dictionaries and
98 recreated from these. It is also possible to make these dictionaries
99 safe for JSON export which is used by the
100 :class:`~logbook.ticketing.TicketingHandler` to store information in a
101 database or the :class:`~logbook.more.MultiProcessingHandler` to send
102 information between processes.
0 What does it do?
1 ================
2
3 Although the Python standard library provides a logging system, you should
4 consider having a look at Logbook for your applications.
5
6 We think it will work out for you and be fun to use :)
7
8 Logbook leverages some features of Python that are not available in older Python releases.
9 Logbook currently requires Python 2.7 or higher including Python 3 (3.1 or
10 higher, 3.0 is not supported).
11
12 Core Features
13 -------------
14
15 - Logbook is based on the concept of loggers that are extensible by the
16 application.
17 - Each logger and handler, as well as other parts of the system, may inject
18 additional information into the logging record that improves the usefulness
19 of log entries.
20 - Handlers can be set on an application-wide stack as well as a thread-wide
21 stack. Setting a handler does not replace existing handlers, but gives it
22 higher priority. Each handler has the ability to prevent records from
23 propagating to lower-priority handlers.
24 - Logbook comes with a useful default configuration that spits all the
25 information to stderr in a useful manner.
26 - All of the built-in handlers have a useful default configuration applied with
27 formatters that provide all the available information in a format that
28 makes the most sense for the given handler. For example, a default stream
29 handler will try to put all the required information into one line, whereas
30 an email handler will split it up into nicely formatted ASCII tables that
31 span multiple lines.
32 - Logbook has built-in handlers for streams, arbitrary files, files with time
33 and size based rotation, a handler that delivers mails, a handler for the
34 syslog daemon as well as the NT log file.
35 - There is also a special "fingers crossed" handler that, in combination with
36 the handler stack, has the ability to accumulate all logging messages and
37 will deliver those in case a severity level was exceeded. For example, it
38 can withhold all logging messages for a specific request to a web
39 application until an error record appears, in which case it will also send
40 all withheld records to the handler it wraps. This way, you can always log
41 lots of debugging records, but only get see them when they can actually
42 tell you something of interest.
43 - It is possible to inject a handler for testing that records messages for
44 assertions.
45 - Logbook was designed to be fast and with modern Python features in mind.
46 For example, it uses context managers to handle the stack of handlers as
47 well as new-style string formatting for all of the core log calls.
48 - Builtin support for ZeroMQ, RabbitMQ, Redis and other means to distribute
49 log messages between heavily distributed systems and multiple processes.
50 - The Logbook system does not depend on log levels. In fact, custom log
51 levels are not supported, instead we strongly recommend using logging
52 subclasses or log processors that inject tagged information into the log
53 record for this purpose.
54 - :pep:`8` naming and code style.
55
56 Advantages over Logging
57 -----------------------
58
59 If properly configured, Logbook's logging calls will be very cheap and
60 provide a great performance improvement over an equivalent configuration
61 of the standard library's logging module. While for some parts we are not
62 quite at performance we desire, there will be some further performance
63 improvements in the upcoming versions.
64
65 It also supports the ability to inject additional information for all
66 logging calls happening in a specific thread or for the whole application.
67 For example, this makes it possible for a web application to add
68 request-specific information to each log record such as remote address,
69 request URL, HTTP method and more.
70
71 The logging system is (besides the stack) stateless and makes unit testing
72 it very simple. If context managers are used, it is impossible to corrupt
73 the stack, so each test can easily hook in custom log handlers.
74
75 Cooperation
76 -----------
77
78 Logbook is an addon library to Python and working in an area where there
79 are already a couple of contestants. First of all there is the standard
80 library's :mod:`logging` module, secondly there is also the
81 :mod:`warnings` module which is used internally in Python to warn about
82 invalid uses of APIs and more. We know that there are many situations
83 where you want to use either of them. Be it that they are integrated into
84 a legacy system, part of a library outside of your control or just because
85 they are a better choice.
86
87 Because of that, Logbook is two-way compatible with :mod:`logging` and
88 one-way compatible with :mod:`warnings`. If you want, you can let all
89 logging calls redirect to the logbook handlers or the other way round,
90 depending on what your desired setup looks like. That way you can enjoy
91 the best of both worlds.
92
93 It should be Fun
94 ----------------
95
96 Logging should be fun. A good log setup makes debugging easier when
97 things go rough. For good results you really have to start using logging
98 before things actually break. Logbook comes with a couple of unusual log
99 handlers to bring the fun back to logging. You can log to your personal
100 twitter feed, you can log to mobile devices, your desktop notification
101 system and more.
102
103 Logbook in a Nutshell
104 ---------------------
105
106 This is how easy it is to get started with Logbook::
107
108 from logbook import warn
109 warn('This is a warning')
110
111 That will use the default logging channel. But you can create as many as
112 you like::
113
114 from logbook import Logger
115 log = Logger('My Logger')
116 log.warn('This is a warning')
117
118 Roadmap
119 -------
120
121 Here a list of things you can expect in upcoming versions:
122
123 - c implementation of the internal stack management and record
124 dispatching for higher performance.
125 - a ticketing log handler that creates tickets in trac and redmine.
126 - a web frontend for the ticketing database handler.
0 Welcome to Logbook
1 ==================
2
3 Logbook is a logging sytem for Python that replaces the standard library's
4 logging module. It was designed with both complex and simple applications
5 in mind and the idea to make logging fun:
6
7 >>> from logbook import Logger
8 >>> log = Logger('Logbook')
9 >>> log.info('Hello, World!')
10 [2010-07-23 16:34] INFO: Logbook: Hello, World!
11
12 What makes it fun? What about getting log messages on your phone or
13 desktop notification system? :ref:`Logbook can do that <notifiers>`.
14
15 Feedback is appreciated. The docs here only show a tiny,
16 tiny feature set and can be incomplete. We will have better docs
17 soon, but until then we hope this gives a sneak peak about how cool
18 Logbook is. If you want more, have a look at the comprehensive suite of tests.
19
20 Documentation
21 -------------
22
23 .. toctree::
24 :maxdepth: 2
25
26 features
27 quickstart
28 setups
29 stacks
30 performance
31 libraries
32 unittesting
33 ticketing
34 compat
35 api/index
36 designexplained
37 designdefense
38 changelog
39
40 Project Information
41 -------------------
42
43 .. cssclass:: toctree-l1
44
45 * `Download from PyPI`_
46 * `Master repository on GitHub`_
47 * `Mailing list`_
48 * IRC: ``#pocoo`` on freenode
49
50 .. _Download from PyPI: http://pypi.python.org/pypi/Logbook
51 .. _Master repository on GitHub: https://github.com/mitsuhiko/logbook
52 .. _Mailing list: http://groups.google.com/group/pocoo-libs
0 Logbook in Libraries
1 ====================
2
3 Logging becomes more useful the higher the number of components in a
4 system that are using it. Logbook itself is not a widely supported
5 library so far, but a handful of libraries are using the :mod:`logging`
6 already which can be redirected to Logbook if necessary.
7
8 Logbook itself is easier to support for libraries than logging because it
9 does away with the central logger registry and can easily be mocked in
10 case the library is not available.
11
12 Mocking Logbook
13 ---------------
14
15 If you want to support Logbook in your library but not depend on it you
16 can copy/paste the following piece of code. It will attempt to import
17 logbook and create a :class:`~logbook.Logger` and if it fails provide a
18 class that just swallows all calls::
19
20 try:
21 from logbook import Logger
22 except ImportError:
23 class Logger(object):
24 def __init__(self, name, level=0):
25 self.name = name
26 self.level = level
27 debug = info = warn = warning = notice = error = exception = \
28 critical = log = lambda *a, **kw: None
29
30 log = Logger('My library')
31
32 Best Practices
33 --------------
34
35 - A library that wants to log to the Logbook system should generally be
36 designed to provide an interface to the record dispatchers it is
37 using. That does not have to be a reference to the record dispatcher
38 itself, it is perfectly fine if there is a toggle to switch it on or
39 off.
40
41 - The channel name should be readable and descriptive.
42
43 - For example, if you are a database library that wants to use the
44 logging system to log all SQL statements issued in debug mode, you can
45 enable and disable your record dispatcher based on that debug flag.
46
47 - Libraries should never set up log setups except temporarily on a
48 per-thread basis if it never changes the stack for a longer duration
49 than a function call in a library. For example, hooking in a null
50 handler for a call to a noisy function is fine, changing the global
51 stack in a function and not reverting it at the end of the function is
52 bad.
53
54 Debug Loggers
55 -------------
56
57 Sometimes you want to have loggers in place that are only really good for
58 debugging. For example you might have a library that does a lot of
59 server/client communication and for debugging purposes it would be nice if
60 you can enable/disable that log output as necessary.
61
62 In that case it makes sense to create a logger and disable that by default
63 and give people a way to get hold of the logger to flip the flag.
64 Additionally you can override the :attr:`~logbook.Logger.disabled` flag to
65 automatically set it based on another value::
66
67 class MyLogger(Logger):
68 @property
69 def disabled(self):
70 return not database_connection.debug
71 database_connection.logger = MyLogger('mylibrary.dbconnection')
0 @ECHO OFF
1
2 REM Command file for Sphinx documentation
3
4 if "%SPHINXBUILD%" == "" (
5 set SPHINXBUILD=sphinx-build
6 )
7 set BUILDDIR=_build
8 set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
9 if NOT "%PAPER%" == "" (
10 set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
11 )
12
13 if "%1" == "" goto help
14
15 if "%1" == "help" (
16 :help
17 echo.Please use `make ^<target^>` where ^<target^> is one of
18 echo. html to make standalone HTML files
19 echo. dirhtml to make HTML files named index.html in directories
20 echo. singlehtml to make a single large HTML file
21 echo. pickle to make pickle files
22 echo. json to make JSON files
23 echo. htmlhelp to make HTML files and a HTML help project
24 echo. qthelp to make HTML files and a qthelp project
25 echo. devhelp to make HTML files and a Devhelp project
26 echo. epub to make an epub
27 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
28 echo. text to make text files
29 echo. man to make manual pages
30 echo. changes to make an overview over all changed/added/deprecated items
31 echo. linkcheck to check all external links for integrity
32 echo. doctest to run all doctests embedded in the documentation if enabled
33 goto end
34 )
35
36 if "%1" == "clean" (
37 for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
38 del /q /s %BUILDDIR%\*
39 goto end
40 )
41
42 if "%1" == "html" (
43 %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
44 echo.
45 echo.Build finished. The HTML pages are in %BUILDDIR%/html.
46 goto end
47 )
48
49 if "%1" == "dirhtml" (
50 %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
51 echo.
52 echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
53 goto end
54 )
55
56 if "%1" == "singlehtml" (
57 %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
58 echo.
59 echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
60 goto end
61 )
62
63 if "%1" == "pickle" (
64 %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
65 echo.
66 echo.Build finished; now you can process the pickle files.
67 goto end
68 )
69
70 if "%1" == "json" (
71 %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
72 echo.
73 echo.Build finished; now you can process the JSON files.
74 goto end
75 )
76
77 if "%1" == "htmlhelp" (
78 %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
79 echo.
80 echo.Build finished; now you can run HTML Help Workshop with the ^
81 .hhp project file in %BUILDDIR%/htmlhelp.
82 goto end
83 )
84
85 if "%1" == "qthelp" (
86 %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
87 echo.
88 echo.Build finished; now you can run "qcollectiongenerator" with the ^
89 .qhcp project file in %BUILDDIR%/qthelp, like this:
90 echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Logbook.qhcp
91 echo.To view the help file:
92 echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Logbook.ghc
93 goto end
94 )
95
96 if "%1" == "devhelp" (
97 %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
98 echo.
99 echo.Build finished.
100 goto end
101 )
102
103 if "%1" == "epub" (
104 %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
105 echo.
106 echo.Build finished. The epub file is in %BUILDDIR%/epub.
107 goto end
108 )
109
110 if "%1" == "latex" (
111 %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
112 echo.
113 echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
114 goto end
115 )
116
117 if "%1" == "text" (
118 %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
119 echo.
120 echo.Build finished. The text files are in %BUILDDIR%/text.
121 goto end
122 )
123
124 if "%1" == "man" (
125 %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
126 echo.
127 echo.Build finished. The manual pages are in %BUILDDIR%/man.
128 goto end
129 )
130
131 if "%1" == "changes" (
132 %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
133 echo.
134 echo.The overview file is in %BUILDDIR%/changes.
135 goto end
136 )
137
138 if "%1" == "linkcheck" (
139 %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
140 echo.
141 echo.Link check complete; look for any errors in the above output ^
142 or in %BUILDDIR%/linkcheck/output.txt.
143 goto end
144 )
145
146 if "%1" == "doctest" (
147 %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
148 echo.
149 echo.Testing of doctests in the sources finished, look at the ^
150 results in %BUILDDIR%/doctest/output.txt.
151 goto end
152 )
153
154 :end
0 Performance Tuning
1 ==================
2
3 The more logging calls you add to your application and libraries, the more
4 overhead will you introduce. There are a couple things you can do to
5 remedy this behavior.
6
7 Debug-Only Logging
8 ------------------
9
10 There are debug log calls, and there are debug log calls. Some debug log
11 calls would sometimes be interesting in a production environment, others
12 really only if you are on your local machine fiddling around with the
13 code. Logbook internally makes sure to process as little of your logging
14 call as necessary, but it will still have to walk the current stack to
15 figure out if there are any active handlers or not. Depending on the
16 number of handlers on the stack, the kind of handler etc, there will be
17 more or less processed.
18
19 Generally speaking a not-handled logging call is cheap enough that you
20 don't have to care about it. However there is not only your logging call,
21 there might also be some data you have to process for the record. This
22 will always be processed, even if the log record ends up being discarded.
23
24 This is where the Python ``__debug__`` feature comes in handy. This
25 variable is a special flag that is evaluated at the time where Python
26 processes your script. It can elliminate code completely from your script
27 so that it does not even exist in the compiled bytecode (requires Python
28 to be run with the ``-O`` switch)::
29
30 if __debug__:
31 info = get_wallcalculate_debug_info()
32 logger.debug("Call to response() failed. Reason: {0}", info)
33
34 Keep the Fingers Crossed
35 ------------------------
36
37 Do you really need the debug info? In case you find yourself only looking
38 at the logfiles when errors occurred it would be an option to put in the
39 :class:`~logbook.FingersCrossedHandler`. Logging into memory is always
40 cheaper than logging on a filesystem.
41
42 Keep the Stack Static
43 ---------------------
44
45 Whenever you do a push or pop from one of the stacks you will invalidate
46 an internal cache that is used by logbook. This is an implementation
47 detail, but this is how it works for the moment. That means that the
48 first logging call after a push or pop will have a higher impact on the
49 performance than following calls. That means you should not attempt to
50 push or pop from a stack for each logging call. Make sure to do the
51 pushing and popping only as needed. (start/end of application/request)
52
53 Disable Introspection
54 ---------------------
55
56 By default Logbook will try to pull in the interpreter frame of the caller
57 that invoked a logging function. While this is a fast operation that
58 usually does not slow down the execution of your script it also means that
59 for certain Python implementations it invalidates assumptions a JIT
60 compiler might have made of the function body. Currently this for example
61 is the case for applications running on pypy. If you would be using a
62 stock logbook setup on pypy, the JIT wouldn't be able to work properly.
63
64 In case you don't need the frame based information (name of module,
65 calling function, filename, line number) you can disable the introspection
66 feature::
67
68 from logbook import Flags
69
70 with Flags(introspection=False):
71 # all logging calls here will not use introspection
72 ...
0 Quickstart
1 ==========
2
3 .. currentmodule:: logbook
4
5 Logbook makes it very easy to get started with logging. Just import the logger
6 class, create yourself a logger and you are set:
7
8 >>> from logbook import Logger
9 >>> log = Logger('My Awesome Logger')
10 >>> log.warn('This is too cool for stdlib')
11 [2010-07-23 16:34] WARNING: My Awesome Logger: This is too cool for stdlib
12
13 A logger is a so-called :class:`~logbook.base.RecordDispatcher`, which is
14 commonly referred to as a "logging channel". The name you give such a channel
15 is up to you and need not be unique although it's a good idea to keep it
16 unique so that you can filter by it if you want.
17
18 The basic interface is similar to what you may already know from the standard
19 library's :mod:`logging` module.
20
21 There are several logging levels, available as methods on the logger. The
22 levels -- and their suggested meaning -- are:
23
24 * ``critical`` -- for errors that lead to termination
25 * ``error`` -- for errors that occur, but are handled
26 * ``warning`` -- for exceptional circumstances that might not be errors
27 * ``notice`` -- for non-error messages you usually want to see
28 * ``info`` -- for messages you usually don't want to see
29 * ``debug`` -- for debug messages
30
31 Each of these levels is available as method on the :class:`Logger`.
32 Additionally the ``warning`` level is aliased as :meth:`~Logger.warn`.
33
34 Alternatively, there is the :meth:`~Logger.log` method that takes the logging
35 level (string or integer) as an argument.
36
37 Handlers
38 --------
39
40 Each call to a logging method creates a log *record* which is then passed to
41 *handlers*, which decide how to store or present the logging info. There are a
42 multitude of available handlers, and of course you can also create your own:
43
44 * :class:`StreamHandler` for logging to arbitrary streams
45 * :class:`StderrHandler` for logging to stderr
46 * :class:`FileHandler`, :class:`MonitoringFileHandler`,
47 :class:`RotatingFileHandler` and :class:`TimedRotatingFileHandler` for
48 logging to files
49 * :class:`MailHandler` and :class:`GMailHandler` for logging via e-mail
50 * :class:`SyslogHandler` for logging to the syslog daemon
51 * :class:`NTEventLogHandler` for logging to the Windows NT event log
52
53 On top of those there are a couple of handlers for special use cases:
54
55 * :class:`logbook.FingersCrossedHandler` for logging into memory and
56 delegating information to another handler when a certain level was
57 exceeded, otherwise discarding all buffered records.
58 * :class:`logbook.more.TaggingHandler` for dispatching log records that
59 are tagged (used in combination with a
60 :class:`logbook.more.TaggingLogger`)
61 * :class:`logbook.queues.ZeroMQHandler` for logging to ZeroMQ
62 * :class:`logbook.queues.RedisHandler` for logging to Redis
63 * :class:`logbook.queues.MultiProcessingHandler` for logging from a child
64 process to a handler from the outer process.
65 * :class:`logbook.queues.ThreadedWrapperHandler` for moving the actual
66 handling of a handler into a background thread and using a queue to
67 deliver records to that thread.
68 * :class:`logbook.notifiers.GrowlHandler` and
69 :class:`logbook.notifiers.LibNotifyHandler` for logging to the OS X Growl
70 or the linux notification daemon.
71 * :class:`logbook.notifiers.BoxcarHandler` for logging to `boxcar`_.
72 * :class:`logbook.more.TwitterHandler` for logging to twitter.
73 * :class:`logbook.more.ExternalApplicationHandler` for logging to an
74 external application such as the OS X ``say`` command.
75 * :class:`logbook.ticketing.TicketingHandler` for creating tickets from
76 log records in a database or other data store.
77
78 .. _boxcar: http://boxcar.io/
79
80 Registering Handlers
81 --------------------
82
83 So how are handlers registered? If you are used to the standard Python logging
84 system, it works a little bit differently here. Handlers can be registered for
85 a thread or for a whole process or individually for a logger. However, it is
86 strongly recommended not to add handlers to loggers unless there is a very good
87 use case for that.
88
89 If you want errors to go to syslog, you can set up logging like this::
90
91 from logbook import SyslogHandler
92
93 error_handler = SyslogHandler('logbook example', level='ERROR')
94 with error_handler.applicationbound():
95 # whatever is executed here and an error is logged to the
96 # error handler
97 ...
98
99 This will send all errors to the syslog but warnings and lower record
100 levels still to stderr. This is because the handler is not bubbling by
101 default which means that if a record is handled by the handler, it will
102 not bubble up to a higher handler. If you want to display all records on
103 stderr, even if they went to the syslog you can enable bubbling by setting
104 *bubble* to ``True``::
105
106 from logbook import SyslogHandler
107
108 error_handler = SyslogHandler('logbook example', level='ERROR', bubble=True)
109 with error_handler.applicationbound():
110 # whatever is executed here and an error is logged to the
111 # error handler but it will also bubble up to the default
112 # stderr handler.
113 ...
114
115 So what if you want to only log errors to the syslog and nothing to
116 stderr? Then you can combine this with a :class:`NullHandler`::
117
118 from logbook import SyslogHandler, NullHandler
119
120 error_handler = SyslogHandler('logbook example', level='ERROR')
121 null_handler = NullHandler()
122
123 with null_handler.applicationbound():
124 with error_handler.applicationbound():
125 # errors now go to the error_handler and everything else
126 # is swallowed by the null handler so nothing ends up
127 # on the default stderr handler
128 ...
129
130 Record Processors
131 -----------------
132
133 What makes logbook interesting is the ability to automatically process log
134 records. This is handy if you want additional information to be logged for
135 everything you do. A good example use case is recording the IP of the current
136 request in a web application. Or, in a daemon process you might want to log
137 the user and working directory of the process.
138
139 A context processor can be injected at two places: you can either bind a
140 processor to a stack like you do with handlers or you can override the
141 override the :meth:`.RecordDispatcher.process_record` method.
142
143 Here an example that injects the current working directory into the
144 `extra` dictionary of a log record::
145
146 import os
147 from logbook import Processor
148
149 def inject_cwd(record):
150 record.extra['cwd'] = os.getcwd()
151
152 with my_handler.applicationbound():
153 with Processor(inject_cwd).applicationbound():
154 # everything logged here will have the current working
155 # directory in the log record.
156 ...
157
158 The alternative is to inject information just for one logger in which case
159 you might want to subclass it::
160
161 import os
162
163 class MyLogger(logbook.Logger):
164
165 def process_record(self, record):
166 logbook.Logger.process_record(self, record)
167 record.extra['cwd'] = os.getcwd()
168
169
170 Configuring the Logging Format
171 ------------------------------
172
173 All handlers have a useful default log format you don't have to change to use
174 logbook. However if you start injecting custom information into log records,
175 it makes sense to configure the log formatting so that you can see that
176 information.
177
178 There are two ways to configure formatting: you can either just change the
179 format string or hook in a custom format function.
180
181 All the handlers that come with logbook and that log into a string use the
182 :class:`~logbook.StringFormatter` by default. Their constructors accept a
183 format string which sets the :attr:`logbook.Handler.format_string` attribute.
184 You can override this attribute in which case a new string formatter is set:
185
186 >>> from logbook import StderrHandler
187 >>> handler = StderrHandler()
188 >>> handler.format_string = '{record.channel}: {record.message}'
189 >>> handler.formatter
190 <logbook.handlers.StringFormatter object at 0x100641b90>
191
192 Alternatively you can also set a custom format function which is invoked
193 with the record and handler as arguments:
194
195 >>> def my_formatter(record, handler):
196 ... return record.message
197 ...
198 >>> handler.formatter = my_formatter
199
200 The format string used for the default string formatter has one variable called
201 `record` available which is the log record itself. All attributes can be
202 looked up using the dotted syntax, and items in the `extra` dict looked up
203 using brackets. Note that if you are accessing an item in the extra dict that
204 does not exist, an empty string is returned.
205
206 Here is an example configuration that shows the current working directory from
207 the example in the previous section::
208
209 handler = StderrHandler(format_string=
210 '{record.channel}: {record.message) [{record.extra[cwd]}]')
211
212 In the :mod:`~logbook.more` module there is a formatter that uses the Jinja2
213 template engine to format log records, especially useful for multi-line log
214 formatting such as mails (:class:`~logbook.more.JinjaFormatter`).
0 Common Logbook Setups
1 =====================
2
3 This part of the documentation shows how you can configure Logbook for
4 different kinds of setups.
5
6
7 Desktop Application Setup
8 -------------------------
9
10 If you develop a desktop application (command line or GUI), you probably have a line
11 like this in your code::
12
13 if __name__ == '__main__':
14 main()
15
16 This is what you should wrap with a ``with`` statement that sets up your log
17 handler::
18
19 from logbook import FileHandler
20 log_handler = FileHandler('application.log')
21
22 if __name__ == '__main__':
23 with log_handler.applicationbound():
24 main()
25
26 Alternatively you can also just push a handler in there::
27
28 from logbook import FileHandler
29 log_handler = FileHandler('application.log')
30 log_handler.push_application()
31
32 if __name__ == '__main__':
33 main()
34
35 Please keep in mind that you will have to pop the handlers in reverse order if
36 you want to remove them from the stack, so it is recommended to use the context
37 manager API if you plan on reverting the handlers.
38
39 Web Application Setup
40 ---------------------
41
42 Typical modern web applications written in Python have two separate contexts
43 where code might be executed: when the code is imported, as well as when a
44 request is handled. The first case is easy to handle, just push a global file
45 handler that writes everything into a file.
46
47 But Logbook also gives you the ability to improve upon the logging. For
48 example, you can easily create yourself a log handler that is used for
49 request-bound logging that also injects additional information.
50
51 For this you can either subclass the logger or you can bind to the handler with
52 a function that is invoked before logging. The latter has the advantage that it
53 will also be triggered for other logger instances which might be used by a
54 different library.
55
56 Here is a simple WSGI example application that showcases sending error mails for
57 errors happened during a WSGI application::
58
59 from logbook import MailHandler
60
61 mail_handler = MailHandler('errors@example.com',
62 ['admin@example.com'],
63 format_string=u'''\
64 Subject: Application Error at {record.extra[url]}
65
66 Message type: {record.level_name}
67 Location: {record.filename}:{record.lineno}
68 Module: {record.module}
69 Function: {record.func_name}
70 Time: {record.time:%Y-%m-%d %H:%M:%S}
71 Remote IP: {record.extra[ip]}
72 Request: {record.extra[url]} [{record.extra[method]}]
73
74 Message:
75
76 {record.message}
77 ''', bubble=True)
78
79 def application(environ, start_response):
80 request = Request(environ)
81
82 def inject_info(record, handler):
83 record.extra.update(
84 ip=request.remote_addr,
85 method=request.method,
86 url=request.url
87 )
88
89 with mail_handler.threadbound(processor=inject_info):
90 # standard WSGI processing happens here. If an error
91 # is logged, a mail will be sent to the admin on
92 # example.com
93 ...
94
95 Deeply Nested Setups
96 --------------------
97
98 If you want deeply nested logger setups, you can use the
99 :class:`~logbook.NestedSetup` class which simplifies that. This is best
100 explained using an example::
101
102 import os
103 from logbook import NestedSetup, NullHandler, FileHandler, \
104 MailHandler, Processor
105
106 def inject_information(record):
107 record.extra['cwd'] = os.getcwd()
108
109 # a nested handler setup can be used to configure more complex setups
110 setup = NestedSetup([
111 # make sure we never bubble up to the stderr handler
112 # if we run out of setup handling
113 NullHandler(),
114 # then write messages that are at least warnings to to a logfile
115 FileHandler('application.log', level='WARNING'),
116 # errors should then be delivered by mail and also be kept
117 # in the application log, so we let them bubble up.
118 MailHandler('servererrors@example.com',
119 ['admin@example.com'],
120 level='ERROR', bubble=True),
121 # while we're at it we can push a processor on its own stack to
122 # record additional information. Because processors and handlers
123 # go to different stacks it does not matter if the processor is
124 # added here at the bottom or at the very beginning. Same would
125 # be true for flags.
126 Processor(inject_information)
127 ])
128
129 Once such a complex setup is defined, the nested handler setup can be used as if
130 it was a single handler::
131
132 with setup.threadbound():
133 # everything here is handled as specified by the rules above.
134 ...
135
136
137 Distributed Logging
138 -------------------
139
140 For applications that are spread over multiple processes or even machines
141 logging into a central system can be a pain. Logbook supports ZeroMQ to
142 deal with that. You can set up a :class:`~logbook.queues.ZeroMQHandler`
143 that acts as ZeroMQ publisher and will send log records encoded as JSON
144 over the wire::
145
146 from logbook.queues import ZeroMQHandler
147 handler = ZeroMQHandler('tcp://127.0.0.1:5000')
148
149 Then you just need a separate process that can receive the log records and
150 hand it over to another log handler using the
151 :class:`~logbook.queues.ZeroMQSubscriber`. The usual setup is this::
152
153 from logbook.queues import ZeroMQSubscriber
154 subscriber = ZeroMQSubscriber('tcp://127.0.0.1:5000')
155 with my_handler:
156 subscriber.dispatch_forever()
157
158 You can also run that loop in a background thread with
159 :meth:`~logbook.queues.ZeroMQSubscriber.dispatch_in_background`::
160
161 from logbook.queues import ZeroMQSubscriber
162 subscriber = ZeroMQSubscriber('tcp://127.0.0.1:5000')
163 subscriber.dispatch_in_background(my_handler)
164
165 If you just want to use this in a :mod:`multiprocessing` environment you
166 can use the :class:`~logbook.queues.MultiProcessingHandler` and
167 :class:`~logbook.queues.MultiProcessingSubscriber` instead. They work the
168 same way as the ZeroMQ equivalents but are connected through a
169 :class:`multiprocessing.Queue`::
170
171 from multiprocessing import Queue
172 from logbook.queues import MultiProcessingHandler, \
173 MultiProcessingSubscriber
174 queue = Queue(-1)
175 handler = MultiProcessingHandler(queue)
176 subscriber = MultiProcessingSubscriber(queue)
177
178 There is also the possibility to log into a Redis instance using the
179 :class:`~logbook.queues.RedisHandler`. To do so, you just need to create an
180 instance of this handler as follows::
181
182 import logbook
183 from logbook.queues import RedisHandler
184
185 handler = RedisHandler()
186 l = logbook.Logger()
187 with handler:
188 l.info('Your log message')
189
190 With the default parameters, this will send a message to redis under the key redis.
191
192
193 Redirecting Single Loggers
194 --------------------------
195
196 If you want to have a single logger go to another logfile you have two
197 options. First of all you can attach a handler to a specific record
198 dispatcher. So just import the logger and attach something::
199
200 from yourapplication.yourmodule import logger
201 logger.handlers.append(MyHandler(...))
202
203 Handlers attached directly to a record dispatcher will always take
204 precedence over the stack based handlers. The bubble flag works as
205 expected, so if you have a non-bubbling handler on your logger and it
206 always handles, it will never be passed to other handlers.
207
208 Secondly you can write a handler that looks at the logging channel and
209 only accepts loggers of a specific kind. You can also do that with a
210 filter function::
211
212 handler = MyHandler(filter=lambda r: r.channel == 'app.database')
213
214 Keep in mind that the channel is intended to be a human readable string
215 and is not necessarily unique. If you really need to keep loggers apart
216 on a central point you might want to introduce some more meta information
217 into the extra dictionary.
218
219 You can also compare the dispatcher on the log record::
220
221 from yourapplication.yourmodule import logger
222 handler = MyHandler(filter=lambda r: r.dispatcher is logger)
223
224 This however has the disadvantage that the dispatcher entry on the log
225 record is a weak reference and might go away unexpectedly and will not be
226 there if log records are sent to a different process.
227
228 Last but not least you can check if you can modify the stack around the
229 execution of the code that triggers that logger For instance if the
230 logger you are interested in is used by a specific subsystem, you can
231 modify the stacks before calling into the system.
0 {% extends "basic/layout.html" %}
1
2 {% block extrahead %}
3 {% if online %}
4 <link href='http://fonts.googleapis.com/css?family=OFL+Sorts+Mill+Goudy+TT|Inconsolata'
5 rel='stylesheet' type='text/css'>
6 {% endif %}
7 {% endblock %}
8
9 {% block header %}
10 <div class="book">
11 <div class="banner">
12 <a href="{{ pathto(master_doc) }}">
13 <!-- <img src="{{ pathto('_static/' + logo, 1) }}" alt="{{ project }} logo"></img> -->
14 <h1>{{ project }} </h1>
15 </a>
16 </div>
17 {% endblock %}
18
19 {% block footer %}
20 {% if online %}
21 <a href="http://github.com/mitsuhiko/logbook">
22 <img style="position: fixed; top: 0; right: 0; border: 0;"
23 src="http://s3.amazonaws.com/github/ribbons/forkme_right_gray_6d6d6d.png"
24 alt="Fork me on GitHub">
25 </a>
26 {% endif %}
27 {{ super() }}
28 </div>
29 {% endblock %}
0 /*
1 * sheet.css
2 * ~~~~~~~~~
3 *
4 * Sphinx stylesheet for the sheet theme.
5 *
6 * :copyright: Copyright 2010 by Armin Ronacher, Georg Brandl.
7 * :license: BSD, see LICENSE for details.
8 *
9 */
10
11 @import url("basic.css");
12
13 body {
14 text-align: center;
15 font-family: {{ theme_bodyfont }};
16 margin: 0;
17 padding: 0;
18 background: #d5dde2;
19 }
20
21 .book {
22 padding: 15px 25px 25px 85px;
23 margin: 0 auto;
24 width: 695px;
25 text-align: left;
26 background: white url("background.png") repeat-y;
27 }
28
29 a {
30 font-weight: bold;
31 color: #003366;
32 }
33
34 h1, h2, h3, h4, h5, h6 {
35 font-family: {{ theme_seriffont }};
36 font-weight: normal;
37 }
38
39 h1 { font-size: 2.8em; }
40 h2 { font-size: 2.2em; }
41
42 .document {
43 border-bottom: 1px solid #837D7C;
44 border-top: 1px solid #837D7C;
45 margin: 12px 0px;
46 line-height: 1.5em;
47 }
48
49 .related a {
50 margin: 0;
51 font-size: 1.1em;
52 font-weight: normal;
53 letter-spacing: 3px;
54 text-transform: uppercase;
55 text-decoration: none;
56 border-bottom: 1px dashed #ddd;
57 color: #837D7C;
58 }
59
60 div.related ul {
61 margin-right: -10px;
62 padding: 0px;
63 }
64
65 .banner h1 {
66 font-size: 4.5em;
67 font-weight: normal;
68 line-height: 1;
69 letter-spacing: -3px;
70 margin-top: 12px;
71 margin-bottom: 12px;
72 }
73
74 .banner {
75 color: #000000;
76 letter-spacing: -1px;
77 font-family: {{ theme_seriffont }};
78 margin-bottom: 24px;
79 }
80
81 .banner a {
82 color: #000000;
83 text-decoration: none;
84 }
85
86 .footer {
87 color: #000000;
88 font-size: 0.7em;
89 text-align: center;
90 line-height: 1;
91 margin-top: 20px;
92 font-family: {{ theme_monofont }};
93 font-weight: normal;
94 letter-spacing: 2px;
95 text-transform: uppercase;
96 text-decoration: none;
97 color: #837D7C;
98 }
99
100 .highlight pre {
101 background-color: #f8f8f8;
102 border-top: 1px solid #c8c8c8;
103 border-bottom: 1px solid #c8c8c8;
104 line-height: 120%;
105 padding: 10px 6px;
106 }
107
108 .highlighttable .highlight pre {
109 margin: 0px;
110 }
111
112 div.sphinxsidebar {
113 margin-left: 0px;
114 float: none;
115 width: 100%;
116 font-size: 0.8em;
117 }
118
119 .toctree-l1 a {
120 text-decoration: none;
121 }
122
123 img.align-right {
124 margin-left: 24px;
125 }
126
127 pre, tt {
128 font-family: {{ theme_monofont }};
129 font-size: 15px!important;
130 }
131
132 dl.class dt {
133 padding-left: 60px;
134 text-indent: -60px;
135 }
136
137 tt.descname {
138 font-size: 1em;
139 }
140
141 p.output-caption {
142 font-size: small;
143 margin: 0px;
144 }
0 [theme]
1 inherit = basic
2 stylesheet = sheet.css
3 pygments_style = tango
4
5 [options]
6 bodyfont = 'Cantarell', 'Lucida Grande', sans-serif
7 seriffont = 'OFL Sorts Mill Goudy TT', 'Georgia', 'Bitstream Vera Serif', serif
8 monofont = 'Consolas', 'Inconsolata', 'Bitstream Vera Sans Mono', monospace
0 Stacks in Logbook
1 =================
2
3 Logbook keeps three stacks internally currently:
4
5 - one for the :class:`~logbook.Handler`\s: each handler is handled from
6 stack top to bottom. When a record was handled it depends on the
7 :attr:`~logbook.Handler.bubble` flag of the handler if it should still
8 be processed by the next handler on the stack.
9 - one for the :class:`~logbook.Processor`\s: each processor in the stack
10 is applied on a record before the log record is handled by the
11 handler.
12 - one for the :class:`~logbook.Flags`: this stack manages simple flags
13 such as how errors during logging should be processed or if stackframe
14 introspection should be used etc.
15
16 General Stack Management
17 ------------------------
18
19 Generally all objects that are management by stacks have a common
20 interface (:class:`~logbook.base.StackedObject`) and can be used in
21 combination with the :class:`~logbook.NestedSetup` class.
22
23 Commonly stacked objects are used with a context manager (`with`
24 statement)::
25
26 with context_object.threadbound():
27 # this is managed for this thread only
28 ...
29
30 with context_object.applicationbound():
31 # this is managed for all applications
32 ...
33
34 Alternatively you can also use `try`/`finally`::
35
36 context_object.push_thread()
37 try:
38 # this is managed for this thread only
39 ...
40 finally:
41 context_object.pop_thread()
42
43 context_object.push_application()
44 try:
45 # this is managed for all applications
46 ...
47 finally:
48 context_object.pop_application()
49
50 It's very important that you will always pop from the stack again unless
51 you really want the change to last until the application closes down,
52 which probably is not the case.
53
54 If you want to push and pop multiple stacked objects at the same time, you
55 can use the :class:`~logbook.NestedSetup`::
56
57 setup = NestedSetup([stacked_object1, stacked_object2])
58