@@ -807,6 +807,106 @@ the basis for code meeting your own specific requirements::
807807 if __name__ == '__main__':
808808 main()
809809
810+ A variant of the above script keeps the logging in the main process, in a
811+ separate thread::
812+
813+ import logging
814+ import logging.config
815+ import logging.handlers
816+ from multiprocessing import Process, Queue
817+ import random
818+ import threading
819+ import time
820+
821+ def logger_thread(q):
822+ while True:
823+ record = q.get()
824+ if record is None:
825+ break
826+ logger = logging.getLogger(record.name)
827+ logger.handle(record)
828+
829+
830+ def worker_process(q):
831+ qh = logging.handlers.QueueHandler(q)
832+ root = logging.getLogger()
833+ root.setLevel(logging.DEBUG)
834+ root.addHandler(qh)
835+ levels = [logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
836+ logging.CRITICAL]
837+ loggers = ['foo', 'foo.bar', 'foo.bar.baz',
838+ 'spam', 'spam.ham', 'spam.ham.eggs']
839+ for i in range(100):
840+ lvl = random.choice(levels)
841+ logger = logging.getLogger(random.choice(loggers))
842+ logger.log(lvl, 'Message no. %d', i)
843+
844+ if __name__ == '__main__':
845+ q = Queue()
846+ d = {
847+ 'version': 1,
848+ 'formatters': {
849+ 'detailed': {
850+ 'class': 'logging.Formatter',
851+ 'format': '%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s'
852+ }
853+ },
854+ 'handlers': {
855+ 'console': {
856+ 'class': 'logging.StreamHandler',
857+ 'level': 'INFO',
858+ },
859+ 'file': {
860+ 'class': 'logging.FileHandler',
861+ 'filename': 'mplog.log',
862+ 'mode': 'w',
863+ 'formatter': 'detailed',
864+ },
865+ 'foofile': {
866+ 'class': 'logging.FileHandler',
867+ 'filename': 'mplog-foo.log',
868+ 'mode': 'w',
869+ 'formatter': 'detailed',
870+ },
871+ 'errors': {
872+ 'class': 'logging.FileHandler',
873+ 'filename': 'mplog-errors.log',
874+ 'mode': 'w',
875+ 'level': 'ERROR',
876+ 'formatter': 'detailed',
877+ },
878+ },
879+ 'loggers': {
880+ 'foo': {
881+ 'handlers' : ['foofile']
882+ }
883+ },
884+ 'root': {
885+ 'level': 'DEBUG',
886+ 'handlers': ['console', 'file', 'errors']
887+ },
888+ }
889+ workers = []
890+ for i in range(5):
891+ wp = Process(target=worker_process, name='worker %d' % (i + 1), args=(q,))
892+ workers.append(wp)
893+ wp.start()
894+ logging.config.dictConfig(d)
895+ lp = threading.Thread(target=logger_thread, args=(q,))
896+ lp.start()
897+ # At this point, the main process could do some useful work of its own
898+ # Once it's done that, it can wait for the workers to terminate...
899+ for wp in workers:
900+ wp.join()
901+ # And now tell the logging thread to finish up, too
902+ q.put(None)
903+ lp.join()
904+
905+ This variant shows how you can e.g. apply configuration for particular loggers
906+ - e.g. the ``foo `` logger has a special handler which stores all events in the
907+ ``foo `` subsystem in a file ``mplog-foo.log ``. This will be used by the logging
908+ machinery in the main process (even though the logging events are generated in
909+ the worker processes) to direct the messages to the appropriate destinations.
810910
811911Using file rotation
812912-------------------
0 commit comments