ЛР 7. Декоратор логирования¶
Тема: Параметризуемый декоратор логирования, работа с API ЦБ РФ
Цель работы¶
Создать универсальный параметризуемый декоратор @logger, поддерживающий вывод в stdout, файл и logging.Logger. Применить его для логирования запросов к API курсов валют ЦБ РФ.
Задание¶
- Реализовать декоратор
@logger(handle=...)с поддержкой разных типов вывода. - Написать функцию
get_currenciesдля получения курсов валют через API ЦБ РФ. - Реализовать логирование в файл (
currency.log). - Добавить демонстрацию уровней логирования на примере решения квадратного уравнения.
Код¶
Декоратор¶
import functools
import sys
import logging
def logger(func=None, *, handle=sys.stdout):
def decorator(f):
is_logging_logger = isinstance(handle, logging.Logger)
@functools.wraps(f)
def wrapper(*args, **kwargs):
if is_logging_logger:
log_info = handle.info
log_error = handle.error
else:
def log_info(msg):
handle.write(f"INFO: {msg}\n")
def log_error(msg):
handle.write(f"ERROR: {msg}\n")
log_info(f"Calling {f.__name__} with args={args}, kwargs={kwargs}")
try:
result = f(*args, **kwargs)
log_info(f"{f.__name__} returned {result!r}")
return result
except Exception as e:
log_error(f"{f.__name__} raised {type(e).__name__}: {e}")
raise
return wrapper
if func is None:
return decorator
else:
return decorator(func)
Получение курсов валют¶
import requests
@logger(handle=sys.stdout)
def get_currencies(currency_codes, url="https://www.cbr-xml-daily.ru/daily_json.js"):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise ConnectionError(f"API is not available: {e}")
try:
data = response.json()
except ValueError as e:
raise ValueError(f"Invalid JSON: {e}")
if "Valute" not in data:
raise KeyError("Key 'Valute' not found in response")
valute = data["Valute"]
result = {}
for code in currency_codes:
if code not in valute:
raise KeyError(f"Currency {code} not found in data")
value = valute[code].get("Value")
if not isinstance(value, (int, float)):
raise TypeError(f"Rate for {code} has invalid type: {type(value)}")
result[code] = float(value)
return result
Решение квадратного уравнения с уровнями логирования¶
import math
@logger(handle=quad_logger)
def solve_quadratic(a, b, c):
if a == 0 and b == 0:
quad_logger.critical("Both a and b are zero; not an equation")
raise ValueError("Both a and b are zero")
try:
a, b, c = float(a), float(b), float(c)
except (TypeError, ValueError) as e:
quad_logger.error(f"Invalid coefficients: {e}")
raise
d = b * b - 4 * a * c
if d < 0:
quad_logger.warning(f"Negative discriminant: {d}")
return None
if d == 0:
return (-b / (2 * a),)
sqrt_d = math.sqrt(d)
return (-b - sqrt_d) / (2 * a), (-b + sqrt_d) / (2 * a)
Выводы¶
Создан гибкий параметризуемый декоратор @logger, поддерживающий вывод в stdout, файл и logging.Logger. Продемонстрирована работа с API ЦБ РФ с обработкой ошибок сети, парсинга JSON и отсутствия данных. Реализованы все уровни логирования: INFO, WARNING, ERROR, CRITICAL.