Старый путь, вручную
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”