~ubuntu-branches/ubuntu/saucy/gunicorn/saucy-proposed

« back to all changes in this revision

Viewing changes to gunicorn/logging_config.py

  • Committer: Package Import Robot
  • Author(s): Chris Lamb
  • Date: 2013-07-04 17:28:14 UTC
  • mfrom: (1.1.13)
  • Revision ID: package-import@ubuntu.com-20130704172814-xnxr3mrqxeaihrys
Tags: 17.5-1
* New upstream release.
* Refresh and alter structure of 0001-drop-supplemental-groups.patch. Thanks
  to Randall Leeds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -
2
 
#
3
 
# This file is part of gunicorn released under the MIT license.
4
 
# See the NOTICE for more information.
5
 
#
6
 
# Copyright 2001-2005 by Vinay Sajip. All Rights Reserved.
7
 
#
8
 
 
9
 
"""
10
 
Configuration functions for the logging package for Python. The core package
11
 
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
12
 
by Apache's log4j system.
13
 
 
14
 
Should work under Python versions >= 1.5.2, except that source line
15
 
information is not available unless 'sys._getframe()' is.
16
 
 
17
 
Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved.
18
 
 
19
 
To use, simply 'import logging' and log away!
20
 
"""
21
 
 
22
 
import sys, logging, logging.handlers, string, socket, struct, os, traceback, types
23
 
 
24
 
try:
25
 
    import thread
26
 
    import threading
27
 
except ImportError:
28
 
    thread = None
29
 
 
30
 
from SocketServer import ThreadingTCPServer, StreamRequestHandler
31
 
 
32
 
 
33
 
DEFAULT_LOGGING_CONFIG_PORT = 9030
34
 
 
35
 
if sys.platform == "win32":
36
 
    RESET_ERROR = 10054   #WSAECONNRESET
37
 
else:
38
 
    RESET_ERROR = 104     #ECONNRESET
39
 
 
40
 
#
41
 
#   The following code implements a socket listener for on-the-fly
42
 
#   reconfiguration of logging.
43
 
#
44
 
#   _listener holds the server object doing the listening
45
 
_listener = None
46
 
 
47
 
def fileConfig(fname, defaults=None):
48
 
    """
49
 
    Read the logging configuration from a ConfigParser-format file.
50
 
 
51
 
    This can be called several times from an application, allowing an end user
52
 
    the ability to select from various pre-canned configurations (if the
53
 
    developer provides a mechanism to present the choices and load the chosen
54
 
    configuration).
55
 
    In versions of ConfigParser which have the readfp method [typically
56
 
    shipped in 2.x versions of Python], you can pass in a file-like object
57
 
    rather than a filename, in which case the file-like object will be read
58
 
    using readfp.
59
 
    """
60
 
    import ConfigParser
61
 
 
62
 
    cp = ConfigParser.ConfigParser(defaults)
63
 
    if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
64
 
        cp.readfp(fname)
65
 
    else:
66
 
        cp.read(fname)
67
 
 
68
 
    formatters = _create_formatters(cp)
69
 
 
70
 
    # critical section
71
 
    logging._acquireLock()
72
 
    try:
73
 
        logging._handlers.clear()
74
 
        if hasattr(logging, '_handlerList'):
75
 
            del logging._handlerList[:]
76
 
        # Handlers add themselves to logging._handlers
77
 
        handlers = _install_handlers(cp, formatters)
78
 
        _install_loggers(cp, handlers)
79
 
    finally:
80
 
        logging._releaseLock()
81
 
 
82
 
 
83
 
def _resolve(name):
84
 
    """Resolve a dotted name to a global object."""
85
 
    name = string.split(name, '.')
86
 
    used = name.pop(0)
87
 
    found = __import__(used)
88
 
    for n in name:
89
 
        used = used + '.' + n
90
 
        try:
91
 
            found = getattr(found, n)
92
 
        except AttributeError:
93
 
            __import__(used)
94
 
            found = getattr(found, n)
95
 
    return found
96
 
 
97
 
 
98
 
def _create_formatters(cp):
99
 
    """Create and return formatters"""
100
 
    flist = cp.get("formatters", "keys")
101
 
    if not len(flist):
102
 
        return {}
103
 
    flist = string.split(flist, ",")
104
 
    formatters = {}
105
 
    for form in flist:
106
 
        form = string.strip(form)
107
 
        sectname = "formatter_%s" % form
108
 
        opts = cp.options(sectname)
109
 
        if "format" in opts:
110
 
            fs = cp.get(sectname, "format", 1)
111
 
        else:
112
 
            fs = None
113
 
        if "datefmt" in opts:
114
 
            dfs = cp.get(sectname, "datefmt", 1)
115
 
        else:
116
 
            dfs = None
117
 
        c = logging.Formatter
118
 
        if "class" in opts:
119
 
            class_name = cp.get(sectname, "class")
120
 
            if class_name:
121
 
                c = _resolve(class_name)
122
 
        f = c(fs, dfs)
123
 
        formatters[form] = f
124
 
    return formatters
125
 
 
126
 
 
127
 
def _install_handlers(cp, formatters):
128
 
    """Install and return handlers"""
129
 
    hlist = cp.get("handlers", "keys")
130
 
    if not len(hlist):
131
 
        return {}
132
 
    hlist = string.split(hlist, ",")
133
 
    handlers = {}
134
 
    fixups = [] #for inter-handler references
135
 
    for hand in hlist:
136
 
        hand = string.strip(hand)
137
 
        sectname = "handler_%s" % hand
138
 
        klass = cp.get(sectname, "class")
139
 
        opts = cp.options(sectname)
140
 
        if "formatter" in opts:
141
 
            fmt = cp.get(sectname, "formatter")
142
 
        else:
143
 
            fmt = ""
144
 
        try:
145
 
            klass = eval(klass, vars(logging))
146
 
        except (AttributeError, NameError):
147
 
            klass = _resolve(klass)
148
 
        args = cp.get(sectname, "args")
149
 
        args = eval(args, vars(logging))
150
 
        h = apply(klass, args)
151
 
        if "level" in opts:
152
 
            level = cp.get(sectname, "level")
153
 
            h.setLevel(logging._levelNames[level])
154
 
        if len(fmt):
155
 
            h.setFormatter(formatters[fmt])
156
 
        #temporary hack for FileHandler and MemoryHandler.
157
 
        if klass == logging.handlers.MemoryHandler:
158
 
            if "target" in opts:
159
 
                target = cp.get(sectname,"target")
160
 
            else:
161
 
                target = ""
162
 
            if len(target): #the target handler may not be loaded yet, so keep for later...
163
 
                fixups.append((h, target))
164
 
        handlers[hand] = h
165
 
    #now all handlers are loaded, fixup inter-handler references...
166
 
    for h, t in fixups:
167
 
        h.setTarget(handlers[t])
168
 
    return handlers
169
 
 
170
 
 
171
 
def _install_loggers(cp, handlers):
172
 
    """Create and install loggers"""
173
 
 
174
 
    # configure the root first
175
 
    llist = cp.get("loggers", "keys")
176
 
    llist = string.split(llist, ",")
177
 
    llist = map(lambda x: string.strip(x), llist)
178
 
    llist.remove("root")
179
 
    sectname = "logger_root"
180
 
    root = logging.root
181
 
    log = root
182
 
    opts = cp.options(sectname)
183
 
    if "level" in opts:
184
 
        level = cp.get(sectname, "level")
185
 
        log.setLevel(logging._levelNames[level])
186
 
    for h in root.handlers[:]:
187
 
        root.removeHandler(h)
188
 
    hlist = cp.get(sectname, "handlers")
189
 
    if len(hlist):
190
 
        hlist = string.split(hlist, ",")
191
 
        for hand in hlist:
192
 
            log.addHandler(handlers[string.strip(hand)])
193
 
 
194
 
    #and now the others...
195
 
    #we don't want to lose the existing loggers,
196
 
    #since other threads may have pointers to them.
197
 
    #existing is set to contain all existing loggers,
198
 
    #and as we go through the new configuration we
199
 
    #remove any which are configured. At the end,
200
 
    #what's left in existing is the set of loggers
201
 
    #which were in the previous configuration but
202
 
    #which are not in the new configuration.
203
 
    existing = root.manager.loggerDict.keys()
204
 
    #now set up the new ones...
205
 
    for log in llist:
206
 
        sectname = "logger_%s" % log
207
 
        qn = cp.get(sectname, "qualname")
208
 
        opts = cp.options(sectname)
209
 
        if "propagate" in opts:
210
 
            propagate = cp.getint(sectname, "propagate")
211
 
        else:
212
 
            propagate = 1
213
 
        logger = logging.getLogger(qn)
214
 
        if qn in existing:
215
 
            existing.remove(qn)
216
 
        if "level" in opts:
217
 
            level = cp.get(sectname, "level")
218
 
            logger.setLevel(logging._levelNames[level])
219
 
        for h in logger.handlers[:]:
220
 
            logger.removeHandler(h)
221
 
        logger.propagate = propagate
222
 
        logger.disabled = 0
223
 
        hlist = cp.get(sectname, "handlers")
224
 
        if len(hlist):
225
 
            hlist = string.split(hlist, ",")
226
 
            for hand in hlist:
227
 
                logger.addHandler(handlers[string.strip(hand)])
228
 
 
229
 
    #Disable any old loggers. There's no point deleting
230
 
    #them as other threads may continue to hold references
231
 
    #and by disabling them, you stop them doing any logging.
232
 
    for log in existing:
233
 
        root.manager.loggerDict[log].disabled = 1
234
 
 
235
 
 
236
 
def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
237
 
    """
238
 
    Start up a socket server on the specified port, and listen for new
239
 
    configurations.
240
 
 
241
 
    These will be sent as a file suitable for processing by fileConfig().
242
 
    Returns a Thread object on which you can call start() to start the server,
243
 
    and which you can join() when appropriate. To stop the server, call
244
 
    stopListening().
245
 
    """
246
 
    if not thread:
247
 
        raise NotImplementedError, "listen() needs threading to work"
248
 
 
249
 
    class ConfigStreamHandler(StreamRequestHandler):
250
 
        """
251
 
        Handler for a logging configuration request.
252
 
 
253
 
        It expects a completely new logging configuration and uses fileConfig
254
 
        to install it.
255
 
        """
256
 
        def handle(self):
257
 
            """
258
 
            Handle a request.
259
 
 
260
 
            Each request is expected to be a 4-byte length, packed using
261
 
            struct.pack(">L", n), followed by the config file.
262
 
            Uses fileConfig() to do the grunt work.
263
 
            """
264
 
            import tempfile
265
 
            try:
266
 
                conn = self.connection
267
 
                chunk = conn.recv(4)
268
 
                if len(chunk) == 4:
269
 
                    slen = struct.unpack(">L", chunk)[0]
270
 
                    chunk = self.connection.recv(slen)
271
 
                    while len(chunk) < slen:
272
 
                        chunk = chunk + conn.recv(slen - len(chunk))
273
 
                    #Apply new configuration. We'd like to be able to
274
 
                    #create a StringIO and pass that in, but unfortunately
275
 
                    #1.5.2 ConfigParser does not support reading file
276
 
                    #objects, only actual files. So we create a temporary
277
 
                    #file and remove it later.
278
 
                    file = tempfile.mktemp(".ini")
279
 
                    f = open(file, "w")
280
 
                    f.write(chunk)
281
 
                    f.close()
282
 
                    try:
283
 
                        fileConfig(file)
284
 
                    except (KeyboardInterrupt, SystemExit):
285
 
                        raise
286
 
                    except:
287
 
                        traceback.print_exc()
288
 
                    os.remove(file)
289
 
            except socket.error, e:
290
 
                if type(e.args) != types.TupleType:
291
 
                    raise
292
 
                else:
293
 
                    errcode = e.args[0]
294
 
                    if errcode != RESET_ERROR:
295
 
                        raise
296
 
 
297
 
    class ConfigSocketReceiver(ThreadingTCPServer):
298
 
        """
299
 
        A simple TCP socket-based logging config receiver.
300
 
        """
301
 
 
302
 
        allow_reuse_address = 1
303
 
 
304
 
        def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT,
305
 
                     handler=None):
306
 
            ThreadingTCPServer.__init__(self, (host, port), handler)
307
 
            logging._acquireLock()
308
 
            self.abort = 0
309
 
            logging._releaseLock()
310
 
            self.timeout = 1
311
 
 
312
 
        def serve_until_stopped(self):
313
 
            import select
314
 
            abort = 0
315
 
            while not abort:
316
 
                rd, wr, ex = select.select([self.socket.fileno()],
317
 
                                           [], [],
318
 
                                           self.timeout)
319
 
                if rd:
320
 
                    self.handle_request()
321
 
                logging._acquireLock()
322
 
                abort = self.abort
323
 
                logging._releaseLock()
324
 
 
325
 
    def serve(rcvr, hdlr, port):
326
 
        server = rcvr(port=port, handler=hdlr)
327
 
        global _listener
328
 
        logging._acquireLock()
329
 
        _listener = server
330
 
        logging._releaseLock()
331
 
        server.serve_until_stopped()
332
 
 
333
 
    return threading.Thread(target=serve,
334
 
                            args=(ConfigSocketReceiver,
335
 
                                  ConfigStreamHandler, port))
336
 
 
337
 
def stopListening():
338
 
    """
339
 
    Stop the listening server which was created with a call to listen().
340
 
    """
341
 
    global _listener
342
 
    if _listener:
343
 
        logging._acquireLock()
344
 
        _listener.abort = 1
345
 
        _listener = None
346
 
        logging._releaseLock()