Skip to content

Commit 06745f8

Browse files
committed
Make it even easier to integrate with cron
1 parent eac38f0 commit 06745f8

File tree

5 files changed

+106
-19
lines changed

5 files changed

+106
-19
lines changed

README.rst

+38-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ defined by my verboselogs_ package: if you install both `coloredlogs` and
2525

2626
.. contents::
2727
:local:
28+
:depth: 1
2829

2930
Format of log messages
3031
----------------------
@@ -80,24 +81,49 @@ Colored output from cron
8081
When `coloredlogs` is used in a cron_ job, the output that's e-mailed to you by
8182
cron won't contain any ANSI escape sequences because `coloredlogs` realizes
8283
that it's not attached to an interactive terminal. If you'd like to have colors
83-
e-mailed to you by cron there's a simple way to set it up::
84+
e-mailed to you by cron there are two ways to make it happen:
85+
86+
.. contents::
87+
:local:
88+
89+
You can use this feature without using `coloredlogs` in your Python modules,
90+
but please note that only normal text, bold text and text with one of the
91+
foreground colors black, red, green, yellow, blue, magenta, cyan and white
92+
(these are the portable ANSI color codes) are supported.
93+
94+
Modifying your crontab
95+
~~~~~~~~~~~~~~~~~~~~~~
96+
97+
Here's an example of a minimal crontab::
8498

8599
MAILTO="your-email-address@here"
86100
CONTENT_TYPE="text/html"
87101
* * * * * root coloredlogs --to-html your-command
88102

89103
The ``coloredlogs`` program is installed when you install the `coloredlogs`
90-
package. When you execute ``coloredlogs --to-html your-command`` it runs
104+
Python package. When you execute ``coloredlogs --to-html your-command`` it runs
91105
``your-command`` under the external program ``script`` (you need to have this
92106
installed). This makes ``your-command`` think that it's attached to an
93107
interactive terminal which means it will output ANSI escape sequences which
94108
will then be converted to HTML by the ``coloredlogs`` program. Yes, this is a
95109
bit convoluted, but it works great :-)
96110

97-
You can use this feature without using `coloredlogs` in your Python modules,
98-
but please note that only normal text, bold text and text with one of the
99-
foreground colors black, red, green, yellow, blue, magenta, cyan and white
100-
(these are the portable ANSI color codes) are supported.
111+
Modifying your Python code
112+
~~~~~~~~~~~~~~~~~~~~~~~~~~
113+
114+
The ColoredCronMailer_ class provides a context manager that automatically
115+
enables HTML output when the ``$CONTENT_TYPE`` variable has been correctly set
116+
in the crontab.
117+
118+
This requires my capturer_ package which you can install using ``pip install
119+
'coloredlogs[cron]'``. The ``[cron]`` extra will pull in capturer_ 2.4 or newer
120+
which is required to capture the output while silencing it - otherwise you'd
121+
get duplicate output in the emails sent by ``cron``.
122+
123+
The context manager can also be used to retroactively silence output that has
124+
already been produced, this can be useful to avoid spammy cron jobs that have
125+
nothing useful to do but still email their output to the system administrator
126+
every few minutes :-).
101127

102128
Contact
103129
-------
@@ -116,14 +142,16 @@ This software is licensed under the `MIT license`_.
116142

117143

118144
.. External references:
119-
.. _ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
145+
.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
146+
.. _capturer: https://pypi.python.org/pypi/capturer
120147
.. _Colorama: https://pypi.python.org/pypi/colorama
121-
.. _ColoredFormatter: http://coloredlogs.readthedocs.io/en/latest/#coloredlogs.ColoredFormatter
148+
.. _ColoredCronMailer: https://coloredlogs.readthedocs.io/en/latest/#coloredlogs.converter.ColoredCronMailer
149+
.. _ColoredFormatter: https://coloredlogs.readthedocs.io/en/latest/#coloredlogs.ColoredFormatter
122150
.. _cron: https://en.wikipedia.org/wiki/Cron
123151
.. _GitHub: https://github.com/xolox/python-coloredlogs
124-
.. _logging.Formatter: http://docs.python.org/2/library/logging.html#logging.Formatter
152+
.. _logging.Formatter: https://docs.python.org/2/library/logging.html#logging.Formatter
125153
.. _logging: https://docs.python.org/2/library/logging.html
126-
.. _MIT license: http://en.wikipedia.org/wiki/MIT_License
154+
.. _MIT license: https://en.wikipedia.org/wiki/MIT_License
127155
.. _online documentation: https://coloredlogs.readthedocs.io/
128156
129157
.. _PyPI: https://pypi.python.org/pypi/coloredlogs

coloredlogs/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Colored terminal output for Python's logging module.
22
#
33
# Author: Peter Odding <[email protected]>
4-
# Last Change: May 17, 2017
4+
# Last Change: May 18, 2017
55
# URL: https://coloredlogs.readthedocs.io
66

