1 "Logging functions for Mininet."
4 from logging import Logger
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.
13 LEVELS = { 'debug': logging.DEBUG,
16 'warning': logging.WARNING,
17 'error': logging.ERROR,
18 'critical': logging.CRITICAL }
20 # change this to logging.INFO to get printouts when running unit tests
21 LOGLEVELDEFAULT = OUTPUT
23 #default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
24 LOGMSGFORMAT = '%(message)s'
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."""
34 def emit( self, 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."""
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 )
48 self.stream.write( fs % msg )
50 self.stream.write( fs % msg.encode( 'UTF-8' ) )
52 except ( KeyboardInterrupt, SystemExit ):
55 self.handleError( record )
58 class Singleton( type ):
59 """Singleton pattern from Wikipedia
60 See http://en.wikipedia.org/wiki/Singleton_Pattern
62 Intended to be used as a __metaclass_ param, as shown for the class
65 def __init__( cls, name, bases, dict_ ):
66 super( Singleton, cls ).__init__( name, bases, dict_ )
69 def __call__( cls, *args, **kw ):
70 if cls.instance is None:
71 cls.instance = super( Singleton, cls ).__call__( *args, **kw )
75 class MininetLogger( Logger, object ):
76 """Mininet-specific logger
77 Enable each mininet .py file to with one import:
79 from mininet.log import [lg, info, error]
81 ...get a default logger that doesn't require one newline per logging
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
88 TypeError: Error when calling the metaclass bases
89 a new-style class can't have only classic bases
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.
94 Use singleton pattern to ensure only one logger is ever created."""
96 __metaclass__ = Singleton
100 Logger.__init__( self, "mininet" )
102 # create console handler
103 ch = StreamHandlerNoNewline()
105 formatter = logging.Formatter( LOGMSGFORMAT )
106 # add formatter to ch
107 ch.setFormatter( formatter )
109 self.addHandler( ch )
113 def setLogLevel( self, levelname=None ):
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' )
122 level = LEVELS.get( levelname, level )
124 self.setLevel( level )
125 self.handlers[ 0 ].setLevel( level )
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.
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'.
135 To pass exception information, use the keyword argument exc_info
136 with a true value, e.g.
138 logger.warning("Houston, we have a %s", "cli output", exc_info=1)
140 if self.manager.disable >= OUTPUT:
142 if self.isEnabledFor( OUTPUT ):
143 self._log( OUTPUT, msg, args, kwargs )
145 # pylint: enable=method-hidden
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
155 def makeListCompatible( fn ):
156 """Return a new function allowing fn( 'a 1 b' ) to be called as
157 newfn( 'a', 1, 'b' )"""
160 "Generated function. Closure-ish."
163 args = ' '.join( str( arg ) for arg in args )
166 # Fix newfn's name and docstring
167 setattr( newfn, '__name__', fn.__name__ )
168 setattr( newfn, '__doc__', fn.__doc__ )
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
177 setLogLevel = lg.setLogLevel