Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
179 lines (137 sloc) 5.99 KB
"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.
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
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOGMSGFORMAT = '%(message)s'
# Modified from python2.5/
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."""
msg = self.format( record )
fs = '%s' # was '%s\n'
if not hasattr( types, 'UnicodeType' ): # if no unicode support... fs % msg )
try: fs % msg )
except UnicodeError: fs % msg.encode( 'UTF-8' ) )
except ( KeyboardInterrupt, SystemExit ):
self.handleError( record )
class Singleton( type ):
"""Singleton pattern from Wikipedia
Intended to be used as a __metaclass_ param, as shown for the class
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
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
TypeError: Error when calling the metaclass bases
a new-style class can't have only classic bases
If Python2.5/logging/ 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 )
def setLogLevel( self, levelname=None ):
"""Setup loglevel.
Convenience function to support lowercase names.
levelName: level name from LEVELS"""
if levelname != None:
if levelname not in LEVELS:
raise Exception( 'unknown levelname seen in setLogLevel' )
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/; 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:
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.output, lg.warn, lg.error, lg.debug ) = [
makeListCompatible( f ) for f in, lg.output, lg.warn, lg.error, lg.debug ]
setLogLevel = lg.setLogLevel
Jump to Line
Something went wrong with that request. Please try again.