77
"""
@@ -25,7 +25,7 @@
2525
The :mod:`~coloredlogs.install()` function creates a :class:`ColoredFormatter`
2626
that injects `ANSI escape sequences`_ into the log output.
2727
28-
.. _ANSI escape sequences: http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
28+
.. _ANSI escape sequences: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
2929
3030
Environment variables
3131
=====================

coloredlogs/converter.py

+60-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Program to convert text with ANSI escape sequences to HTML.
22
#
33
# Author: Peter Odding <[email protected]>
4-
# Last Change: May 17, 2017
4+
# Last Change: May 18, 2017
55
# URL: https://coloredlogs.readthedocs.io
66

77
"""Convert text with ANSI escape sequences to HTML."""
@@ -15,7 +15,7 @@
1515
import tempfile
1616

1717
# External dependencies.
18-
from humanfriendly.terminal import ANSI_CSI, clean_terminal_output
18+
from humanfriendly.terminal import ANSI_CSI, clean_terminal_output, output
1919

2020
# Portable color codes from http://en.wikipedia.org/wiki/ANSI_escape_code#Colors.
2121
EIGHT_COLOR_PALETTE = (
@@ -198,3 +198,61 @@ def html_encode(text):
198198
text = text.replace('>', '&gt;')
199199
text = text.replace('"', '&quot;')
200200
return text
201+
202+
203+
class ColoredCronMailer(object):
204+
205+
"""
206+
Easy to use integration between :mod:`coloredlogs` and the UNIX ``cron`` daemon.
207+
208+
By using :class:`ColoredCronMailer` as a context manager in the command
209+
line interface of your Python program you make it trivially easy for users
210+
of your program to opt in to HTML output under ``cron``: The only thing the
211+
user needs to do is set ``CONTENT_TYPE="text/html"`` in their crontab!
212+
213+
Under the hood this requires quite a bit of magic and I must admit that I
214+
developed this code simply because I was curious whether it could even be
215+
done :-). It requires my :mod:`capturer` package which you can install
216+
using ``pip install 'coloredlogs[cron]'``. The ``[cron]`` extra will pull
217+
in the :mod:`capturer` 2.4 or newer which is required to capture the output
218+
while silencing it - otherwise you'd get duplicate output in the emails
219+
sent by ``cron``.
220+
"""
221+
222+
def __init__(self):
223+
"""Initialize output capturing when running under ``cron`` with the correct configuration."""
224+
self.is_enabled = 'text/html' in os.environ.get('CONTENT_TYPE', 'text/plain')
225+
self.is_silent = False
226+
if self.is_enabled:
227+
# We import capturer here so that the coloredlogs[cron] extra
228+
# isn't required to use the other functions in this module.
229+
from capturer import CaptureOutput
230+
self.capturer = CaptureOutput(merged=True, relay=False)
231+
232+
def __enter__(self):
233+
"""Start capturing output (when applicable)."""
234+
if self.is_enabled:
235+
self.capturer.__enter__()
236+
return self
237+
238+
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
239+
"""Stop capturing output and convert the output to HTML (when applicable)."""
240+
if self.is_enabled:
241+
if not self.is_silent:
242+
# Only call output() when we captured something useful.
243+
text = self.capturer.get_text()
244+
if text and not text.isspace():
245+
output(convert(text))
246+
self.capturer.__exit__(exc_type, exc_value, traceback)
247+
248+
def silence(self):
249+
"""
250+
Tell :func:`__exit__()` to swallow all output (things will be silent).
251+
252+
This can be useful when a Python program is written in such a way that
253+
it has already produced output by the time it becomes apparent that
254+
nothing useful can be done (say in a cron job that runs every few
255+
minutes :-p). By calling :func:`silence()` the output can be swallowed
256+
retroactively, avoiding useless emails from ``cron``.
257+
"""
258+
self.is_silent = True

docs/conf.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Colored terminal output for Python's logging module.
22
#
33
# Author: Peter Odding <[email protected]>
4-
# Last Change: March 10, 2017
4+
# Last Change: May 18, 2017
55
# URL: https://coloredlogs.readthedocs.io
66

77
"""Sphinx documentation configuration for the `coloredlogs` package."""
@@ -69,14 +69,15 @@
6969
# From: http://twistedmatrix.com/trac/ticket/4582.
7070
intersphinx_mapping = dict(
7171
python=('https://docs.python.org/2', None),
72+
capturer=('https://capturer.readthedocs.io/en/latest', None),
7273
humanfriendly=('https://humanfriendly.readthedocs.io/en/latest', None),
7374
)
7475

7576
# -- Options for HTML output ---------------------------------------------------
7677

7778
# The theme to use for HTML and HTML Help pages. See the documentation for
7879
# a list of builtin themes.
79-
html_theme = 'classic'
80+
html_theme = 'nature'
8081

8182
# Output file base name for HTML help builder.
8283
htmlhelp_basename = 'coloredlogsdoc'

setup.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env python
22

33
# Setup script for the `coloredlogs' package.
4-
4+
#
55
# Author: Peter Odding <[email protected]>
6-
# Last Change: April 17, 2017
6+
# Last Change: May 18, 2017
77
# URL: https://coloredlogs.readthedocs.io
88

99
"""
@@ -54,7 +54,7 @@ def get_install_requires():
5454

5555
def get_extras_require():
5656
"""Add conditional dependencies for Windows (when creating wheel distributions)."""
57-
extras_require = {}
57+
extras_require = dict(cron='capturer>=2.4')
5858
if have_environment_marker_support():
5959
expression = ':sys_platform == "win32"'
6060
extras_require[expression] = 'colorama'

0 commit comments

Comments
 (0)