Salome HOME
Update copyrights 2014.
[modules/kernel.git] / src / KERNEL_PY / kernel / logger.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
9 #
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21
22 #=============================================================================
23 #  Author    : Guillaume Boulant (CSSI)
24 #  Rewritten by Renaud Barate (EDF R&D)
25 #  Project   : SALOME
26 #  Copyright : EDF 2001-2009
27 #  $Header$
28 #=============================================================================
29 """
30 This module defines a class which provides logging facility in Salome:
31 """
32
33 import sys, os
34 import logging
35
36 from salome.kernel.deprecation import deprecated
37 from salome.kernel import termcolor
38 import salome.kernel.logconfig
39
40 class Logger(logging.Logger):
41     """
42     This class formats and displays log messages in Salome environment. It
43     inherits :class:`Logger<logging.Logger>` class defined in :mod:`logging`
44     module from Python library, so all methods from :class:`logging.Logger`
45     can be used here. The format of the traces is:
46     
47     LEVEL    [keyword] : Message
48     
49     where `LEVEL` is the level of the message (`DEBUG`, `INFO`, etc.),
50     `keyword` is the name of the logger, and `Message` is the message to log.
51     
52     When creating a new Logger object, the parameter `keyword` defines the
53     name of the logger, `level` defines the logging level (default is
54     :const:`logging.DEBUG` if KERNEL module is configured with --enable-debug
55     option or :const:`logging.WARNING` otherwise), and `color` defines the color
56     of the log messages for this logger (log messages will appear in color
57     only when displayed on color-capable ASCII terminals). See module
58     :mod:`salome.kernel.termcolor` for the color constants.
59     
60     By default, log messages will be displayed only on standard output. They
61     can also be recorded in a file (see method :meth:`setLogFile`). For now,
62     the CORBA-based logging facility can not be used through this class.
63
64     A source filename `sourceFileName` can be defined. If this argument is
65     specified, then the `keyword` is modified to the basename of the `sourceFileName`
66     
67     Basic usage::
68     
69         from salome.kernel.logger import Logger
70         log = Logger("Test")
71         log.debug("Debug message")
72         log.info("Information message")
73         log.warning("Warning message")
74         log.error("Error message")
75         log.critical("Fatal error message")
76
77     """
78
79     def __init__(self, keyword = "KEY", level = salome.kernel.logconfig.loggingLevel,
80                  color = None, sourceFileName=None):
81
82         if sourceFileName is not None:
83             keyword = os.path.basename(sourceFileName).split('.')[0]
84         logging.Logger.__init__(self, keyword, level)
85         self._baseFormatString = "%(levelname)-8s [%(name)s] : %(message)s"
86         self._baseFormatter = logging.Formatter(self._baseFormatString)
87         if hasattr(sys.stdout, "flush"):
88             self._stdoutStream = sys.stdout
89         else:
90             self._stdoutStream = _UnFlushableLogStream(sys.stdout)
91         self._stdoutHandler = logging.StreamHandler(self._stdoutStream)
92         self._stdoutHandler.setLevel(logging.DEBUG)
93         self.setColor(color)
94         self.addHandler(self._stdoutHandler)
95         self._fileHandler = None
96
97     def showDebug(self):
98         """
99         Log all messages, including DEBUG level messages (equivalent to
100         ``setLevel(logging.DEBUG)``).
101         """
102         self.setLevel(logging.DEBUG)
103
104     def setLogFile(self, logFilename):
105         """
106         Define a log file to record the log messages (in addition to the
107         standard output).
108         """
109         self.closeLogFile()
110         self._fileHandler = logging.FileHandler(logFilename, 'w')
111         self._fileHandler.setLevel(logging.DEBUG)
112         self._fileHandler.setFormatter(self._baseFormatter)
113         self.addHandler(self._fileHandler)
114
115     def setColor(self, color):
116         """
117         Set the color of log messages on color-capable terminals. If `color`
118         is :const:`None`, the default color will be used.
119         """
120         if color is None or not termcolor.canDisplayColor(self._stdoutStream):
121             stdoutFormatter = self._baseFormatter
122         else:
123             format = ("%s%s%s" %
124                       (termcolor.getControlSequence(color),
125                        self._baseFormatString,
126                        termcolor.getControlSequence(termcolor.DEFAULT)))
127             stdoutFormatter = logging.Formatter(format)
128         self._stdoutHandler.setFormatter(stdoutFormatter)
129
130     def closeLogFile(self):
131         """Close the log file."""
132         if self._fileHandler is not None:
133             self.removeHandler(self._fileHandler)
134             self._fileHandler.close()
135             self._fileHandler = None
136
137     def hideDebug(self):
138         """
139         Hide DEBUG level messages (equivalent to ``setLevel(logging.INFO)``).
140         """
141         self.setLevel(logging.INFO)
142
143     @deprecated("Deprecated since version 5.1.5. Please replace with "
144                 "Logger.critical(message)")
145     def fatal(self, message):
146         """
147         Log a message with CRITICAL level. This method only exists for
148         backward compatibility and is equivalent to ``critical(message)``.
149         """
150         self.critical(message)
151
152
153 class _UnFlushableLogStream:
154     """
155     This utility class allows to log messages to a stream with no `flush`
156     method. This is useful to send log messages to `PyOut` objects.
157     """
158
159     def __init__(self, stream):
160         self._stream = stream
161
162     def write(self, msg):
163         self._stream.write(msg)
164
165     def flush(self):
166         pass
167
168
169 class ExtLogger(Logger):
170     """
171     This class extends :class:`Logger` class and adds exception information
172     when DEBUG messages are recorded. It exists mainly for backward
173     compatibility, as the same thing can be done by calling
174     ``Logger.debug(message, exc_info = True)``.
175     """
176     
177     @deprecated("Class ExtLogger is deprecated since version 5.1.5. See "
178                 "documentation for replacement.")
179     def __init__(self, keyword = "KEY",
180                  level = salome.kernel.logconfig.loggingLevel,
181                  color = None, sourceFileName=None):
182         Logger.__init__(self, keyword, level, color, sourceFileName)
183
184     def debug( self, message ):
185         """
186         Log a DEBUG message with exception information (equivalent to
187         ``Logger.debug(message, exc_info = True)``).
188         """
189         Logger.debug(self, message, exc_info = True)
190
191
192 def TEST_Logger():
193     """Test function for logger module"""
194     log = Logger("TST")
195
196     # Base methods
197     log.info("Information message")
198     log.debug("Debug message")
199     log.fatal("Fatal error message")
200
201     # Message building
202     data = 12
203     log.info("This message displays data = " + str(data))
204
205     data = {}
206     data["KERNEL"] = "V1"
207     data["GEOM"] = "V2"
208     log.info("This message displays data = " + str(data))
209
210     # Test with a non-string parameter
211     log.info(data)
212
213     # Test with a default instance
214     log = Logger()
215     log.info("Default logger")
216
217     # Test showDebug method
218     log.setLogFile("test.log")
219     log.debug("Debug trace")
220     log.hideDebug()
221     log.debug("This trace should NOT be displayed")
222     log.showDebug()
223     log.debug("This trace should be displayed")
224     log.closeLogFile()
225     log.info("After closing the log file")
226
227
228 # Main function only used to test the module
229 if __name__ == "__main__":
230     TEST_Logger()