Декоратор Python: логирование только медленных вызовов

Напишите решение на Python и проверьте его на странице.

Другие упражнения по этой теме: Декораторы Python: задачи на retry, rate limiter, кэш и Circuit Breaker.

Фильтрованная телеметрия операций

В системе мониторинга производительности отслеживается время выполнения ресурсоёмких операций с базой данных. Чтобы журнал не переполнялся тысячами штатных быстрых вызовов, параметризованный декоратор принимает порог длительности и функцию записи. Отчёт формируется и отправляется в приёмник телеметрии только тогда, когда время работы функции строго превысило заданный порог либо вызов завершился любым исключением.

Служебный приёмник не должен влиять на основной рабочий процесс: если при попытке записать отчёт возникает сбой, его следует безопасно подавить. Неполадка в приёмнике не должна мешать возврату вычисленного результата или заменять собой оригинальное исключение, возникшее в целевой функции.

Реализуйте фабрику декоратора профилирования, которая отбирает медленные и аварийные вызовы, изолирует ошибки приёмника телеметрии и сохраняет метаданные декорируемой функции.

Что уже дано

В среде выполнения уже определены вспомогательные инструменты проверки make_telemetry_probe и observe_telemetry_probe. Их реализовывать не нужно, они сразу доступны при запуске тестов.

Вам предстоит реализовать фабрику декоратора filtered_telemetry.

Что нужно сделать

Напишите функцию-фабрику filtered_telemetry(threshold, sink, clock).

Функция принимает три аргумента:

  • threshold — неотрицательный порог длительности в секундах (число от 0 до 1 000 000);
  • sink — вызываемый объект-приёмник, принимающий словарь с отчётом;
  • clock — функция монотонных часов без аргументов, возвращающая текущее числовое время в секундах.

Фабрика возвращает декоратор, который принимает целевую синхронную функцию func и возвращает функцию-обёртку wrapper(*args, **kwargs).

Правила работы обёртки:

  • Аргументы *args и **kwargs передаются в func без изменений. Возвращённый результат или возникшее исключение должны быть полностью сохранены.
  • Замер времени охватывает строго выполнение func: значение duration_seconds вычисляется как разность показаний clock() сразу после завершения (или сбоя) функции и непосредственно перед её вызовом. Задержки внутри sink не включаются в замер. Искусственные паузы (sleep) делать запрещено.
  • Отчёт отправляется в sink ровно один раз при выполнении хотя бы одного из условий:
  1. Вызов func завершился любым исключением (BaseException);
  2. Вызов func завершился успешно, но затраченное время строго больше threshold (duration_seconds > threshold). Если время в точности равно порогу, успешный вызов не отправляется.
  • Если успешный вызов уложился в порог (duration_seconds <= threshold), sink вызываться не должен.
  • Формат словаря, передаваемого в sink:
  • "function": имя функции func.__name__;
  • "duration_seconds": неотрицательное вычисленное время работы функции;
  • "status": строка "success" при успешном завершении либо "error", если было возбуждено исключение;
  • "error": None при успехе, либо строка с именем типа возникшего исключения (type(exc).__name__).
  • Сбои приёмника изолированы: если sink возбуждает любое исключение (подкласс Exception), оно безопасно подавляется и ни при каких условиях не подменяет результат функции и не мешает всплытию исходного исключения из func.
  • Обёртка должна сохранять стандартные метаданные декорируемой функции через functools.wraps: имя, документацию, аннотации типов, модуль, квалифицированное имя и ссылку __wrapped__ на func.

Всего в рамках сессии может поступать от 0 до 1000 вызовов обёртки.

Ввод и вывод

Потоки stdin и stdout не используются: считывать ввод и печатать через print не требуется. После выполнения кода ученика тестирующая система считывает из глобального пространства имён функцию верхнего уровня filtered_telemetry и тестирует полученные обёртки на проверочных сценариях.

Пример использования

probe = make_telemetry_probe([["return", 5, "slow"], ["return", 1, "fast"]])
operation = filtered_telemetry(3, probe.sink, probe.clock)(probe.operation)
print(operation())
print(operation())
print(observe_telemetry_probe(probe)["reports"])

Вывод:

slow
fast
[{'function': 'operation', 'duration_seconds': 5, 'status': 'success', 'error': None}]
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи