views:

171

answers:

2

I have a pylons application where, under certain cirumstances I want to spawn multiple worker threads to process items in a queue. Right now we aren't making use of a ThreadPool (would be ideal, but we'll add that in later). The main problem is that the worker threads logging does not get written to the log files.

When I run the code outside of the pylons application the logging works fine. So I think its something to do with the pylons log handler but not sure what.

Here is a basic example of the code (trimmed down):

import logging
log = logging.getLogger(__name__)

import sys
from Queue import Queue
from threading import Thread, activeCount

def run(input, worker, args = None, simulteneousWorkerLimit = None):
    queue = Queue()
    threads = []

    if args is not None:
        if len(args) > 0:
            args = list(args)
            args = [worker, queue] + args
            args = tuple(args)
        else:
            args = (worker, queue)

    # start threads
    for i in range(4):
        t = Thread(target = __thread, args = args)
        t.daemon = True
        t.start()
        threads.append(t)

    # add ThreadTermSignal
    inputData = list(input)
    inputData.extend([ThreadTermSignal] * 4)

    # put in the queue
    for data in inputData:
        queue.put(data)

    # block until all contents are downloaded
    queue.join()
    log.critical("** A log line that appears fine **")

    del queue
    for thread in threads:
        del thread
    del threads

class ThreadTermSignal(object):
    pass

def __thread(worker, queue, *args):
    try:
        while True:
            data = queue.get()
            if data is ThreadTermSignal:
                sys.exit()
            try:
                log.critical("** I don't appear when run under pylons **")
            finally:
                queue.task_done()
    except SystemExit:
        queue.task_done()
        pass

Take note, that the log lin within the RUN method will show up in the log files, but the log line within the worker method (which is run in a spawned thread), does not appear. Any help would be appreciated. Thanks

** EDIT: I should mention that I tried passing in the "log" variable to the worker thread as well as redefining a new "log" variable within the thread and neither worked.

** EDIT: Adding the configuration used for the pylons application (which comes out of the INI file). So the snippet below is from the INI file.

[loggers]
keys = root

[handlers]
keys = wsgierrors

[formatters]
keys = generic

[logger_root]
level = WARNING
handlers = wsgierrors

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = WARNING
formatter = generic

[handler_wsgierrors]
class = pylons.log.WSGIErrorsHandler
args = ()
level = WARNING
format = generic
A: 

You can try to pass a log variable to the thread through the arguments(args).

Andrey Gubarev
Yah, I tried that before posting. To my surprise it didn't work.
TimM
There's no need to pass loggers around. They are pseudo-singletons: every call to `getLogger` with a given name always returns the same instance.
Vinay Sajip
+1  A: 

One thing to note about logging is that if an exception occurs while emitting a log event (for whatever reason) the exception is typically swallowed, and not allowed to potentially bring down an application just because of a logging error. (It depends on the handlers used and the value of logging.raiseExceptions). So there are a couple of things to check:

  • That your formatting of log messages is dead simple, perhaps just using %(message)s until you find the problem.
  • Check that Pylons hasn't turned logging off or messed with the handlers for whatever reason. You haven't posted your logging initialization code so I'm not sure what handlers, etc. you're using. You can print log.getEffectiveLevel() to see if logging verbosity has been turned right down (unlikely for CRITICAL, but you never know).

If you put in print statements alongside your log statements, do they produce output how you'd expect them to?

Update: I'm aware of the restriction about mod_wsgi and printing, but that only applies to sys.stdout. You can e.g.

print >> sys.stderr, some_data

or

print >> open('/tmp/somefile', 'a'), some_data

without any problem.

Also: you should be aware that a call to logging.config.fileConfig() (which is presumably how the configuration you described is implemented) disables any existing loggers unless they are explicitly named in, or are descendants of loggers explicitly named in, the configuration file. While this might seem odd, it's because a configuration is intended to replace any existing configuration rather than augment it; and since threads might be pointing to existing loggers, they're disabled rather than deleted. You can check a logger's disabled attribute to see if fileConfig() has disabled the logger - that could be your problem.

Vinay Sajip
Thanks for the response. The logger initialization is just the default that comes with a pylons/wsgi application. The log level is set to WARN. The base logger itself works fine because any and all logging in the main threads come out in the log file. The problem is when a new worker thread is spawned the logs no longer show up. I've updated the message above to show the log handler configuration defined in the INI files.
TimM
Also, "print" statements don't work in WSGI applications. You get any error anytime you try to use a print line. Here is the error details:<type 'exceptions.IOError'>: sys.stdout access restricted by mod_wsgi
TimM
Print statements directed to sys.stderr work fine under mod_wsgi. There is no restriction on sys.stderr. If you use mod_wsgi 3.X, there is no restriction on sys.stdout error either as the attempt to encourage people to write portable WSGI applications and not write to sys.stdout or play with sys.stdin was abandoned because rather than fix code people would just complain that mod_wsgi was broken and not even read the documentation to try and understand why such a restriction existed in the first place. Read 'http://blog.dscpl.com.au/2009/04/wsgi-and-printing-to-standard-output.html'.
Graham Dumpleton
Also be aware that Paste server does magic logger initialisation that isn't done automatically by any other WSGI hosting mechanism. When using mod_wsgi you must duplicate that initialisation code else logger will not even log stuff to stderr in the first place. See comments at end of 'http://code.google.com/p/modwsgi/wiki/IntegrationWithPylons'.
Graham Dumpleton
Updated my update to clarify what Graham said about mod_wsgi and printing to sys.stderr. Sorry about the mistake - I don't usually print to either sys.stdout/sys.stderr as I use `logging` instead!
Vinay Sajip
Thanks for the info fellas. After reading more of this: http://pylonsbook.com/en/1.1/logging.html. The doc mentions the WSGIErrorsHandler and states that log messages will be silently lost if attempted outside of a request. So even though my thread spawning is initiated from a request, its seems like it doesn't have access or knowledge of the request.I did add a FileHandler to my logging configuration and it recieved all the log messages from within the thread. So I can always fall back on this if I can't find a nice way to make the new threads aware of the pylons request.
TimM