Statistics
| Branch: | Tag: | Revision:

mininet / mininet / log.py @ 149a1f56

History | View | Annotate | Download (5.98 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 1a40cd04 Brandon Heller
# 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 cdeaca86 Brandon Heller
OUTPUT = 25
12 1a40cd04 Brandon Heller
13 80a8fa62 Bob Lantz
LEVELS = { 'debug': logging.DEBUG,
14 220890a0 Brandon Heller
          'info': logging.INFO,
15 cdeaca86 Brandon Heller
          'output': OUTPUT,
16 220890a0 Brandon Heller
          'warning': logging.WARNING,
17
          'error': logging.ERROR,
18 80a8fa62 Bob Lantz
          'critical': logging.CRITICAL }
19 220890a0 Brandon Heller
20
# change this to logging.INFO to get printouts when running unit tests
21 1dcc0476 Bob Lantz
LOGLEVELDEFAULT = OUTPUT
22 220890a0 Brandon Heller
23
#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
24 80a8fa62 Bob Lantz
LOGMSGFORMAT = '%(message)s'
25 220890a0 Brandon Heller
26
27
# Modified from python2.5/__init__.py
28 80a8fa62 Bob Lantz
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 7b804ffb Brandon Heller
        try:
42 80a8fa62 Bob Lantz
            msg = self.format( record )
43 cf6f6704 Bob Lantz
            fs = '%s'  # was '%s\n'
44
            if not hasattr( types, 'UnicodeType' ):  # if no unicode support...
45 80a8fa62 Bob Lantz
                self.stream.write( fs % msg )
46 7b804ffb Brandon Heller
            else:
47
                try:
48 80a8fa62 Bob Lantz
                    self.stream.write( fs % msg )
49 7b804ffb Brandon Heller
                except UnicodeError:
50 80a8fa62 Bob Lantz
                    self.stream.write( fs % msg.encode( 'UTF-8' ) )
51 7b804ffb Brandon Heller
            self.flush()
52 80a8fa62 Bob Lantz
        except ( KeyboardInterrupt, SystemExit ):
53 7b804ffb Brandon Heller
            raise
54
        except:
55 80a8fa62 Bob Lantz
            self.handleError( record )
56 220890a0 Brandon Heller
57
58 80a8fa62 Bob Lantz
class Singleton( type ):
59
    """Singleton pattern from Wikipedia
60
       See http://en.wikipedia.org/wiki/SingletonPattern#Python
61 723d068c Brandon Heller

62 80a8fa62 Bob Lantz
       Intended to be used as a __metaclass_ param, as shown for the class
63
       below.
64 723d068c Brandon Heller

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

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

83 80a8fa62 Bob Lantz
       ...get a default logger that doesn't require one newline per logging
84
       call.
85 220890a0 Brandon Heller

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

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

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

96
       Use singleton pattern to ensure only one logger is ever created."""
97 723d068c Brandon Heller
98
    __metaclass__ = Singleton
99
100 80a8fa62 Bob Lantz
    def __init__( self ):
101 723d068c Brandon Heller
102 80a8fa62 Bob Lantz
        Logger.__init__( self, "mininet" )
103 723d068c Brandon Heller
104 220890a0 Brandon Heller
        # create console handler
105
        ch = StreamHandlerNoNewline()
106
        # create formatter
107 80a8fa62 Bob Lantz
        formatter = logging.Formatter( LOGMSGFORMAT )
108 220890a0 Brandon Heller
        # add formatter to ch
109 80a8fa62 Bob Lantz
        ch.setFormatter( formatter )
110 220890a0 Brandon Heller
        # add ch to lg
111 80a8fa62 Bob Lantz
        self.addHandler( ch )
112 723d068c Brandon Heller
113 80a8fa62 Bob Lantz
        self.setLogLevel()
114 723d068c Brandon Heller
115 80a8fa62 Bob Lantz
    def setLogLevel( self, levelname=None ):
116
        """Setup loglevel.
117
           Convenience function to support lowercase names.
118
           levelName: level name from LEVELS"""
119
        level = LOGLEVELDEFAULT
120 723d068c Brandon Heller
        if levelname != None:
121
            if levelname not in LEVELS:
122 ddfbfc73 Bob Lantz
                raise Exception( 'unknown levelname seen in setLogLevel' )
123 723d068c Brandon Heller
            else:
124 80a8fa62 Bob Lantz
                level = LEVELS.get( levelname, level )
125 220890a0 Brandon Heller
126 80a8fa62 Bob Lantz
        self.setLevel( level )
127
        self.handlers[ 0 ].setLevel( level )
128 220890a0 Brandon Heller
129 7ae73e67 Brandon Heller
    # pylint: disable-msg=E0202
130
    # "An attribute inherited from mininet.log hide this method"
131
    # Not sure why this is occurring - this function definitely gets called.
132
133 1a40cd04 Brandon Heller
    # See /usr/lib/python2.5/logging/__init__.py; modified from warning()
134 cdeaca86 Brandon Heller
    def output( self, msg, *args, **kwargs ):
135
        """Log 'msg % args' with severity 'OUTPUT'.
136 1a40cd04 Brandon Heller

137
           To pass exception information, use the keyword argument exc_info
138
           with a true value, e.g.
139

140
           logger.warning("Houston, we have a %s", "cli output", exc_info=1)
141
        """
142 cdeaca86 Brandon Heller
        if self.manager.disable >= OUTPUT:
143 1a40cd04 Brandon Heller
            return
144 cdeaca86 Brandon Heller
        if self.isEnabledFor( OUTPUT ):
145
            self._log( OUTPUT, msg, args, kwargs )
146 1a40cd04 Brandon Heller
147 7ae73e67 Brandon Heller
    # pylint: enable-msg=E0202
148 220890a0 Brandon Heller
149 723d068c Brandon Heller
lg = MininetLogger()
150 ddfbfc73 Bob Lantz
151
# Make things a bit more convenient by adding aliases
152
# (info, warn, error, debug) and allowing info( 'this', 'is', 'OK' )
153
# In the future we may wish to make things more efficient by only
154
# doing the join (and calling the function) unless the logging level
155
# is high enough.
156
157
def makeListCompatible( fn ):
158
    """Return a new function allowing fn( 'a 1 b' ) to be called as
159
       newfn( 'a', 1, 'b' )"""
160
161
    def newfn( *args ):
162 80be5642 Bob Lantz
        "Generated function. Closure-ish."
163 ddfbfc73 Bob Lantz
        if len( args ) == 1:
164
            return fn( *args )
165
        args = ' '.join( [ str( arg ) for arg in args ] )
166
        return fn( args )
167
168
    # Fix newfn's name and docstring
169
    setattr( newfn, '__name__', fn.__name__ )
170
    setattr( newfn, '__doc__', fn.__doc__ )
171
    return newfn
172
173 41badb96 Bob Lantz
info, output, warn, error, debug = (
174
    lg.info, lg.output, lg.warn, lg.error, lg.debug ) = [
175
        makeListCompatible( f ) for f in
176 6e4e79af Bob Lantz
            lg.info, lg.output, lg.warn, lg.error, lg.debug ]
177
178
setLogLevel = lg.setLogLevel