Merge lp:~elopio/snapcraft/log_handler into lp:~snappy-dev/snapcraft/core

Proposed by Leo Arias
Status: Merged
Approved by: Michael Terry
Approved revision: 115
Merged at revision: 116
Proposed branch: lp:~elopio/snapcraft/log_handler
Merge into: lp:~snappy-dev/snapcraft/core
Diff against target: 174 lines (+140/-8)
3 files modified
snapcraft/log.py (+52/-0)
snapcraft/main.py (+2/-8)
snapcraft/tests/test_log.py (+86/-0)
To merge this branch: bzr merge lp:~elopio/snapcraft/log_handler
Reviewer Review Type Date Requested Status
Michael Terry (community) Approve
Review via email: mp+266424@code.launchpad.net

Commit message

Send error logs to stderr and message logs to stdout.

Description of the change

On the original logging prints, some messages went to stdout and some to stderr. That was not consistent, nor tested. So this branch adds two handlers to recover that behavoiur, but cleaner.

To post a comment you must log in.
lp:~elopio/snapcraft/log_handler updated
115. By Leo Arias

Added the test file.

Revision history for this message
Michael Terry (mterry) wrote :

LGTM, thanks Leo! And nice that we have a separate log module for any future additions.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'snapcraft/log.py'
2--- snapcraft/log.py 1970-01-01 00:00:00 +0000
3+++ snapcraft/log.py 2015-07-30 14:52:01 +0000
4@@ -0,0 +1,52 @@
5+#!/usr/bin/python3
6+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
7+#
8+# Copyright (C) 2015 Canonical Ltd
9+#
10+# This program is free software: you can redistribute it and/or modify
11+# it under the terms of the GNU General Public License version 3 as
12+# published by the Free Software Foundation.
13+#
14+# This program is distributed in the hope that it will be useful,
15+# but WITHOUT ANY WARRANTY; without even the implied warranty of
16+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+# GNU General Public License for more details.
18+#
19+# You should have received a copy of the GNU General Public License
20+# along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
22+import logging
23+import sys
24+
25+
26+_COLOR_BOLD = '\033[1m'
27+_COLOR_END = '\033[0m'
28+
29+
30+class _StdoutFilter(logging.Filter):
31+
32+ def filter(self, record):
33+ return record.levelno <= logging.WARNING
34+
35+
36+class _StderrFilter(logging.Filter):
37+
38+ def filter(self, record):
39+ return record.levelno >= logging.ERROR
40+
41+
42+def configure(logger_name=None):
43+ stdout_handler = logging.StreamHandler(stream=sys.stdout)
44+ stdout_handler.addFilter(_StdoutFilter())
45+ stderr_handler = logging.StreamHandler(stream=sys.stderr)
46+ stderr_handler.addFilter(_StderrFilter())
47+ handlers = [stdout_handler, stderr_handler]
48+
49+ formatter = logging.Formatter(
50+ _COLOR_BOLD + '{msg}' + _COLOR_END, style='{')
51+ logger = logging.getLogger(logger_name)
52+ for handler in handlers:
53+ handler.setFormatter(formatter)
54+ logger.addHandler(handler)
55+
56+ logger.setLevel(logging.INFO)
57
58=== modified file 'snapcraft/main.py'
59--- snapcraft/main.py 2015-07-23 14:32:16 +0000
60+++ snapcraft/main.py 2015-07-30 14:52:01 +0000
61@@ -16,20 +16,14 @@
62 # along with this program. If not, see <http://www.gnu.org/licenses/>.
63
64 import argparse
65-import logging
66 import sys
67
68 import snapcraft.cmds
69-
70-
71-_COLOR_BOLD = '\033[1m'
72-_COLOR_END = '\033[0m'
73+from snapcraft import log
74
75
76 def main():
77- logging.basicConfig(
78- format=_COLOR_BOLD + '%(message)s' + _COLOR_END, level=logging.INFO)
79-
80+ log.configure()
81 root_parser = argparse.ArgumentParser()
82 subparsers = root_parser.add_subparsers(dest='cmd')
83
84
85=== added file 'snapcraft/tests/test_log.py'
86--- snapcraft/tests/test_log.py 1970-01-01 00:00:00 +0000
87+++ snapcraft/tests/test_log.py 2015-07-30 14:52:01 +0000
88@@ -0,0 +1,86 @@
89+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-
90+#
91+# Copyright (C) 2015 Canonical Ltd
92+#
93+# This program is free software: you can redistribute it and/or modify
94+# it under the terms of the GNU General Public License version 3 as
95+# published by the Free Software Foundation.
96+#
97+# This program is distributed in the hope that it will be useful,
98+# but WITHOUT ANY WARRANTY; without even the implied warranty of
99+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
100+# GNU General Public License for more details.
101+#
102+# You should have received a copy of the GNU General Public License
103+# along with this program. If not, see <http://www.gnu.org/licenses/>.
104+
105+import io
106+import logging
107+from unittest import mock
108+
109+from snapcraft import (
110+ log,
111+ tests
112+)
113+
114+
115+@mock.patch('sys.stdout', new_callable=io.StringIO)
116+@mock.patch('sys.stderr', new_callable=io.StringIO)
117+class LogTestCase(tests.TestCase):
118+
119+ def test_configure_must_send_messages_to_stdout(
120+ self, mock_stderr, mock_stdout):
121+ logger_name = self.id()
122+ log.configure(logger_name)
123+ logger = logging.getLogger(logger_name)
124+ # Overwrite the level to log everything.
125+ logger.setLevel(logging.DEBUG)
126+
127+ logger.debug('Test debug')
128+ logger.info('Test info')
129+ logger.warning('Test warning')
130+
131+ expected_out = (
132+ '\033[1mTest debug\033[0m\n'
133+ '\033[1mTest info\033[0m\n'
134+ '\033[1mTest warning\033[0m\n')
135+ self.assertEqual(expected_out, mock_stdout.getvalue())
136+ self.assertEqual('', mock_stderr.getvalue())
137+
138+ def test_configure_must_send_errors_to_stderr(
139+ self, mock_stderr, mock_stdout):
140+ logger_name = self.id()
141+ log.configure(logger_name)
142+ logger = logging.getLogger(logger_name)
143+ # Overwrite the level to log everything.
144+ logger.setLevel(logging.DEBUG)
145+
146+ logger.error('Test error')
147+ logger.critical('Test critical')
148+
149+ expected_err = (
150+ '\033[1mTest error\033[0m\n'
151+ '\033[1mTest critical\033[0m\n')
152+ self.assertEqual(expected_err, mock_stderr.getvalue())
153+ self.assertEqual('', mock_stdout.getvalue())
154+
155+ def test_configure_must_log_info_and_higher(
156+ self, mock_stderr, mock_stdout):
157+ logger_name = self.id()
158+ log.configure(logger_name)
159+ logger = logging.getLogger(logger_name)
160+
161+ logger.debug('Test debug')
162+ logger.info('Test info')
163+ logger.warning('Test warning')
164+ logger.error('Test error')
165+ logger.critical('Test critical')
166+
167+ expected_out = (
168+ '\033[1mTest info\033[0m\n'
169+ '\033[1mTest warning\033[0m\n')
170+ expected_err = (
171+ '\033[1mTest error\033[0m\n'
172+ '\033[1mTest critical\033[0m\n')
173+ self.assertEqual(expected_out, mock_stdout.getvalue())
174+ self.assertEqual(expected_err, mock_stderr.getvalue())

Subscribers

People subscribed via source and target branches

to all changes: