Statistics
| Branch: | Tag: | Revision:

mininet / mininet / log.py @ 086ef80e

History | View | Annotate | Download (4.87 KB)

1 80a8fa62 Bob Lantz
"Logging functions for Mininet."
2 7b804ffb Brandon Heller
3 220890a0 Brandon Heller
import logging
4 723d068c Brandon Heller
from logging import Logger
5 7b804ffb Brandon Heller
import types
6
7 80a8fa62 Bob Lantz
LEVELS = { 'debug': logging.DEBUG,
8 220890a0 Brandon Heller
          'info': logging.INFO,
9
          'warning': logging.WARNING,
10
          'error': logging.ERROR,
11 80a8fa62 Bob Lantz
          'critical': logging.CRITICAL }
12 220890a0 Brandon Heller
13
# change this to logging.INFO to get printouts when running unit tests
14 80a8fa62 Bob Lantz
LOGLEVELDEFAULT = logging.WARNING
15 220890a0 Brandon Heller
16
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
17 80a8fa62 Bob Lantz
LOGMSGFORMAT = '%(message)s'
18 220890a0 Brandon Heller
19
20
# Modified from python2.5/__init__.py
21 80a8fa62 Bob Lantz
class StreamHandlerNoNewline( logging.StreamHandler ):
22
    """StreamHandler that doesn't print newlines by default.
23
       Since StreamHandler automatically adds newlines, define a mod to more
24
       easily support interactive mode when we want it, or errors-only logging
25
       for running unit tests."""
26
27
    def emit( self, record ):
28
        """Emit a record.
29
           If a formatter is specified, it is used to format the record.
30
           The record is then written to the stream with a trailing newline
31
           [ N.B. this may be removed depending on feedback ]. If exception
32
           information is present, it is formatted using
33
           traceback.printException and appended to the stream."""
34 7b804ffb Brandon Heller
        try:
35 80a8fa62 Bob Lantz
            msg = self.format( record )
36 220890a0 Brandon Heller
            fs = '%s' # was '%s\n'
37 80a8fa62 Bob Lantz
            if not hasattr( types, 'UnicodeType' ): #if no unicode support...
38
                self.stream.write( fs % msg )
39 7b804ffb Brandon Heller
            else:
40
                try:
41 80a8fa62 Bob Lantz
                    self.stream.write( fs % msg )
42 7b804ffb Brandon Heller
                except UnicodeError:
43 80a8fa62 Bob Lantz
                    self.stream.write( fs % msg.encode( 'UTF-8' ) )
44 7b804ffb Brandon Heller
            self.flush()
45 80a8fa62 Bob Lantz
        except ( KeyboardInterrupt, SystemExit ):
46 7b804ffb Brandon Heller
            raise
47
        except:
48 80a8fa62 Bob Lantz
            self.handleError( record )
49 220890a0 Brandon Heller
50
51 80a8fa62 Bob Lantz
class Singleton( type ):
52
    """Singleton pattern from Wikipedia
53
       See http://en.wikipedia.org/wiki/SingletonPattern#Python
54 723d068c Brandon Heller

55 80a8fa62 Bob Lantz
       Intended to be used as a __metaclass_ param, as shown for the class
56
       below.
57 723d068c Brandon Heller

58 80a8fa62 Bob Lantz
       Changed cls first args to mcs to satisfy pylint."""
59 723d068c Brandon Heller
60 80a8fa62 Bob Lantz
    def __init__( mcs, name, bases, dict_ ):
61
        super( Singleton, mcs ).__init__( name, bases, dict_ )
62 723d068c Brandon Heller
        mcs.instance = None
63
64 80a8fa62 Bob Lantz
    def __call__( mcs, *args, **kw ):
65 723d068c Brandon Heller
        if mcs.instance is None:
66 80a8fa62 Bob Lantz
            mcs.instance = super( Singleton, mcs ).__call__( *args, **kw )
67 723d068c Brandon Heller
            return mcs.instance
