PK#M7izaiologfields.py""" aiologfields: inject Task-context-fields into loggers Copyright 2017 Caleb Hattingh Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ __version__ = '2018.8.1' import asyncio import logging from types import SimpleNamespace from copy import deepcopy def _record_factory_factory(task_attr='logging_fields'): old_factory = logging.getLogRecordFactory() def _record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) try: t = asyncio.Task.current_task() except RuntimeError: pass # No loop in this thread. Don't worry about it. else: if t and hasattr(t, task_attr): attr = getattr(t, task_attr) assert isinstance(attr, SimpleNamespace) for k, v in attr.__dict__.items(): setattr(record, k, v) return record return _record_factory def set_log_record_factory_logging_fields(task_attr: str = 'logging_fields'): """ Modify the default LogRecord factory so that we automatically insert Task-local logging fields into the LoggingRecords. """ # NOTE: for some reason that I don't understand, get_event_loop MUST be # called before logging.basicConfig() is set up, or else asyncio # barfs due to something in our record factory above. No idea why yet. loop = asyncio.get_event_loop() # TODO: investigate logging.setLogRecordFactory(_record_factory_factory(task_attr=task_attr)) def _new_task_factory_factory(task_attr='logging_fields'): def _new_task_factory(loop, coro): """ Automatically add SimpleNamespace attribute to new task objects """ t = asyncio.Task(coro, loop=loop) setattr(t, task_attr, SimpleNamespace()) # If there are fields on the CURRENT task, copy them over to this # task. current_task = asyncio.Task.current_task() if current_task: current_attr = getattr(current_task, task_attr, None) if current_attr: # The NEW task's container attr = getattr(t, task_attr) attr.__dict__.update( # Using a full copy to make sure that we don't # inadvertently make objects live for longer than they # should, i.e. create references that never go away. deepcopy(current_attr.__dict__) ) return t return _new_task_factory def set_task_factory_logging_fields(task_attr='logging_fields'): loop = asyncio.get_event_loop() loop.set_task_factory(_new_task_factory_factory(task_attr)) def install(): set_log_record_factory_logging_fields() set_task_factory_logging_fields() def set_fields(task: asyncio.Task = None, task_attr='logging_fields', **kwargs): """ This is a convenience function, whose main benefit is hiding the ``t`` reference to the task. Call it like this: .. code-block:: python aiologfields.set_fields(correlation_id=12345) - Raises RuntimeError if called with no loop in the current thread. - The new task factory should already have been set up, typically via ``aiologfields.install()`` """ t = task or asyncio.Task.current_task() if t and hasattr(t, task_attr): attr = getattr(t, task_attr) assert isinstance(attr, SimpleNamespace) for name, value in kwargs.items(): setattr(attr, name, value) PKrKr6z0^S. ܔaH 9\^iz‚O[Wku+|b畁seqPo}i-ߏ>ÕlE\6!b l-҇BH aTZ0ɵtEVU+oEyQ - Rօdӥ**|j3[K ѾuSPsL7[ؙf8J 4廳yPzXDT@sI#BD FfqVGY`ˮ!z""H廜1w);S؃Kٱ&sWdi\~CY$1v4$YXk!Ԛ%$Kkm jor"U䖌 *GEQ5P-c7w-Eiݸ7=]낂g/O^KظBBPZ-L@#Dϫ.]#o×l4>?ydfƸ@㌵ XC~pN`?RoR"䑒b ʔGn(e̓PCDq =%3DVt9,-ˈq'g Z&I]]b1bi<h{ĉ$:OFKP$llN[Gcaraj'*i{: q~poW k%^w6jsM`Qjm̂K[ă L=T`xG@U\WթXϣ5 rH~bG;2u6W*y%eR5+(\/'ko[od(Hب~ŜՑbu:4%qXC]K:##wFה\Xb'r2PK!H}&aiologfields-2018.8.1.dist-info/RECORDKr0нg4ɢ ~`81V_)wLw^=O>yC!tl؉]Eo [~Xg0g;!e;o0(3ϙhH;2W{ak|y&? 7 }uZ0c ==UFl\Ŷ.)+i\:u3 |\[ yHඐY(}b&* ֵ.M / `PK#M7izaiologfields.pyPKrK