tyler46 / nameko-structlog

Nameko extension exposing a structlog dependency injector
Other
10 stars 3 forks source link

Failing ErrorHandler (DependencyProvider with `worker_result`) #2

Closed vlcinsky closed 4 years ago

vlcinsky commented 4 years ago

Question: is this library compatible with the latest nameko==3.0.0rc8?

When I added it to my project, it fails:

$ nameko run --config config.yaml svc_alert_masapi.sqs_service                                                                 
Traceback (most recent call last):
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/bin/nameko", line 11, in <module>                                                                                          
    sys.exit(cli())
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/click/core.py", line 764, in __call__                                                          
    return self.main(*args, **kwargs)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/click/core.py", line 717, in main                                                              
    rv = self.invoke(ctx)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/click/core.py", line 1137, in invoke                                                           
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/click/core.py", line 956, in invoke                                                            
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/click/core.py", line 555, in invoke                                                            
    return callback(*args, **kwargs)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/cli/__init__.py", line 67, in run                                                       
    main(services, backdoor_port)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/cli/run.py", line 101, in main                                                          
    run(services, backdoor_port=backdoor_port)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/cli/run.py", line 52, in run                                                            
    service_runner.add_service(service_cls)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/runners.py", line 50, in add_service                                                    
    container = self.container_cls(cls)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/containers.py", line 142, in __init__                                                   
    bound = dependency.bind(self.interface, attr_name)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/extensions.py", line 142, in bind                                                       
    instance = super(DependencyProvider, self).bind(container)
  File "/home/javl/devel/croads/gitlab/datahub/app_alert_masapi/svc_alert_masapi/.venv/lib/python3.6/site-packages/nameko/extensions.py", line 97, in bind                                                        
    setattr(instance, name, ext.bind(container))
TypeError: bind() missing 1 required positional argument: 'attr_name'
vlcinsky commented 4 years ago

I can reproduce the error on both nameko==2.12.0 as well as on nameko=3.0.0rc8

With service.py

from nameko.timer import timer

from nameko.extensions import DependencyProvider
from nameko_structlog import StructlogDependency

class ErrorHandler(DependencyProvider):
    log = StructlogDependency()

    def worker_result(self, worker_ctx, res, exc_info):
        pass

class MyService:
    name = "demo"

    handler = ErrorHandler()
    log = StructlogDependency()

    @timer(interval=3)
    def my_method(self):
        self.log.info("info for you -------", name=self.name)

it fails (with nameko==2.x.

$ nameko run --config config.yaml service
Traceback (most recent call last):
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/bin/nameko", line 10, in <module>
    sys.exit(main())
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/cli/main.py", line 112, in main                                                                         
    args.main(args)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/cli/commands.py", line 110, in main                                                                     
    main(args)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/cli/run.py", line 184, in main                                                                          
    run(services, config, backdoor_port=args.backdoor_port)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/cli/run.py", line 119, in run                                                                           
    service_runner.add_service(service_cls)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/runners.py", line 51, in add_service                                                                    
    container = self.container_cls(cls, self.config)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/containers.py", line 151, in __init__                                                                   
    bound = dependency.bind(self.interface, attr_name)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/extensions.py", line 147, in bind                                                                       
    instance = super(DependencyProvider, self).bind(container)
  File "/home/javl/sandbox/nameko-structlog/nameko-structlog/.tox/py36/lib/python3.6/site-packages/nameko/extensions.py", line 102, in bind                                                                       
    setattr(instance, name, ext.bind(container))
TypeError: bind() missing 1 required positional argument: 'attr_name'

Hint:

vlcinsky commented 4 years ago

Here is workaround (tested with nameko==2.x and 3.0.0rc8).

service.py


from nameko.extensions import DependencyProvider
from nameko_structlog import StructlogDependency

class ErrorHandler(DependencyProvider):
    # log = StructlogDependency()

    def worker_result(self, worker_ctx, res, exc_info):
        if exc_info is None:
            return
        try:
            logger = worker_ctx.service.log
            logger.exception("error", exc_info=exc_info)
        except AttributeError:
            import logging
            svc_name = worker_ctx.service.name

            logger = logging.getLogger(svc_name)
            logger.warning(
                f"service {svc_name} missing `log` StructlogDependency\nevery service deserves it"
            )
            logger.error("exception", exc_info=exc_info)

class MyService:
    name = "demo"

    handler = ErrorHandler()
    log = StructlogDependency()

    @timer(interval=3)
    def my_method(self):
        self.log.info("info for you -------", name=self.name)
        # print(f"info for you -------{self.name}")
        1 / 0
vlcinsky commented 4 years ago

Workaround can be optimized as logger and error handler can be merged into one dependency.

service.py:

from nameko.timer import timer

from nameko_structlog import StructlogDependency

class StructlogAndErrorHandler(StructlogDependency):
    def worker_result(self, worker_ctx, res, exc_info):
        if exc_info is None:
            return
        logger = getattr(worker_ctx.service, self.attr_name)
        logger.exception("error", exc_info=exc_info)

class MyService:
    name = "demo"

    log = StructlogAndErrorHandler()

    @timer(interval=3)
    def my_method(self):
        self.log.info("info for you -------", name=self.name)
        1 / 0
vlcinsky commented 4 years ago

The problem is trying to declare dependency log on ErrorHandler dependency provider class.

This is definitely not the case as with services and should not be used this way.

My final "workaround" is in fact the way it shall work.