68
69
70 80a8fa62 Bob Lantz
class MininetLogger( Logger, object ):
71
    """Mininet-specific logger
72
       Enable each mininet .py file to with one import:
73 723d068c Brandon Heller

74 f8952d56 Bob Lantz
       from mininet.log import [lg, info, error]
75 723d068c Brandon Heller

76 80a8fa62 Bob Lantz
       ...get a default logger that doesn't require one newline per logging
77
       call.
78 220890a0 Brandon Heller

79 80a8fa62 Bob Lantz
       Inherit from object to ensure that we have at least one new-style base
80
       class, and can then use the __metaclass__ directive, to prevent this
81
       error:
82 723d068c Brandon Heller

83 80a8fa62 Bob Lantz
       TypeError: Error when calling the metaclass bases
84 723d068c Brandon Heller
       a new-style class can't have only classic bases
85

86 80a8fa62 Bob Lantz
       If Python2.5/logging/__init__.py defined Filterer as a new-style class,
87
       via Filterer( object ): rather than Filterer, we wouldn't need this.
88

89
       Use singleton pattern to ensure only one logger is ever created."""
90 723d068c Brandon Heller
91
    __metaclass__ = Singleton
92
93 80a8fa62 Bob Lantz
    def __init__( self ):
94 723d068c Brandon Heller
95 80a8fa62 Bob Lantz
        Logger.__init__( self, "mininet" )
96 723d068c Brandon Heller
97 220890a0 Brandon Heller
        # create console handler
98
        ch = StreamHandlerNoNewline()
99
        # create formatter
100 80a8fa62 Bob Lantz
        formatter = logging.Formatter( LOGMSGFORMAT )
101 220890a0 Brandon Heller
        # add formatter to ch
102 80a8fa62 Bob Lantz
        ch.setFormatter( formatter )
103 220890a0 Brandon Heller
        # add ch to lg
104 80a8fa62 Bob Lantz
        self.addHandler( ch )
105 723d068c Brandon Heller
106 80a8fa62 Bob Lantz
        self.setLogLevel()
107 723d068c Brandon Heller
108 80a8fa62 Bob Lantz
    def setLogLevel( self, levelname=None ):
109
        """Setup loglevel.
110
           Convenience function to support lowercase names.
111
           levelName: level name from LEVELS"""
112
        level = LOGLEVELDEFAULT
113 723d068c Brandon Heller
        if levelname != None:
114
            if levelname not in LEVELS:
115 ddfbfc73 Bob Lantz
                raise Exception( 'unknown levelname seen in setLogLevel' )
116 723d068c Brandon Heller
            else:
117 80a8fa62 Bob Lantz
                level = LEVELS.get( levelname, level )
118 220890a0 Brandon Heller
119 80a8fa62 Bob Lantz
        self.setLevel( level )
120
        self.handlers[ 0 ].setLevel( level )
121 220890a0 Brandon Heller
122
123 723d068c Brandon Heller
lg = MininetLogger()
124 ddfbfc73 Bob Lantz
125
# Make things a bit more convenient by adding aliases
126
# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
127
# In the future we may wish to make things more efficient by only
128
# doing the join (and calling the function) unless the logging level
129
# is high enough.
130
131
def makeListCompatible( fn ):
132
    """Return a new function allowing fn( 'a 1 b' ) to be called as
133
       newfn( 'a', 1, 'b' )"""
134
135
    def newfn( *args ):
136
        "Generated function."
137
        if len( args ) == 1:
138
            return fn( *args )
139
        args = ' '.join( [ str( arg ) for arg in args ] )
140
        return fn( args )
141
142
    # Fix newfn's name and docstring
143
    setattr( newfn, '__name__', fn.__name__ )
144
    setattr( newfn, '__doc__', fn.__doc__ )
145
    return newfn
146
147
info, warn, error, debug = lg.info, lg.warn, lg.error, lg.debug = [
148
    makeListCompatible( f ) for f in lg.info, lg.warn, lg.error, lg.debug ]