Skip to content

Commit

Permalink
Issue #131: maintain a thread-local logger stack (RC fix)
Browse files Browse the repository at this point in the history
Originally `byexample` used a global stack of loggers to support
contextual logging.

This works fine when we work with subprocesses because each one will
have its own stack. But with threads, all of them will see the same
shared stack.

This RC will confuse the logging system: while one thread enter in
`'byexample.exec'` context it may end logging as `'byexample.init'` because
other thread entered in that context.

Using a thread-local logger stack fixes the RC. Now the
`init_thread_specific_log_system` function needs to be called at the begin
of each thread to initialize it.
  • Loading branch information
eldipa committed Mar 10, 2021
1 parent 730c0c1 commit 6829f2e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
3 changes: 2 additions & 1 deletion byexample/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from queue import Queue

import signal, contextlib
from .log import clog, CHAT
from .log import clog, CHAT, init_thread_specific_log_system
from .init import init_worker


Expand All @@ -23,6 +23,7 @@ def worker(func, input, output, cfg):
After receiving a None, close the <output> queue.
'''
init_thread_specific_log_system()
harvester, executor = init_worker(cfg)
for item in iter(input.get, None):
output.put(func(item, harvester, executor, cfg['dry']))
Expand Down
40 changes: 36 additions & 4 deletions byexample/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from .common import colored, highlight_syntax, indent
from .log_level import TRACE, DEBUG, CHAT, INFO, NOTE, WARNING, ERROR, CRITICAL
import functools
import functools, threading


class XFormatter(Formatter):
Expand Down Expand Up @@ -184,7 +184,30 @@ def exception(self, msg, *args, **kwargs):
)


_logger_stack = []
class _LoggerLocalStack(threading.local):
def __init__(self):
self.stack = []

def __len__(self):
return len(self.stack)

def append(self, item):
return self.stack.append(item)

def pop(self):
return self.stack.pop()

def __getitem__(self, ix):
return self.stack[ix]

def __setitem__(self, ix, v):
self.stack[ix] = v

def __delitem__(self, ix):
del self.stack[ix]


_logger_stack = _LoggerLocalStack()


def clog():
Expand Down Expand Up @@ -286,10 +309,12 @@ def init_log_system(level=NOTE, use_colors=False):
rlog.xstream_handler = ch
rlog.bg_queue_listener = ql

# Set up the global logger.
# Set up the global logger (for this thread).
# Activate and deactivate sub loggers using log_context
# decorator on the top level functions
_logger_stack.append(rlog)
#
# Other threads will have to call this function too
init_thread_specific_log_system()

assert level is not None
assert use_colors is not None
Expand All @@ -300,6 +325,13 @@ def init_log_system(level=NOTE, use_colors=False):
rlog.bg_queue_listener.start()


def init_thread_specific_log_system():
global _logger_stack

rlog = getLogger(name='byexample') # root
_logger_stack.append(rlog)


def configure_log_system(default_level=None, use_colors=None, concerns=None):
rlog = getLogger(name='byexample') # root
if default_level is not None:
Expand Down

0 comments on commit 6829f2e

Please sign in to comment.