First commit
diff --git a/mininet/log.py b/mininet/log.py
new file mode 100644
index 0000000..cd00821
--- /dev/null
+++ b/mininet/log.py
@@ -0,0 +1,178 @@
+"Logging functions for Mininet."
+
+import logging
+from logging import Logger
+import types
+
+# Create a new loglevel, 'CLI info', which enables a Mininet user to see only
+# the output of the commands they execute, plus any errors or warnings. This
+# level is in between info and warning. CLI info-level commands should not be
+# printed during regression tests.
+OUTPUT = 25
+
+LEVELS = { 'debug': logging.DEBUG,
+ 'info': logging.INFO,
+ 'output': OUTPUT,
+ 'warning': logging.WARNING,
+ 'error': logging.ERROR,
+ 'critical': logging.CRITICAL }
+
+# change this to logging.INFO to get printouts when running unit tests
+LOGLEVELDEFAULT = OUTPUT
+
+#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+LOGMSGFORMAT = '%(message)s'
+
+
+# Modified from python2.5/__init__.py
+class StreamHandlerNoNewline( logging.StreamHandler ):
+ """StreamHandler that doesn't print newlines by default.
+ Since StreamHandler automatically adds newlines, define a mod to more
+ easily support interactive mode when we want it, or errors-only logging
+ for running unit tests."""
+
+ def emit( self, record ):
+ """Emit a record.
+ If a formatter is specified, it is used to format the record.
+ The record is then written to the stream with a trailing newline
+ [ N.B. this may be removed depending on feedback ]. If exception
+ information is present, it is formatted using
+ traceback.printException and appended to the stream."""
+ try:
+ msg = self.format( record )
+ fs = '%s' # was '%s\n'
+ if not hasattr( types, 'UnicodeType' ): # if no unicode support...
+ self.stream.write( fs % msg )
+ else:
+ try:
+ self.stream.write( fs % msg )
+ except UnicodeError:
+ self.stream.write( fs % msg.encode( 'UTF-8' ) )
+ self.flush()
+ except ( KeyboardInterrupt, SystemExit ):
+ raise
+ except:
+ self.handleError( record )
+
+
+class Singleton( type ):
+ """Singleton pattern from Wikipedia
+ See http://en.wikipedia.org/wiki/SingletonPattern#Python
+
+ Intended to be used as a __metaclass_ param, as shown for the class
+ below.
+
+ Changed cls first args to mcs to satisfy pylint."""
+
+ def __init__( mcs, name, bases, dict_ ):
+ super( Singleton, mcs ).__init__( name, bases, dict_ )
+ mcs.instance = None
+
+ def __call__( mcs, *args, **kw ):
+ if mcs.instance is None:
+ mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
+ return mcs.instance
+
+
+class MininetLogger( Logger, object ):
+ """Mininet-specific logger
+ Enable each mininet .py file to with one import:
+
+ from mininet.log import [lg, info, error]
+
+ ...get a default logger that doesn't require one newline per logging
+ call.
+
+ Inherit from object to ensure that we have at least one new-style base
+ class, and can then use the __metaclass__ directive, to prevent this
+ error:
+
+ TypeError: Error when calling the metaclass bases
+ a new-style class can't have only classic bases
+
+ If Python2.5/logging/__init__.py defined Filterer as a new-style class,
+ via Filterer( object ): rather than Filterer, we wouldn't need this.
+
+ Use singleton pattern to ensure only one logger is ever created."""
+
+ __metaclass__ = Singleton
+
+ def __init__( self ):
+
+ Logger.__init__( self, "mininet" )
+
+ # create console handler
+ ch = StreamHandlerNoNewline()
+ # create formatter
+ formatter = logging.Formatter( LOGMSGFORMAT )
+ # add formatter to ch
+ ch.setFormatter( formatter )
+ # add ch to lg
+ self.addHandler( ch )
+
+ self.setLogLevel()
+
+ def setLogLevel( self, levelname=None ):
+ """Setup loglevel.
+ Convenience function to support lowercase names.
+ levelName: level name from LEVELS"""
+ level = LOGLEVELDEFAULT
+ if levelname is not None:
+ if levelname not in LEVELS:
+ raise Exception( 'unknown levelname seen in setLogLevel' )
+ else:
+ level = LEVELS.get( levelname, level )
+
+ self.setLevel( level )
+ self.handlers[ 0 ].setLevel( level )
+
+ # pylint: disable-msg=E0202
+ # "An attribute inherited from mininet.log hide this method"
+ # Not sure why this is occurring - this function definitely gets called.
+
+ # See /usr/lib/python2.5/logging/__init__.py; modified from warning()
+ def output( self, msg, *args, **kwargs ):
+ """Log 'msg % args' with severity 'OUTPUT'.
+
+ To pass exception information, use the keyword argument exc_info
+ with a true value, e.g.
+
+ logger.warning("Houston, we have a %s", "cli output", exc_info=1)
+ """
+ if self.manager.disable >= OUTPUT:
+ return
+ if self.isEnabledFor( OUTPUT ):
+ self._log( OUTPUT, msg, args, kwargs )
+
+ # pylint: enable-msg=E0202
+
+lg = MininetLogger()
+
+# Make things a bit more convenient by adding aliases
+# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
+# In the future we may wish to make things more efficient by only
+# doing the join (and calling the function) unless the logging level
+# is high enough.
+
+def makeListCompatible( fn ):
+ """Return a new function allowing fn( 'a 1 b' ) to be called as
+ newfn( 'a', 1, 'b' )"""
+
+ def newfn( *args ):
+ "Generated function. Closure-ish."
+ if len( args ) == 1:
+ return fn( *args )
+ args = ' '.join( [ str( arg ) for arg in args ] )
+ return fn( args )
+
+ # Fix newfn's name and docstring
+ setattr( newfn, '__name__', fn.__name__ )
+ setattr( newfn, '__doc__', fn.__doc__ )
+ return newfn
+
+info, output, warn, error, debug = (
+ lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
+ makeListCompatible( f ) for f in
+ lg.info, lg.output, lg.warn, lg.error, lg.debug ]
+
+setLogLevel = lg.setLogLevel