source: rtems-tools/rtemstoolkit/log.py @ 0c0b2d4

4.10
Last change on this file since 0c0b2d4 was 2de37f3, checked in by Chris Johns <chrisj@…>, on Mar 9, 2016 at 3:27:42 AM

Python 2 and python 3 refactor fixes.

Updates #2619.

  • Property mode set to 100755
File size: 7.0 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2010-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-testing'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31#
32# Log output to stdout and/or a file.
33#
34
35from __future__ import print_function
36
37import os
38import sys
39import threading
40
41#
42# Support to handle use in a package and as a unit test.
43# If there is a better way to let us know.
44#
45try:
46    from . import error
47except (ValueError, SystemError):
48    import error
49
50#
51# A global log.
52#
53default = None
54
55#
56# Global parameters.
57#
58tracing = False
59quiet = False
60
61#
62# Global output lock to keep output together when working with threads
63#
64lock = threading.Lock()
65
66def set_default_once(log):
67    if default is None:
68        default = log
69
70def _output(text = os.linesep, log = None):
71    """Output the text to a log if provided else send it to stdout."""
72    if text is None:
73        text = os.linesep
74    if type(text) is list:
75        _text = ''
76        for l in text:
77            _text += l + os.linesep
78        text = _text
79    if log:
80        log.output(text)
81    elif default is not None:
82        default.output(text)
83    else:
84        lock.acquire()
85        for l in text.replace(chr(13), '').splitlines():
86            print(l)
87        lock.release()
88
89def stderr(text = os.linesep, log = None):
90    lock.acquire()
91    for l in text.replace(chr(13), '').splitlines():
92        print(l, file = sys.stderr)
93    lock.release()
94
95def output(text = os.linesep, log = None):
96    if not quiet:
97        _output(text, log)
98
99def notice(text = os.linesep, log = None, stdout_only = False):
100    if not quiet and \
101            (default is not None and not default.has_stdout() or stdout_only):
102        lock.acquire()
103        for l in text.replace(chr(13), '').splitlines():
104            print(l)
105        lock.release()
106    if not stdout_only:
107        _output(text, log)
108
109def trace(text = os.linesep, log = None):
110    if tracing:
111        _output(text, log)
112
113def warning(text = os.linesep, log = None):
114    for l in text.replace(chr(13), '').splitlines():
115        _output('warning: %s' % (l), log)
116
117def flush(log = None):
118    if log:
119        log.flush()
120    elif default is not None:
121        default.flush()
122
123class log:
124    """Log output to stdout or a file."""
125    def __init__(self, streams = None, tail_size = 100):
126        self.lock = threading.Lock()
127        self.tail = []
128        self.tail_size = tail_size
129        self.fhs = [None, None]
130        if streams:
131            for s in streams:
132                if s == 'stdout':
133                    self.fhs[0] = sys.stdout
134                elif s == 'stderr':
135                    self.fhs[1] = sys.stderr
136                else:
137                    try:
138                        self.fhs.append(open(s, 'w'))
139                    except IOError as ioe:
140                         raise error.general("creating log file '" + s + \
141                                             "': " + str(ioe))
142
143    def __del__(self):
144        for f in range(2, len(self.fhs)):
145            self.fhs[f].close()
146
147    def __str__(self):
148        t = ''
149        for tl in self.tail:
150            t += tl + os.linesep
151        return t[:-len(os.linesep)]
152
153    def _tail(self, text):
154        if type(text) is not list:
155            text = text.splitlines()
156        self.tail += text
157        if len(self.tail) > self.tail_size:
158            self.tail = self.tail[-self.tail_size:]
159
160    def has_stdout(self):
161        return self.fhs[0] is not None
162
163    def has_stderr(self):
164        return self.fhs[1] is not None
165
166    def output(self, text):
167        """Output the text message to all the logs."""
168        # Reformat the text to have local line types.
169        text = text.replace(chr(13), '').splitlines()
170        self._tail(text)
171        out = ''
172        for l in text:
173            out += l + os.linesep
174        self.lock.acquire()
175        try:
176            for f in range(0, len(self.fhs)):
177                if self.fhs[f] is not None:
178                    self.fhs[f].write(out)
179            self.flush()
180        except:
181            raise
182        finally:
183            self.lock.release()
184
185    def flush(self):
186        """Flush the output."""
187        for f in range(0, len(self.fhs)):
188            if self.fhs[f] is not None:
189                self.fhs[f].flush()
190
191if __name__ == "__main__":
192    l = log(['stdout', 'log.txt'], tail_size = 20)
193    for i in range(0, 10):
194        l.output('log: hello world: %d\n' % (i))
195    l.output('log: hello world CRLF\r\n')
196    l.output('log: hello world NONE')
197    l.flush()
198    print('=-' * 40)
199    print('tail: %d' % (len(l.tail)))
200    print(l)
201    print('=-' * 40)
202    for i in range(0, 10):
203        l.output('log: hello world 2: %d\n' % (i))
204    l.flush()
205    print('=-' * 40)
206    print('tail: %d' % (len(l.tail)))
207    print(l)
208    print('=-' * 40)
209    for i in [0, 1]:
210        quiet = False
211        tracing = False
212        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
213        trace('trace with quiet and trace off')
214        notice('notice with quiet and trace off')
215        quiet = True
216        tracing = False
217        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
218        trace('trace with quiet on and trace off')
219        notice('notice with quiet on and trace off')
220        quiet = False
221        tracing = True
222        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
223        trace('trace with quiet off and trace on')
224        notice('notice with quiet off and trace on')
225        quiet = True
226        tracing = True
227        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
228        trace('trace with quiet on and trace on')
229        notice('notice with quiet on and trace on')
230        default = l
231    print('=-' * 40)
232    print('tail: %d' % (len(l.tail)))
233    print(l)
234    print('=-' * 40)
235    del l
Note: See TracBrowser for help on using the repository browser.