python-peps/pep-0282/index.html

742 lines
50 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 282 A Logging System | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-0282/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 282 A Logging System | peps.python.org'>
<meta property="og:description" content="This PEP describes a proposed logging package for Pythons standard library.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-0282/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="This PEP describes a proposed logging package for Pythons standard library.">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 282</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 282 A Logging System</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Vinay Sajip &lt;vinay_sajip at red-dove.com&gt;,
Trent Mick &lt;trentm&#32;&#97;t&#32;activestate.com&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">04-Feb-2002</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">2.3</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><p></p></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#influences">Influences</a></li>
<li><a class="reference internal" href="#simple-example">Simple Example</a></li>
<li><a class="reference internal" href="#control-flow">Control Flow</a></li>
<li><a class="reference internal" href="#levels">Levels</a></li>
<li><a class="reference internal" href="#loggers">Loggers</a></li>
<li><a class="reference internal" href="#handlers">Handlers</a></li>
<li><a class="reference internal" href="#logrecords">LogRecords</a></li>
<li><a class="reference internal" href="#formatters">Formatters</a></li>
<li><a class="reference internal" href="#filters">Filters</a></li>
<li><a class="reference internal" href="#configuration">Configuration</a></li>
<li><a class="reference internal" href="#thread-safety">Thread Safety</a></li>
<li><a class="reference internal" href="#module-level-functions">Module-Level Functions</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#packaging">Packaging</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>This PEP describes a proposed logging package for Pythons
standard library.</p>
<p>Basically the system involves the user creating one or more logger
objects on which methods are called to log debugging notes,
general information, warnings, errors etc. Different logging
levels can be used to distinguish important messages from less
important ones.</p>
<p>A registry of named singleton logger objects is maintained so that</p>
<ol class="arabic simple">
<li>different logical logging streams (or channels) exist
(say, one for zope.zodb stuff and another for
mywebsite-specific stuff)</li>
<li>one does not have to pass logger object references around.</li>
</ol>
<p>The system is configurable at runtime. This configuration
mechanism allows one to tune the level and type of logging done
while not touching the application itself.</p>
</section>
<section id="motivation">
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
<p>If a single logging mechanism is enshrined in the standard
library, 1) logging is more likely to be done well, and 2)
multiple libraries will be able to be integrated into larger
applications which can be logged reasonably coherently.</p>
</section>
<section id="influences">
<h2><a class="toc-backref" href="#influences" role="doc-backlink">Influences</a></h2>
<p>This proposal was put together after having studied the
following logging packages:</p>
<ul class="simple">
<li>java.util.logging in JDK 1.4 (a.k.a. JSR047) <a class="footnote-reference brackets" href="#id11" id="id1">[1]</a></li>
<li>log4j <a class="footnote-reference brackets" href="#id12" id="id2">[2]</a></li>
<li>the Syslog package from the Protomatter project <a class="footnote-reference brackets" href="#id13" id="id3">[3]</a></li>
<li>MALs mx.Log package <a class="footnote-reference brackets" href="#id14" id="id4">[4]</a></li>
</ul>
</section>
<section id="simple-example">
<h2><a class="toc-backref" href="#simple-example" role="doc-backlink">Simple Example</a></h2>
<p>This shows a very simple example of how the logging package can be
used to generate simple logging output on stderr.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="o">---------</span> <span class="n">mymodule</span><span class="o">.</span><span class="n">py</span> <span class="o">-------------------------------</span>
<span class="kn">import</span> <span class="nn">logging</span>
<span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;MyModule&quot;</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">doIt</span><span class="p">():</span>
<span class="n">log</span><span class="o">.</span><span class="n">debug</span><span class="p">(</span><span class="s2">&quot;Doin&#39; stuff...&quot;</span><span class="p">)</span>
<span class="c1">#do stuff...</span>
<span class="k">raise</span> <span class="ne">TypeError</span><span class="p">,</span> <span class="s2">&quot;Bogus type error for testing&quot;</span>
<span class="o">-----------------------------------------------------</span>
</pre></div>
</div>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="o">---------</span> <span class="n">myapp</span><span class="o">.</span><span class="n">py</span> <span class="o">----------------------------------</span>
<span class="kn">import</span> <span class="nn">mymodule</span><span class="o">,</span> <span class="nn">logging</span>
<span class="n">logging</span><span class="o">.</span><span class="n">basicConfig</span><span class="p">()</span>
<span class="n">log</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;MyApp&quot;</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Starting my app&quot;</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">mymodule</span><span class="o">.</span><span class="n">doIt</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
<span class="n">log</span><span class="o">.</span><span class="n">exception</span><span class="p">(</span><span class="s2">&quot;There was a problem.&quot;</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Ending my app&quot;</span><span class="p">)</span>
<span class="o">-----------------------------------------------------</span>
</pre></div>
</div>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>python<span class="w"> </span>myapp.py
<span class="go">INFO:MyApp: Starting my app</span>
<span class="go">DEBUG:MyModule: Doin&#39; stuff...</span>
<span class="go">ERROR:MyApp: There was a problem.</span>
<span class="go">Traceback (most recent call last):</span>
<span class="go"> File &quot;myapp.py&quot;, line 9, in ?</span>
<span class="go"> mymodule.doIt()</span>
<span class="go"> File &quot;mymodule.py&quot;, line 7, in doIt</span>
<span class="go"> raise TypeError, &quot;Bogus type error for testing&quot;</span>
<span class="go">TypeError: Bogus type error for testing</span>
<span class="go">INFO:MyApp: Ending my app</span>
</pre></div>
</div>
<p>The above example shows the default output format. All
aspects of the output format should be configurable, so that
you could have output formatted like this:</p>
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>2002-04-19 07:56:58,174 MyModule DEBUG - Doin&#39; stuff...
or just
Doin&#39; stuff...
</pre></div>
</div>
</section>
<section id="control-flow">
<h2><a class="toc-backref" href="#control-flow" role="doc-backlink">Control Flow</a></h2>
<p>Applications make logging calls on <strong>Logger</strong> objects. Loggers are
organized in a hierarchical namespace and child Loggers inherit
some logging properties from their parents in the namespace.</p>
<p>Logger names fit into a “dotted name” namespace, with dots
(periods) indicating sub-namespaces. The namespace of logger
objects therefore corresponds to a single tree data structure.</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">&quot;&quot;</span></code> is the root of the namespace</li>
<li><code class="docutils literal notranslate"><span class="pre">&quot;Zope&quot;</span></code> would be a child node of the root</li>
<li><code class="docutils literal notranslate"><span class="pre">&quot;Zope.ZODB&quot;</span></code> would be a child node of <code class="docutils literal notranslate"><span class="pre">&quot;Zope&quot;</span></code></li>
</ul>
<p>These Logger objects create <strong>LogRecord</strong> objects which are passed
to <strong>Handler</strong> objects for output. Both Loggers and Handlers may
use logging <strong>levels</strong> and (optionally) <strong>Filters</strong> to decide if they
are interested in a particular LogRecord. When it is necessary to
output a LogRecord externally, a Handler can (optionally) use a
<strong>Formatter</strong> to localize and format the message before sending it
to an I/O stream.</p>
<p>Each Logger keeps track of a set of output Handlers. By default
all Loggers also send their output to all Handlers of their
ancestor Loggers. Loggers may, however, also be configured to
ignore Handlers higher up the tree.</p>
<p>The APIs are structured so that calls on the Logger APIs can be
cheap when logging is disabled. If logging is disabled for a
given log level, then the Logger can make a cheap comparison test
and return. If logging is enabled for a given log level, the
Logger is still careful to minimize costs before passing the
LogRecord into the Handlers. In particular, localization and
formatting (which are relatively expensive) are deferred until the
Handler requests them.</p>
<p>The overall Logger hierarchy can also have a level associated with
it, which takes precedence over the levels of individual Loggers.
This is done through a module-level function:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">disable</span><span class="p">(</span><span class="n">lvl</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Do not generate any LogRecords for requests with a severity less</span>
<span class="sd"> than &#39;lvl&#39;.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
<section id="levels">
<h2><a class="toc-backref" href="#levels" role="doc-backlink">Levels</a></h2>
<p>The logging levels, in increasing order of importance, are:</p>
<ul class="simple">
<li>DEBUG</li>
<li>INFO</li>
<li>WARN</li>
<li>ERROR</li>
<li>CRITICAL</li>
</ul>
<p>The term CRITICAL is used in preference to FATAL, which is used by
log4j. The levels are conceptually the same - that of a serious,
or very serious, error. However, FATAL implies death, which in
Python implies a raised and uncaught exception, traceback, and
exit. Since the logging module does not enforce such an outcome
from a FATAL-level log entry, it makes sense to use CRITICAL in
preference to FATAL.</p>
<p>These are just integer constants, to allow simple comparison of
importance. Experience has shown that too many levels can be
confusing, as they lead to subjective interpretation of which
level should be applied to any particular log request.</p>
<p>Although the above levels are strongly recommended, the logging
system should not be prescriptive. Users may define their own
levels, as well as the textual representation of any levels. User
defined levels must, however, obey the constraints that they are
all positive integers and that they increase in order of
increasing severity.</p>
<p>User-defined logging levels are supported through two module-level
functions:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getLevelName</span><span class="p">(</span><span class="n">lvl</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return the text for level &#39;lvl&#39;.&quot;&quot;&quot;</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">addLevelName</span><span class="p">(</span><span class="n">lvl</span><span class="p">,</span> <span class="n">lvlName</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Add the level &#39;lvl&#39; with associated text &#39;levelName&#39;, or</span>
<span class="sd"> set the textual representation of existing level &#39;lvl&#39; to be</span>
<span class="sd"> &#39;lvlName&#39;.&quot;&quot;&quot;</span>
<span class="o">...</span>
</pre></div>
</div>
</section>
<section id="loggers">
<h2><a class="toc-backref" href="#loggers" role="doc-backlink">Loggers</a></h2>
<p>Each Logger object keeps track of a log level (or threshold) that
it is interested in, and discards log requests below that level.</p>
<p>A <strong>Manager</strong> class instance maintains the hierarchical namespace of
named Logger objects. Generations are denoted with dot-separated
names: Logger “foo” is the parent of Loggers “foo.bar” and
“foo.baz”.</p>
<p>The Manager class instance is a singleton and is not directly
exposed to users, who interact with it using various module-level
functions.</p>
<p>The general logging method is:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Logger</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">log</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lvl</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Log &#39;str(msg) % args&#39; at logging level &#39;lvl&#39;.&quot;&quot;&quot;</span>
<span class="o">...</span>
</pre></div>
</div>
<p>However, convenience functions are defined for each logging level:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Logger</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">debug</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">info</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">warn</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">error</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
<span class="k">def</span> <span class="nf">critical</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Only one keyword argument is recognized at present - “exc_info”.
If true, the caller wants exception information to be provided in
the logging output. This mechanism is only needed if exception
information needs to be provided at <strong>any</strong> logging level. In the
more common case, where exception information needs to be added to
the log only when errors occur, i.e. at the ERROR level, then
another convenience method is provided:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Logger</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">exception</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>This should only be called in the context of an exception handler,
and is the preferred way of indicating a desire for exception
information in the log. The other convenience methods are
intended to be called with exc_info only in the unusual situation
where you might want to provide exception information in the
context of an INFO message, for example.</p>
<p>The “msg” argument shown above will normally be a format string;
however, it can be any object x for which <code class="docutils literal notranslate"><span class="pre">str(x)</span></code> returns the
format string. This facilitates, for example, the use of an
object which fetches a locale- specific message for an
internationalized/localized application, perhaps using the
standard gettext module. An outline example:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Message</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Represents a message&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="nb">id</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Initialize with the message ID&quot;&quot;&quot;</span>
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Return an appropriate localized message text&quot;&quot;&quot;</span>
<span class="o">...</span>
<span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="n">Message</span><span class="p">(</span><span class="s2">&quot;abc&quot;</span><span class="p">),</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</div>
<p>Gathering and formatting data for a log message may be expensive,
and a waste if the logger was going to discard the message anyway.
To see if a request will be honoured by the logger, the
<code class="docutils literal notranslate"><span class="pre">isEnabledFor()</span></code> method can be used:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Logger</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">isEnabledFor</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lvl</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return true if requests at level &#39;lvl&#39; will NOT be</span>
<span class="sd"> discarded.</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="o">...</span>
</pre></div>
</div>
<p>so instead of this expensive and possibly wasteful DOM to XML
conversion:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">...</span>
<span class="n">hamletStr</span> <span class="o">=</span> <span class="n">hamletDom</span><span class="o">.</span><span class="n">toxml</span><span class="p">()</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="n">hamletStr</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
</div>
<p>one can do this:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">log</span><span class="o">.</span><span class="n">isEnabledFor</span><span class="p">(</span><span class="n">logging</span><span class="o">.</span><span class="n">INFO</span><span class="p">):</span>
<span class="n">hamletStr</span> <span class="o">=</span> <span class="n">hamletDom</span><span class="o">.</span><span class="n">toxml</span><span class="p">()</span>
<span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="n">hamletStr</span><span class="p">)</span>
</pre></div>
</div>
<p>When new loggers are created, they are initialized with a level
which signifies “no level”. A level can be set explicitly using
the <code class="docutils literal notranslate"><span class="pre">setLevel()</span></code> method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Logger</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">setLevel</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lvl</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>If a loggers level is not set, the system consults all its
ancestors, walking up the hierarchy until an explicitly set level
is found. That is regarded as the “effective level” of the
logger, and can be queried via the getEffectiveLevel() method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getEffectiveLevel</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Loggers are never instantiated directly. Instead, a module-level
function is used:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">getLogger</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>If no name is specified, the root logger is returned. Otherwise,
if a logger with that name exists, it is returned. If not, a new
logger is initialized and returned. Here, “name” is synonymous
with “channel name”.</p>
<p>Users can specify a custom subclass of Logger to be used by the
system when instantiating new loggers:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">setLoggerClass</span><span class="p">(</span><span class="n">klass</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>The passed class should be a subclass of Logger, and its <code class="docutils literal notranslate"><span class="pre">__init__</span></code>
method should call <code class="docutils literal notranslate"><span class="pre">Logger.__init__</span></code>.</p>
</section>
<section id="handlers">
<h2><a class="toc-backref" href="#handlers" role="doc-backlink">Handlers</a></h2>
<p>Handlers are responsible for doing something useful with a given
<code class="docutils literal notranslate"><span class="pre">LogRecord</span></code>. The following core Handlers will be implemented:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">StreamHandler</span></code>: A handler for writing to a file-like object.</li>
<li><code class="docutils literal notranslate"><span class="pre">FileHandler</span></code>: A handler for writing to a single file or set
of rotating files.</li>
<li><code class="docutils literal notranslate"><span class="pre">SocketHandler</span></code>: A handler for writing to remote TCP ports.</li>
<li><code class="docutils literal notranslate"><span class="pre">DatagramHandler</span></code>: A handler for writing to UDP sockets, for
low-cost logging. Jeff Bauer already had such a system <a class="footnote-reference brackets" href="#id15" id="id5">[5]</a>.</li>
<li><code class="docutils literal notranslate"><span class="pre">MemoryHandler</span></code>: A handler that buffers log records in memory
until the buffer is full or a particular condition occurs
<a class="footnote-reference brackets" href="#id11" id="id6">[1]</a>.</li>
<li><code class="docutils literal notranslate"><span class="pre">SMTPHandler</span></code>: A handler for sending to email addresses via SMTP.</li>
<li><code class="docutils literal notranslate"><span class="pre">SysLogHandler</span></code>: A handler for writing to Unix syslog via UDP.</li>
<li><code class="docutils literal notranslate"><span class="pre">NTEventLogHandler</span></code>: A handler for writing to event logs on
Windows NT, 2000 and XP.</li>
<li><code class="docutils literal notranslate"><span class="pre">HTTPHandler</span></code>: A handler for writing to a Web server with
either GET or POST semantics.</li>
</ul>
<p>Handlers can also have levels set for them using the
<code class="docutils literal notranslate"><span class="pre">setLevel()</span></code> method:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">setLevel</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lvl</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>The FileHandler can be set up to create a rotating set of log
files. In this case, the file name passed to the constructor is
taken as a “base” file name. Additional file names for the
rotation are created by appending .1, .2, etc. to the base file
name, up to a maximum as specified when rollover is requested.
The setRollover method is used to specify a maximum size for a log
file and a maximum number of backup files in the rotation.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">setRollover</span><span class="p">(</span><span class="n">maxBytes</span><span class="p">,</span> <span class="n">backupCount</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>If maxBytes is specified as zero, no rollover ever occurs and the
log file grows indefinitely. If a non-zero size is specified,
when that size is about to be exceeded, rollover occurs. The
rollover method ensures that the base file name is always the most
recent, .1 is the next most recent, .2 the next most recent after
that, and so on.</p>
<p>There are many additional handlers implemented in the test/example
scripts provided with <a class="footnote-reference brackets" href="#id16" id="id7">[6]</a> - for example, XMLHandler and
SOAPHandler.</p>
</section>
<section id="logrecords">
<h2><a class="toc-backref" href="#logrecords" role="doc-backlink">LogRecords</a></h2>
<p>A LogRecord acts as a receptacle for information about a
logging event. It is little more than a dictionary, though it
does define a <code class="docutils literal notranslate"><span class="pre">getMessage</span></code> method which merges a message with
optional runarguments.</p>
</section>
<section id="formatters">
<h2><a class="toc-backref" href="#formatters" role="doc-backlink">Formatters</a></h2>
<p>A Formatter is responsible for converting a LogRecord to a string
representation. A Handler may call its Formatter before writing a
record. The following core Formatters will be implemented:</p>
<ul class="simple">
<li><code class="docutils literal notranslate"><span class="pre">Formatter</span></code>: Provide printf-like formatting, using the % operator.</li>
<li><code class="docutils literal notranslate"><span class="pre">BufferingFormatter</span></code>: Provide formatting for multiple
messages, with header and trailer formatting support.</li>
</ul>
<p>Formatters are associated with Handlers by calling <code class="docutils literal notranslate"><span class="pre">setFormatter()</span></code>
on a handler:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">setFormatter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">form</span><span class="p">):</span> <span class="o">...</span>
</pre></div>
</div>
<p>Formatters use the % operator to format the logging message. The
format string should contain <code class="docutils literal notranslate"><span class="pre">%(name)x</span></code> and the attribute dictionary
of the LogRecord is used to obtain message-specific data. The
following attributes are provided:</p>
<table class="docutils align-default">
<tbody>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(name)s</span></code></td>
<td>Name of the logger (logging channel)</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(levelno)s</span></code></td>
<td>Numeric logging level for the message (DEBUG,
INFO, WARN, ERROR, CRITICAL)</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(levelname)s</span></code></td>
<td>Text logging level for the message (“DEBUG”, “INFO”,
“WARN”, “ERROR”, “CRITICAL”)</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(pathname)s</span></code></td>
<td>Full pathname of the source file where the logging
call was issued (if available)</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(filename)s</span></code></td>
<td>Filename portion of pathname</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(module)s</span></code></td>
<td>Module from which logging call was made</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(lineno)d</span></code></td>
<td>Source line number where the logging call was issued
(if available)</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(created)f</span></code></td>
<td>Time when the LogRecord was created (<code class="docutils literal notranslate"><span class="pre">time.time()</span></code>
return value)</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(asctime)s</span></code></td>
<td>Textual time when the LogRecord was created</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(msecs)d</span></code></td>
<td>Millisecond portion of the creation time</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(relativeCreated)d</span></code></td>
<td>Time in milliseconds when the LogRecord was created,
relative to the time the logging module was loaded
(typically at application startup time)</td>
</tr>
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">%(thread)d</span></code></td>
<td>Thread ID (if available)</td>
</tr>
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">%(message)s</span></code></td>
<td>The result of record.getMessage(), computed just as
the record is emitted</td>
</tr>
</tbody>
</table>
<p>If a formatter sees that the format string includes “(asctime)s”,
the creation time is formatted into the LogRecords asctime
attribute. To allow flexibility in formatting dates, Formatters
are initialized with a format string for the message as a whole,
and a separate format string for date/time. The date/time format
string should be in time.strftime format. The default value for
the message format is “%(message)s”. The default date/time format
is ISO8601.</p>
<p>The formatter uses a class attribute, “converter”, to indicate how
to convert a time from seconds to a tuple. By default, the value
of “converter” is “time.localtime”. If needed, a different
converter (e.g. “time.gmtime”) can be set on an individual
formatter instance, or the class attribute changed to affect all
formatter instances.</p>
</section>
<section id="filters">
<h2><a class="toc-backref" href="#filters" role="doc-backlink">Filters</a></h2>
<p>When level-based filtering is insufficient, a Filter can be called
by a Logger or Handler to decide if a LogRecord should be output.
Loggers and Handlers can have multiple filters installed, and any
one of them can veto a LogRecord being output.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Filter</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">filter</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">record</span><span class="p">):</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Return a value indicating true if the record is to be</span>
<span class="sd"> processed. Possibly modify the record, if deemed</span>
<span class="sd"> appropriate by the filter.</span>
<span class="sd"> &quot;&quot;&quot;</span>
</pre></div>
</div>
<p>The default behaviour allows a Filter to be initialized with a
Logger name. This will only allow through events which are
generated using the named logger or any of its children. For
example, a filter initialized with “A.B” will allow events logged
by loggers “A.B”, “A.B.C”, “A.B.C.D”, “A.B.D” etc. but not “A.BB”,
“B.A.B” etc. If initialized with the empty string, all events are
passed by the Filter. This filter behaviour is useful when it is
desired to focus attention on one particular area of an
application; the focus can be changed simply by changing a filter
attached to the root logger.</p>
<p>There are many examples of Filters provided in <a class="footnote-reference brackets" href="#id16" id="id8">[6]</a>.</p>
</section>
<section id="configuration">
<h2><a class="toc-backref" href="#configuration" role="doc-backlink">Configuration</a></h2>
<p>The main benefit of a logging system like this is that one can
control how much and what logging output one gets from an
application without changing that applications source code.
Therefore, although configuration can be performed through the
logging API, it must also be possible to change the logging
configuration without changing an application at all. For
long-running programs like Zope, it should be possible to change
the logging configuration while the program is running.</p>
<p>Configuration includes the following:</p>
<ul class="simple">
<li>What logging level a logger or handler should be interested in.</li>
<li>What handlers should be attached to which loggers.</li>
<li>What filters should be attached to which handlers and loggers.</li>
<li>Specifying attributes specific to certain handlers and filters.</li>
</ul>
<p>In general each application will have its own requirements for how
a user may configure logging output. However, each application
will specify the required configuration to the logging system
through a standard mechanism.</p>
<p>The most simple configuration is that of a single handler, writing
to stderr, attached to the root logger. This configuration is set
up by calling the <code class="docutils literal notranslate"><span class="pre">basicConfig()</span></code> function once the logging module
has been imported.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">basicConfig</span><span class="p">():</span> <span class="o">...</span>
</pre></div>
</div>
<p>For more sophisticated configurations, this PEP makes no specific
proposals, for the following reasons:</p>
<ul class="simple">
<li>A specific proposal may be seen as prescriptive.</li>
<li>Without the benefit of wide practical experience in the
Python community, there is no way to know whether any given
configuration approach is a good one. That practice cant
really come until the logging module is used, and that means
until <strong>after</strong> Python 2.3 has shipped.</li>
<li>There is a likelihood that different types of applications
may require different configuration approaches, so that no
“one size fits all”.</li>
</ul>
<p>The reference implementation <a class="footnote-reference brackets" href="#id16" id="id9">[6]</a> has a working configuration file
format, implemented for the purpose of proving the concept and
suggesting one possible alternative. It may be that separate
extension modules, not part of the core Python distribution, are
created for logging configuration and log viewing, supplemental
handlers and other features which are not of interest to the bulk
of the community.</p>
</section>
<section id="thread-safety">
<h2><a class="toc-backref" href="#thread-safety" role="doc-backlink">Thread Safety</a></h2>
<p>The logging system should support thread-safe operation without
any special action needing to be taken by its users.</p>
</section>
<section id="module-level-functions">
<h2><a class="toc-backref" href="#module-level-functions" role="doc-backlink">Module-Level Functions</a></h2>
<p>To support use of the logging mechanism in short scripts and small
applications, module-level functions <code class="docutils literal notranslate"><span class="pre">debug()</span></code>, <code class="docutils literal notranslate"><span class="pre">info()</span></code>, <code class="docutils literal notranslate"><span class="pre">warn()</span></code>,
<code class="docutils literal notranslate"><span class="pre">error()</span></code>, <code class="docutils literal notranslate"><span class="pre">critical()</span></code> and <code class="docutils literal notranslate"><span class="pre">exception()</span></code> are provided. These work in
the same way as the correspondingly named methods of Logger - in
fact they delegate to the corresponding methods on the root
logger. A further convenience provided by these functions is that
if no configuration has been done, <code class="docutils literal notranslate"><span class="pre">basicConfig()</span></code> is automatically
called.</p>
<p>At application exit, all handlers can be flushed by calling the function:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">shutdown</span><span class="p">():</span> <span class="o">...</span>
</pre></div>
</div>
<p>This will flush and close all handlers.</p>
</section>
<section id="implementation">
<h2><a class="toc-backref" href="#implementation" role="doc-backlink">Implementation</a></h2>
<p>The reference implementation is Vinay Sajips logging module <a class="footnote-reference brackets" href="#id16" id="id10">[6]</a>.</p>
</section>
<section id="packaging">
<h2><a class="toc-backref" href="#packaging" role="doc-backlink">Packaging</a></h2>
<p>The reference implementation is implemented as a single module.
This offers the simplest interface - all users have to do is
“import logging” and they are in a position to use all the
functionality available.</p>
</section>
<section id="references">
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
<aside class="footnote-list brackets">
<aside class="footnote brackets" id="id11" role="doc-footnote">
<dt class="label" id="id11">[1]<em> (<a href='#id1'>1</a>, <a href='#id6'>2</a>) </em></dt>
<dd>java.util.logging
<a class="reference external" href="http://java.sun.com/j2se/1.4/docs/guide/util/logging/">http://java.sun.com/j2se/1.4/docs/guide/util/logging/</a></aside>
<aside class="footnote brackets" id="id12" role="doc-footnote">
<dt class="label" id="id12">[<a href="#id2">2</a>]</dt>
<dd>log4j: a Java logging package
<a class="reference external" href="https://logging.apache.org/log4j/">https://logging.apache.org/log4j/</a></aside>
<aside class="footnote brackets" id="id13" role="doc-footnote">
<dt class="label" id="id13">[<a href="#id3">3</a>]</dt>
<dd>Protomatters Syslog
<a class="reference external" href="http://protomatter.sourceforge.net/1.1.6/index.html">http://protomatter.sourceforge.net/1.1.6/index.html</a>
<a class="reference external" href="http://protomatter.sourceforge.net/1.1.6/javadoc/com/protomatter/syslog/syslog-whitepaper.html">http://protomatter.sourceforge.net/1.1.6/javadoc/com/protomatter/syslog/syslog-whitepaper.html</a></aside>
<aside class="footnote brackets" id="id14" role="doc-footnote">
<dt class="label" id="id14">[<a href="#id4">4</a>]</dt>
<dd>MAL mentions his mx.Log logging module:
<a class="reference external" href="https://mail.python.org/pipermail/python-dev/2002-February/019767.html">https://mail.python.org/pipermail/python-dev/2002-February/019767.html</a></aside>
<aside class="footnote brackets" id="id15" role="doc-footnote">
<dt class="label" id="id15">[<a href="#id5">5</a>]</dt>
<dd>Jeff Bauers Mr. Creosote
<a class="reference external" href="http://starship.python.net/crew/jbauer/creosote/">http://starship.python.net/crew/jbauer/creosote/</a></aside>
<aside class="footnote brackets" id="id16" role="doc-footnote">
<dt class="label" id="id16">[6]<em> (<a href='#id7'>1</a>, <a href='#id8'>2</a>, <a href='#id9'>3</a>, <a href='#id10'>4</a>) </em></dt>
<dd>Vinay Sajips logging module.
<a class="reference external" href="https://old.red-dove.com/python_logging.html">https://old.red-dove.com/python_logging.html</a></aside>
</aside>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0282.rst">https://github.com/python/peps/blob/main/peps/pep-0282.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0282.rst">2023-10-10 05:32:07 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#motivation">Motivation</a></li>
<li><a class="reference internal" href="#influences">Influences</a></li>
<li><a class="reference internal" href="#simple-example">Simple Example</a></li>
<li><a class="reference internal" href="#control-flow">Control Flow</a></li>
<li><a class="reference internal" href="#levels">Levels</a></li>
<li><a class="reference internal" href="#loggers">Loggers</a></li>
<li><a class="reference internal" href="#handlers">Handlers</a></li>
<li><a class="reference internal" href="#logrecords">LogRecords</a></li>
<li><a class="reference internal" href="#formatters">Formatters</a></li>
<li><a class="reference internal" href="#filters">Filters</a></li>
<li><a class="reference internal" href="#configuration">Configuration</a></li>
<li><a class="reference internal" href="#thread-safety">Thread Safety</a></li>
<li><a class="reference internal" href="#module-level-functions">Module-Level Functions</a></li>
<li><a class="reference internal" href="#implementation">Implementation</a></li>
<li><a class="reference internal" href="#packaging">Packaging</a></li>
<li><a class="reference internal" href="#references">References</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0282.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>