Рубрики
Без рубрики

Три способа автоматически добавления функций и методов вызова журнала для кода Python

Старый путь, вручную вручную регистрацию импорта. GetLogger (__ имя__) def f … Помечено с Python, регистрацию.

Старый путь, вручную

import logging
logger = logging.getLogger(__name__)

def func(*args, **kwargs):
    logger.info(f'Call func with {args, kwargs}')

func(1, 2, 3, a=True, b='arg')
INFO:__main__:Call func with ((1, 2, 3), {'a': True, 'b': 'arg'})

1. Используйте декораторы

Для функции

def func_logger(func):

    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        logger.info(f'Call func {func.__name__} with {args, kwargs} returns {ret}')
        return ret

    return inner


@func_logger
def add(a, b):
    return a + b

add(1,2)
add(1, b=2)
add(a=1, b=2)
INFO:__main__:Call func add with ((1, 2), {}) returns 3
INFO:__main__:Call func add with ((1,), {'b': 2}) returns 3
INFO:__main__:Call func add with ((), {'a': 1, 'b': 2}) returns 3

Для метода

Нам нужно заботиться о Я Отказ

def method_logger(method):

    def inner(self, *args, **kwargs):
        ret = method(self, *args, **kwargs)
        logger.info(f'Call method {method.__name__} of {self} with {args, kwargs} returns {ret}')
        return ret

    return inner

class A:
    a:int
    def __init__(self, a):
        self.a = a

    @method_logger
    def addX(self, x: int):
        return self.a + x

    def __repr__(self):
        return f'A(a={self.a})'

a = A(1)
a.addX(2)
a.addX(x=3)
INFO:__main__:Call method addX of A(a=1) with ((2,), {}) returns 3
INFO:__main__:Call method addX of A(a=1) with ((), {'x': 3}) returns 4

Тебе лучше определить пользовательские __str__ или __repr__ Способ для вашего класса для описания объекта в журнале. Один простой способ использует Dataclass Отказ

from dataclasses import dataclass

@dataclass
class Account:
    uid: str
    banlance: int

    @method_logger
    def transerTo(self, target: 'Account', value:int):
        self.banlance -= value
        target.banlance += value
        return self, target

a = Account('aaaa', 10)
b = Account('bbbb', 10)
a.transerTo(b, 5)
INFO:__main__:Call method transerTo of Account(uid='aaaa', banlance=5) with ((Account(uid='bbbb', banlance=15), 5), {}) returns (Account(uid='aaaa', banlance=5), Account(uid='bbbb', banlance=15))

2. Используйте метод __getAttribute__

def method_logger_x(method, obj):
    def inner(*args, **kwargs):
        ret = method(*args, **kwargs)
        logger.info(f'Call method {method.__name__} of {obj} with {args, kwargs} returns {ret}')
        return ret
    return inner


class MethodLogger:
    def __getattribute__(self, key):
        value = super().__getattribute__(key)
        if callable(value) and not key.startswith('__'):
            return method_logger_x(value, self)
        return value

@dataclass
class Account(MethodLogger):
    uid: str
    banlance: int
    frozen: bool = False

    def transerTo(self, target: 'Account', value:int):
        self.banlance -= value
        target.banlance += value
        return self, target

    def freeze(self, reason:str):
        self.frozen = True

a = Account('aaaa', 10)
b = Account('bbbb', 10)
a.transerTo(b, 5)
a.freeze('Dangerous Action')
INFO:__main__:Call method transerTo of Account(uid='aaaa', banlance=5, frozen=False) with ((Account(uid='bbbb', banlance=15, frozen=False), 5), {}) returns (Account(uid='aaaa', banlance=5, frozen=False), Account(uid='bbbb', banlance=15, frozen=False))
INFO:__main__:Call method freeze of Account(uid='aaaa', banlance=5, frozen=True) with (('Dangerous Action',), {}) returns None

PS Когда звоните __getattribute__ Способ объекта, значение (метод) уже получит Я параметр.

3. Используйте мета класса

def method_logger(method):
    def inner(self, *args, **kwargs):
        ret = method(self, *args, **kwargs)
        logger.info(f'Call method {method.__name__} of {self} with {args, kwargs} returns {ret}')
        return ret

    return inner

from typing import Tuple
class MethodLoggerMeta(type):
    def __new__(self, name:str, bases:Tuple[type], attrs:dict):
        attrs_copy = attrs.copy()
        for key, value in attrs.items():
            if callable(value) and not key.startswith('__'):
                attrs_copy[key] = method_logger(value)
        return type(name, bases, attrs_copy)

@dataclass
class Account(metaclass=MethodLoggerMeta):
    uid: str
    banlance: int
    frozen: bool = False

    def transerTo(self, target: 'Account', value:int):
        self.banlance -= value
        target.banlance += value
        return self, target

    def freeze(self, reason:str):
        self.frozen = True

a = Account('aaaa', 10)
b = Account('bbbb', 10)
a.transerTo(b, 5)

Вывод

Добавление большего количества журналов обычно является лучшим способом. Вы можете выбрать один из трех подходов, которые вам нравятся.

Оригинал: “https://dev.to/duyixian1234/three-ways-to-automatically-add-functions-and-methods-calling-log-for-python-code-5fd0”