~ubuntu-branches/ubuntu/precise/pyzmq/precise

« back to all changes in this revision

Viewing changes to zmq/log/handlers.py

  • Committer: Bazaar Package Importer
  • Author(s): Piotr Ożarowski
  • Date: 2011-02-15 09:08:36 UTC
  • mfrom: (2.1.2 experimental)
  • Revision ID: james.westby@ubuntu.com-20110215090836-phh4slym1g6muucn
Tags: 2.0.10.1-2
* Team upload.
* Upload to unstable
* Add Breaks: ${python:Breaks}

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
"""pyzmq logging handlers.
 
3
 
 
4
This mainly defines the PUBHandler object for publishing logging messages over
 
5
a zmq.PUB socket.
 
6
 
 
7
The PUBHandler can be used with the regular logging module, as in::
 
8
 
 
9
    >>> import logging
 
10
    >>> handler = PUBHandler('tcp://127.0.0.1:12345')
 
11
    >>> handler.root_topic = 'foo'
 
12
    >>> logger = logging.getLogger('foobar')
 
13
    >>> logger.addHandler(logging.DEBUG, handler)
 
14
 
 
15
After this point, all messages logged by ``logger`` will be published on the
 
16
PUB socket.
 
17
 
 
18
Code adapted from StarCluster:
 
19
 
 
20
    http://github.com/jtriley/StarCluster/blob/master/starcluster/logger.py
 
21
 
 
22
Authors
 
23
-------
 
24
* Min RK
 
25
"""
 
26
 
 
27
#
 
28
#    Copyright (c) 2010 Min Ragan-Kelley
 
29
#
 
30
#    This file is part of pyzmq.
 
31
#
 
32
#    pyzmq is free software; you can redistribute it and/or modify it under
 
33
#    the terms of the Lesser GNU General Public License as published by
 
34
#    the Free Software Foundation; either version 3 of the License, or
 
35
#    (at your option) any later version.
 
36
#
 
37
#    pyzmq is distributed in the hope that it will be useful,
 
38
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
39
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
40
#    Lesser GNU General Public License for more details.
 
41
#
 
42
#    You should have received a copy of the Lesser GNU General Public License
 
43
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
44
#
 
45
 
 
46
#-----------------------------------------------------------------------------
 
47
# Imports
 
48
#-----------------------------------------------------------------------------
 
49
 
 
50
import logging
 
51
from logging import INFO, DEBUG, WARN, ERROR, FATAL
 
52
 
 
53
import zmq
 
54
from zmq.utils.strtypes import bytes,unicode
 
55
 
 
56
#-----------------------------------------------------------------------------
 
57
# Code
 
58
#-----------------------------------------------------------------------------
 
59
 
 
60
TOPIC_DELIM="::" # delimiter for splitting topics on the receiving end.
 
61
 
 
62
 
 
63
class PUBHandler(logging.Handler):
 
64
    """A basic logging handler that emits log messages through a PUB socket.
 
65
 
 
66
    Takes a PUB socket already bound to interfaces or an interface to bind to.
 
67
 
 
68
    Example::
 
69
 
 
70
        sock = context.socket(zmq.PUB)
 
71
        sock.bind('inproc://log')
 
72
        handler = PUBHandler(sock)
 
73
 
 
74
    Or::
 
75
 
 
76
        handler = PUBHandler('inproc://loc')
 
77
 
 
78
    These are equivalent.
 
79
 
 
80
    Log messages handled by this handler are broadcast with ZMQ topics
 
81
    ``this.root_topic`` comes first, followed by the log level
 
82
    (DEBUG,INFO,etc.), followed by any additional subtopics specified in the
 
83
    message by: log.debug("subtopic.subsub::the real message")
 
84
    """
 
85
    root_topic="".encode()
 
86
    socket = None
 
87
    
 
88
    formatters = {
 
89
        logging.DEBUG: logging.Formatter(
 
90
        "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n"),
 
91
        logging.INFO: logging.Formatter("%(message)s\n"),
 
92
        logging.WARN: logging.Formatter(
 
93
        "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n"),
 
94
        logging.ERROR: logging.Formatter(
 
95
        "%(levelname)s %(filename)s:%(lineno)d - %(message)s - %(exc_info)s\n"),
 
96
        logging.CRITICAL: logging.Formatter(
 
97
        "%(levelname)s %(filename)s:%(lineno)d - %(message)s\n")}
 
98
    
 
99
    def __init__(self, interface_or_socket, context=None):
 
100
        logging.Handler.__init__(self)
 
101
        if isinstance(interface_or_socket, zmq.Socket):
 
102
            self.socket = interface_or_socket
 
103
            self.ctx = self.socket.context
 
104
        else:
 
105
            self.ctx = context or zmq.Context()
 
106
            self.socket = self.ctx.socket(zmq.PUB)
 
107
            self.socket.bind(interface_or_socket)
 
108
 
 
109
    def format(self,record):
 
110
        """Format a record."""
 
111
        return self.formatters[record.levelno].format(record)
 
112
 
 
113
    def emit(self, record):
 
114
        """Emit a log message on my socket."""
 
115
        try:
 
116
            topic, record.msg = record.msg.split(TOPIC_DELIM,1)
 
117
            topic = topic.encode()
 
118
        except:
 
119
            topic = "".encode()
 
120
        try:
 
121
            msg = self.format(record).encode()
 
122
        except (KeyboardInterrupt, SystemExit):
 
123
            raise
 
124
        except:
 
125
            self.handleError(record)
 
126
        topic_list = []
 
127
 
 
128
        if self.root_topic:
 
129
            topic_list.append(self.root_topic)
 
130
 
 
131
        topic_list.append(record.levelname.encode())
 
132
 
 
133
        if topic:
 
134
            topic_list.append(topic)
 
135
 
 
136
        topic = '.'.encode().join(topic_list)
 
137
 
 
138
        # map str, since sometimes we get unicode, and zmq can't deal with it
 
139
        self.socket.send_multipart([topic,msg])
 
140
 
 
141
 
 
142
class TopicLogger(logging.Logger):
 
143
    """A simple wrapper that takes an additional argument to log methods.
 
144
 
 
145
    All the regular methods exist, but instead of one msg argument, two
 
146
    arguments: topic, msg are passed.
 
147
 
 
148
    That is::
 
149
 
 
150
        logger.debug('msg')
 
151
 
 
152
    Would become::
 
153
 
 
154
        logger.debug('topic.sub', 'msg')
 
155
    """
 
156
    def log(self, level, topic, msg, *args, **kwargs):
 
157
        """Log 'msg % args' with level and topic.
 
158
 
 
159
        To pass exception information, use the keyword argument exc_info
 
160
        with a True value::
 
161
 
 
162
            logger.log(level, "zmq.fun", "We have a %s", 
 
163
                    "mysterious problem", exc_info=1)
 
164
        """
 
165
        logging.Logger.log(self, level, '%s::%s'%(topic,msg), *args, **kwargs)
 
166
 
 
167
# Generate the methods of TopicLogger, since they are just adding a
 
168
# topic prefix to a message.
 
169
for name in "debug warn warning error critical fatal".split():
 
170
    meth = getattr(logging.Logger,name)
 
171
    setattr(TopicLogger, name, 
 
172
            lambda self, level, topic, msg, *args, **kwargs: 
 
173
                meth(self, level, topic+TOPIC_DELIM+msg,*args, **kwargs))
 
174