Package: pybotx-smart-logger
pybotx-smart-loggerShows logs when you need itПроблема/решениеВ основном наши боты работают в закрытых контурах. Там невозможно использовать Sentry,
поэтому наш главный помощник в диагностике неполадок - логи контейнера.Однако, если сделать логи слишком подробными, то действительно важную информацию будет
очень сложно найти. Также мы получим проблемы избыточного использования постоянной
памяти или слишком быструю ротацию логов. Но сделав логи слишком сжатыми, мы рискуем
столкнуться с ситуацией, когда их недостаточно для диагностики ошибки.То есть хочется видеть как можно больше информации во время возникновения ошибок, и как
можно меньше - когда всё хорошо.ИспользованиеИспользуя функциюsmart_log(log_message: str, *args: Any, **kwargs: Any)логируете всю
информацию, которая поможет в диагностике ошибки. Если во время обработки сообщения
будет выброшено исключение, в логи попадёт:Текущее сообщение от пользователя,Вся залогированная с помощьюsmart_logинформация,Выброшенное исключение.Если обработка сообщения завершится успешно, накопленные логи будут "выброшены".НастройкаУстанавливаем библиотеку:poetryaddpybotx-smart-loggerПодключим мидлварь для логирования входящих сообщений:middlewares/smart_logger.pyasyncdefsmart_logger_middleware(message:IncomingMessage,bot:Bot,call_next:IncomingMessageHandlerFunc,)->None:asyncwithwrap_smart_logger(log_source="Incoming message",context_func=lambda:format_raw_command(message.raw_command),debug=True,):awaitcall_next(message,bot)bot.pyBot(collectors=[collector],bot_accounts=[BOT_CREDENTIALS],middlewares=[smart_logger_middleware,],)Для того чтобы логировать какие-то другие части приложения, необходимо обернуть в контекстный менеджер:asyncdefhandler()->None:asyncwithwrap_smart_logger(log_source="Request to Server",context_func=lambda:str(kwargs),debug=False,):awaitmake_request(**kwargs)Также можно использовать smart_logger для логирования запросов к FastAPI приложению:app=FastAPI()@app.middleware("http")asyncdefsmart_logger_middleware(request:Request,call_next:Callable)->None:asyncwithwrap_smart_logger(log_source="Incoming request",context_func=lambda:pformat_str_request(request),debug=DEBUG,):returnawaitcall_next(request)log_sourceопределяет источник логов.context_func- пользовательская функция для форматирования логов.Пример команд для включения отладки@collector.command("/_debug:enable-for-huids",visible=False)asyncdefenable_debug_for_users(message:IncomingMessage,bot:Bot)->None:try:huids=[UUID(huid)forhuidinmessage.arguments]exceptValueError:awaitbot.answer_message("Получен невалидный user_huid")return# TODO: Обновите список user_huidawaitbot.answer_message(f"Список user_huid для отладки обновлён{huids}")@collector.command("/_debug:enable-for-tasks",visible=False)asyncdefenable_debug_for_tasks(message:IncomingMessage,bot:Bot)->None:# TODO: Обновите список имён задачawaitbot.answer_message("Список задач для отладки обновлён")Группировка сообщений логераДля того, чтобы логи из разных хендлеров не перемешивались между собой
и можно было удобно отследить весь порядок действия пользователя, можно
включить группировку логов.asyncdefsmart_logger_middleware(message:IncomingMessage,bot:Bot,call_next:IncomingMessageHandlerFunc,)->None:asyncwithwrap_smart_logger(log_source="Incoming message",context_func=lambda:format_raw_command(message.raw_command),debug=True,group=True,# Enable grouping):awaitcall_next(message,bot)Теперь всё, что было залогировано через функциюsmart_logбудет "выброшено" только
после обработки сообщения и не будет перемешано с логами других сообщений.Работает только с включеннымDEBUGГде применятьПроверка роли:# TODO: Мидлварь для заполнения message.state.userasyncdefsubscribed_users_only_middleware(message:IncomingMessage,bot:Bot,call_next:IncomingMessageHandlerFunc,)->None:ifnotmessage.state.user.is_subscribed:awaitbot.send(message=only_subscribed_users_allowed_message(message))returnsmart_log("This user is subscribed")awaitcall_next(message,bot)Обращение в API:asyncdef_perform_request(method:Literal["GET","POST"],url:str,query_params:Optional[Dict[str,Any]]=None,body_dict:Optional[Dict[str,Any]]=None,)->str:smart_log("Performing request to YourAwesomeAPI")smart_log("Method:",method)smart_log("URL:",url)smart_log("Query parameters:",query_params)smart_log("Body dict:",body_dict)try:asyncwithAsyncClient(base_url=base_url)asclient:response=awaitclient.request(method,url,params=query_params,json=body_dict,)exceptHTTPErrorasexc:raiseRequestToAwesomeAPIErrorfromexcsmart_log("Response text:",response.text)try:response.raise_for_status()exceptHTTPStatusErrorasexc:# noqa: WPS440raiseInvalidStatusCodeFromAwesomeAPIErrorfromexcreturnresponse.textА также любые моменты, где что-то может пойти не так. Логируйте - не стестяйтесь.