diff --git a/tests/test_spawning.py b/tests/test_spawning.py index 7508a39..220af3e 100644 --- a/tests/test_spawning.py +++ b/tests/test_spawning.py @@ -1,6 +1,7 @@ """ Spawning basics """ +import pytest import trio import tractor @@ -94,3 +95,42 @@ async def test_most_beautiful_word(start_method): # actor has completed its main task ``cellar_door``. print(await portal.result()) + + +async def check_loglevel(level): + assert tractor.current_actor().loglevel == level + log = tractor.log.get_logger() + # XXX using a level actually used inside tractor seems to trigger + # some kind of `logging` module bug FYI. + log.critical('yoyoyo') + + +def test_loglevel_propagated_to_subactor( + start_method, + capfd, + arb_addr, +): + if start_method == 'forkserver': + pytest.skip( + "a bug with `capfd` seems to make forkserver capture not work?") + + level = 'critical' + + async def main(): + async with tractor.open_nursery() as tn: + await tn.run_in_actor( + 'log_checker', + check_loglevel, + level=level, + ) + + tractor.run( + main, + name='arbiter', + loglevel=level, + start_method=start_method, + arbiter_addr=arb_addr, + ) + # ensure subactor spits log message on stderr + captured = capfd.readouterr() + assert 'yoyoyo' in captured.err diff --git a/tractor/__init__.py b/tractor/__init__.py index 5872abf..2f6e7d7 100644 --- a/tractor/__init__.py +++ b/tractor/__init__.py @@ -10,7 +10,7 @@ import typing import trio # type: ignore from trio import MultiError -from .log import get_console_log, get_logger, get_loglevel +from . import log from ._ipc import _connect_chan, Channel, Context from ._actor import ( Actor, _start_actor, Arbiter, get_arbiter, find_actor, wait_for_actor @@ -51,11 +51,14 @@ async def _main( ) -> typing.Any: """Async entry point for ``tractor``. """ - log = get_logger('tractor') + logger = log.get_logger('tractor') main = partial(async_fn, *args) arbiter_addr = (host, port) = arbiter_addr or ( _default_arbiter_host, _default_arbiter_port) - get_console_log(kwargs.get('loglevel', get_loglevel())) + loglevel = kwargs.get('loglevel', log.get_loglevel()) + if loglevel is not None: + log._default_loglevel = loglevel + log.get_console_log(loglevel) # make a temporary connection to see if an arbiter exists arbiter_found = False @@ -63,11 +66,11 @@ async def _main( async with _connect_chan(host, port): arbiter_found = True except OSError: - log.warning(f"No actor could be found @ {host}:{port}") + logger.warning(f"No actor could be found @ {host}:{port}") # create a local actor and start up its main routine/task if arbiter_found: # we were able to connect to an arbiter - log.info(f"Arbiter seems to exist @ {host}:{port}") + logger.info(f"Arbiter seems to exist @ {host}:{port}") actor = Actor( name or 'anonymous', arbiter_addr=arbiter_addr, diff --git a/tractor/_trionics.py b/tractor/_trionics.py index bf83179..7ebe37a 100644 --- a/tractor/_trionics.py +++ b/tractor/_trionics.py @@ -112,6 +112,7 @@ class ActorNursery: rpc_module_paths=[mod_path] + rpc_module_paths, bind_addr=bind_addr, statespace=statespace, + loglevel=loglevel, ) # this marks the actor to be cancelled after its portal result # is retreived, see ``wait()`` below.