Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / mininet / mininet / log.py
1 "Logging functions for Mininet."
2
3 import logging
4 from logging import Logger
5 import types
6
7 # Create a new loglevel, 'CLI info', which enables a Mininet user to see only
8 # the output of the commands they execute, plus any errors or warnings.  This
9 # level is in between info and warning.  CLI info-level commands should not be
10 # printed during regression tests.
11 OUTPUT = 25
12
13 LEVELS = { 'debug': logging.DEBUG,
14            'info': logging.INFO,
15            'output': OUTPUT,
16            'warning': logging.WARNING,
17            'error': logging.ERROR,
18            'critical': logging.CRITICAL }
19
20 # change this to logging.INFO to get printouts when running unit tests
21 LOGLEVELDEFAULT = OUTPUT
22
23 #default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
24 LOGMSGFORMAT = '%(message)s'
25
26
27 # Modified from python2.5/__init__.py
28 class StreamHandlerNoNewline( logging.StreamHandler ):
29     """StreamHandler that doesn't print newlines by default.
30        Since StreamHandler automatically adds newlines, define a mod to more
31        easily support interactive mode when we want it, or errors-only logging
32        for running unit tests."""
33
34     def emit( self, record ):
35         """Emit a record.
36            If a formatter is specified, it is used to format the record.
37            The record is then written to the stream with a trailing newline
38            [ N.B. this may be removed depending on feedback ]. If exception
39            information is present, it is formatted using
40            traceback.printException and appended to the stream."""
41         try:
42             msg = self.format( record )
43             fs = '%s'  # was '%s\n'
44             if not hasattr( types, 'UnicodeType' ):  # if no unicode support...
45                 self.stream.write( fs % msg )
46             else:
47                 try:
48                     self.stream.write( fs % msg )
49                 except UnicodeError:
50                     self.stream.write( fs % msg.encode( 'UTF-8' ) )
51             self.flush()
52         except ( KeyboardInterrupt, SystemExit ):
53             raise
54         except:
55             self.handleError( record )
56
57
58 class Singleton( type ):
59     """Singleton pattern from Wikipedia
60        See http://en.wikipedia.org/wiki/Singleton_Pattern
61
62        Intended to be used as a __metaclass_ param, as shown for the class
63        below."""
64
65     def __init__( cls, name, bases, dict_ ):
66         super( Singleton, cls ).__init__( name, bases, dict_ )
67         cls.instance = None
68
69     def __call__( cls, *args, **kw ):
70         if cls.instance is None:
71             cls.instance = super( Singleton, cls ).__call__( *args, **kw )
72         return cls.instance
73
74
75 class MininetLogger( Logger, object ):
76     """Mininet-specific logger
77        Enable each mininet .py file to with one import:
78
79        from mininet.log import [lg, info, error]
80
81        ...get a default logger that doesn't require one newline per logging
82        call.
83
84        Inherit from object to ensure that we have at least one new-style base
85        class, and can then use the __metaclass__ directive, to prevent this
86        error:
87
88        TypeError: Error when calling the metaclass bases
89        a new-style class can't have only classic bases
90
91        If Python2.5/logging/__init__.py defined Filterer as a new-style class,
92        via Filterer( object ): rather than Filterer, we wouldn't need this.
93
94        Use singleton pattern to ensure only one logger is ever created."""
95
96     __metaclass__ = Singleton
97
98     def __init__( self ):
99
100         Logger.__init__( self, "mininet" )
101
102         # create console handler
103         ch = StreamHandlerNoNewline()
104         # create formatter
105         formatter = logging.Formatter( LOGMSGFORMAT )
106         # add formatter to ch
107         ch.setFormatter( formatter )
108         # add ch to lg
109         self.addHandler( ch )
110
111         self.setLogLevel()
112
113     def setLogLevel( self, levelname=None ):
114         """Setup loglevel.
115            Convenience function to support lowercase names.
116            levelName: level name from LEVELS"""
117         level = LOGLEVELDEFAULT
118         if levelname is not None:
119             if levelname not in LEVELS:
120                 raise Exception( 'unknown levelname seen in setLogLevel' )
121             else:
122                 level = LEVELS.get( levelname, level )
123
124         self.setLevel( level )
125         self.handlers[ 0 ].setLevel( level )
126
127     # pylint: disable=method-hidden
128     # "An attribute inherited from mininet.log hide this method" (sic)
129     # Not sure why this is occurring - this function definitely gets called.
130
131     # See /usr/lib/python2.5/logging/__init__.py; modified from warning()
132     def output( self, msg, *args, **kwargs ):
133         """Log 'msg % args' with severity 'OUTPUT'.
134
135            To pass exception information, use the keyword argument exc_info
136            with a true value, e.g.
137
138            logger.warning("Houston, we have a %s", "cli output", exc_info=1)
139         """
140         if self.manager.disable >= OUTPUT:
141             return
142         if self.isEnabledFor( OUTPUT ):
143             self._log( OUTPUT, msg, args, kwargs )
144
145     # pylint: enable=method-hidden
146
147 lg = MininetLogger()
148
149 # Make things a bit more convenient by adding aliases
150 # (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
151 # In the future we may wish to make things more efficient by only
152 # doing the join (and calling the function) unless the logging level
153 # is high enough.
154
155 def makeListCompatible( fn ):
156     """Return a new function allowing fn( 'a 1 b' ) to be called as
157        newfn( 'a', 1, 'b' )"""
158
159     def newfn( *args ):
160         "Generated function. Closure-ish."
161         if len( args ) == 1:
162             return fn( *args )
163         args = ' '.join( str( arg ) for arg in args )
164         return fn( args )
165
166     # Fix newfn's name and docstring
167     setattr( newfn, '__name__', fn.__name__ )
168     setattr( newfn, '__doc__', fn.__doc__ )
169     return newfn
170
171 _loggers = lg.info, lg.output, lg.warn, lg.error, lg.debug
172 _loggers = tuple( makeListCompatible( logger )
173                   for logger in _loggers )
174 lg.info, lg.output, lg.warn, lg.error, lg.debug = _loggers
175 info, output, warn, error, debug = _loggers
176
177 setLogLevel = lg.setLogLevel