source: rtems-tools/rtemstoolkit/log.py @ e058db0

5
Last change on this file since e058db0 was e058db0, checked in by Chris Johns <chrisj@…>, on 11/07/18 at 03:55:20

python: Provide support to select a valid python version.

  • Update imports after wrapping the code.
  • Fix python3 issues.
  • Fix config path issues for in repo and install runs.

Closes #3537

  • Property mode set to 100755
File size: 7.2 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# A global capture handler.
57#
58capture = None
59
60#
61# Global parameters.
62#
63tracing = False
64quiet = False
65
66#
67# Global output lock to keep output together when working with threads
68#
69lock = threading.Lock()
70
71def set_default_once(log):
72    if default is None:
73        default = log
74
75def _output(text = os.linesep, log = None):
76    """Output the text to a log if provided else send it to stdout."""
77    if text is None:
78        text = os.linesep
79    if type(text) is list:
80        text = os.linesep.join(text) + os.linesep
81    if isinstance(text, bytes):
82        text = text.decode('utf-8', 'ignore')
83    if log:
84        log.output(text)
85    elif default is not None:
86        default.output(text)
87    else:
88        lock.acquire()
89        for l in text.replace(chr(13), '').splitlines():
90            print(l)
91        lock.release()
92    if capture is not None:
93        lock.acquire()
94        capture(text)
95        lock.release()
96
97def stderr(text = os.linesep, log = None):
98    lock.acquire()
99    for l in text.replace(chr(13), '').splitlines():
100        print(l, file = sys.stderr)
101    lock.release()
102
103def output(text = os.linesep, log = None):
104    if not quiet:
105        _output(text, log)
106
107def notice(text = os.linesep, log = None, stdout_only = False):
108    if not quiet and \
109            (default is not None and not default.has_stdout() or stdout_only):
110        lock.acquire()
111        for l in text.replace(chr(13), '').splitlines():
112            print(l)
113        lock.release()
114    if not stdout_only:
115        _output(text, log)
116
117def trace(text = os.linesep, log = None):
118    if tracing:
119        _output(text, log)
120
121def warning(text = os.linesep, log = None):
122    for l in text.replace(chr(13), '').splitlines():
123        _output('warning: %s' % (l), log)
124
125def flush(log = None):
126    if log:
127        log.flush()
128    elif default is not None:
129        default.flush()
130
131class log:
132    """Log output to stdout or a file."""
133    def __init__(self, streams = None, tail_size = 100):
134        self.lock = threading.Lock()
135        self.tail = []
136        self.tail_size = tail_size
137        self.fhs = [None, None]
138        if streams:
139            for s in streams:
140                if s == 'stdout':
141                    self.fhs[0] = sys.stdout
142                elif s == 'stderr':
143                    self.fhs[1] = sys.stderr
144                else:
145                    try:
146                        self.fhs.append(open(s, 'w'))
147                    except IOError as ioe:
148                         raise error.general("creating log file '" + s + \
149                                             "': " + str(ioe))
150
151    def __del__(self):
152        for f in range(2, len(self.fhs)):
153            self.fhs[f].close()
154
155    def __str__(self):
156        t = ''
157        for tl in self.tail:
158            t += tl + os.linesep
159        return t[:-len(os.linesep)]
160
161    def _tail(self, text):
162        if type(text) is not list:
163            text = text.splitlines()
164        self.tail += text
165        if len(self.tail) > self.tail_size:
166            self.tail = self.tail[-self.tail_size:]
167
168    def has_stdout(self):
169        return self.fhs[0] is not None
170
171    def has_stderr(self):
172        return self.fhs[1] is not None
173
174    def output(self, text):
175        """Output the text message to all the logs."""
176        # Reformat the text to have local line types.
177        text = text.replace(chr(13), '').splitlines()
178        self._tail(text)
179        out = os.linesep.join(text) + os.linesep
180        if isinstance(out, bytes):
181            out = out.decode('utf-8', 'ignore')
182        self.lock.acquire()
183        try:
184            for f in range(0, len(self.fhs)):
185                if self.fhs[f] is not None:
186                    self.fhs[f].write(out)
187            self.flush()
188        except:
189            raise
190        finally:
191            self.lock.release()
192
193    def flush(self):
194        """Flush the output."""
195        for f in range(0, len(self.fhs)):
196            if self.fhs[f] is not None:
197                self.fhs[f].flush()
198
199if __name__ == "__main__":
200    l = log(['stdout', 'log.txt'], tail_size = 20)
201    for i in range(0, 10):
202        l.output('log: hello world: %d\n' % (i))
203    l.output('log: hello world CRLF\r\n')
204    l.output('log: hello world NONE')
205    l.flush()
206    print('=-' * 40)
207    print('tail: %d' % (len(l.tail)))
208    print(l)
209    print('=-' * 40)
210    for i in range(0, 10):
211        l.output('log: hello world 2: %d\n' % (i))
212    l.flush()
213    print('=-' * 40)
214    print('tail: %d' % (len(l.tail)))
215    print(l)
216    print('=-' * 40)
217    for i in [0, 1]:
218        quiet = False
219        tracing = False
220        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
221        trace('trace with quiet and trace off')
222        notice('notice with quiet and trace off')
223        quiet = True
224        tracing = False
225        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
226        trace('trace with quiet on and trace off')
227        notice('notice with quiet on and trace off')
228        quiet = False
229        tracing = True
230        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
231        trace('trace with quiet off and trace on')
232        notice('notice with quiet off and trace on')
233        quiet = True
234        tracing = True
235        print('- quiet:%s - trace:%s %s' % (str(quiet), str(tracing), '-' * 30))
236        trace('trace with quiet on and trace on')
237        notice('notice with quiet on and trace on')
238        default = l
239    print('=-' * 40)
240    print('tail: %d' % (len(l.tail)))
241    print(l)
242    print('=-' * 40)
243    del l
Note: See TracBrowser for help on using the repository browser.