[{"data":1,"prerenderedAt":1520},["ShallowReactive",2],{"navigation_docs":3,"-modules-problems-monolog":238,"-modules-problems-monolog-surround":1517},[4,45,60],{"title":5,"description":6,"icon":7,"path":8,"stem":9,"children":10},"Методология",null,"ruler","\u002Fmethodology","1.methodology",[11,15,20,25,30,35,40],{"title":12,"path":8,"stem":13,"description":14},"bxshef: методология и проверка навыков","1.methodology\u002Findex","Как писать навыки ИИ-агентов для Битрикса и как проверять, что им можно верить",{"title":16,"path":17,"stem":18,"description":19},"Стандарт навыка","\u002Fmethodology\u002Fstandard","1.methodology\u002F1.standard","11 правил, каждое из провала на стенде",{"title":21,"path":22,"stem":23,"description":24},"Методология проверки","\u002Fmethodology\u002Fmethod","1.methodology\u002F2.method","lint → eval → стенд; что измерено",{"title":26,"path":27,"stem":28,"description":29},"bxshef — CLI","\u002Fmethodology\u002Fbxshef","1.methodology\u002F3.bxshef","lint, eval, feedback",{"title":31,"path":32,"stem":33,"description":34},"GitHub Action","\u002Fmethodology\u002Faction","1.methodology\u002F4.action","тот же lint + eval в любом репозитории навыков",{"title":36,"path":37,"stem":38,"description":39},"Заготовка репозитория","\u002Fmethodology\u002Ftemplate","1.methodology\u002F5.template","с чего начать автору модуля",{"title":41,"path":42,"stem":43,"description":44},"Приёмник отзывов","\u002Fmethodology\u002Ffeedback","1.methodology\u002F6.feedback","куда уходят отзывы ИИ-агентов",{"title":46,"description":6,"icon":47,"path":48,"stem":49,"children":50},"Навыки","sparkles","\u002Fskills","2.skills",[51,55],{"title":52,"path":48,"stem":53,"description":54},"Навыки shef.*","2.skills\u002Findex","npx skills add bx-shef\u002Fskills",{"title":56,"path":57,"stem":58,"description":59},"shef-feedback","\u002Fskills\u002Fshef-feedback","2.skills\u002F1.shef-feedback","Отправить отзыв о навыке shef-* или навыке команды. Брать ВСЕГДА, когда просят оставить или отправить отзыв о навыке — даже если сам навык в диалоге не виден, —",{"title":61,"description":6,"icon":62,"path":63,"stem":64,"children":65},"Модули shef.*","package","\u002Fmodules","3.modules",[66,69,131,179],{"title":61,"path":63,"stem":67,"description":68},"3.modules\u002Findex","Что за чем ставить",{"title":70,"description":6,"path":71,"stem":72,"children":73},"shef.options","\u002Fmodules\u002Foptions","3.modules\u002F1.options\u002Findex",[74,76,81,86,91,96,101,106,110,114,119,123,127],{"title":70,"path":71,"stem":72,"description":75},"фундамент: настройки, трейты, компоненты",{"title":77,"path":78,"stem":79,"description":80},"[`\\Shef\\Options\\Installator`] Installer","\u002Fmodules\u002Foptions\u002Finstaller","3.modules\u002F1.options\u002F1.installer","Для облегчения установок.",{"title":82,"path":83,"stem":84,"description":85},"Сборка, CI и релиз","\u002Fmodules\u002Foptions\u002Fbuild-and-install","3.modules\u002F1.options\u002F10.build-and-install","Раскладка репозитория — в module-structure.md, процесс —\nв CONTRIBUTING.md, установка глазами пользователя —\nв README.md.",{"title":87,"path":88,"stem":89,"description":90},"Раскладка репозитория","\u002Fmodules\u002Foptions\u002Fmodule-structure","3.modules\u002F1.options\u002F11.module-structure","Файл про устройство репозитория. Опорные точки модуля — в CLAUDE.md,\nпроцесс — в CONTRIBUTING.md, сборка — в\nbuild-and-install.md.",{"title":92,"path":93,"stem":94,"description":95},"Проверка на портале","\u002Fmodules\u002Foptions\u002Fportal-check","3.modules\u002F1.options\u002F12.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, кеш,\nраскладка файлов, поведение при обновлении. Проверять это приходится руками —\nи лучше по списку, потому что забытый шаг находит не разработчик, а клиент.",{"title":97,"path":98,"stem":99,"description":100},"[`\\Shef\\Options\\Main\\Options`] Опции настроек модуля","\u002Fmodules\u002Foptions\u002Foptions","3.modules\u002F1.options\u002F2.options","Поддерживает следующие типы:",{"title":102,"path":103,"stem":104,"description":105},"Работа с пользователями","\u002Fmodules\u002Foptions\u002Fsecurity","3.modules\u002F1.options\u002F3.security","",{"title":107,"path":108,"stem":109,"description":105},"[`\\Shef\\Options\\Main\\Utils`] Утилиты","\u002Fmodules\u002Foptions\u002Futils","3.modules\u002F1.options\u002F4.utils",{"title":111,"path":112,"stem":113,"description":105},"[`\\Shef\\Options\\Components`] Работа с компонентами","\u002Fmodules\u002Foptions\u002Fcomponents","3.modules\u002F1.options\u002F5.components",{"title":115,"path":116,"stem":117,"description":118},"[`\\Shef\\Options\\Options`] Паттерны","\u002Fmodules\u002Foptions\u002Fpattern","3.modules\u002F1.options\u002F6.pattern","Запускаемые примеры: singleton.php,\nconfig.php, smartstd.php —\nкак их гонять, написано в examples\u002FREADME.md.",{"title":120,"path":121,"stem":122,"description":105},"[`\\Shef\\Options\\Tests`] Тестирование","\u002Fmodules\u002Foptions\u002Ftests","3.modules\u002F1.options\u002F7.tests",{"title":124,"path":125,"stem":126,"description":105},"[`\\Shef\\Options\\TraitList`] Набор трейтов","\u002Fmodules\u002Foptions\u002Ftraitlist","3.modules\u002F1.options\u002F8.traitlist",{"title":128,"path":129,"stem":130,"description":105},"Правила для ИИ-агентов в этом репозитории","\u002Fmodules\u002Foptions\u002Fagent-rules","3.modules\u002F1.options\u002F9.agent-rules",{"title":132,"description":6,"path":133,"stem":134,"children":135},"shef.problems","\u002Fmodules\u002Fproblems","3.modules\u002F2.problems\u002Findex",[136,138,143,147,152,157,161,166,169,172,175],{"title":132,"path":133,"stem":134,"description":137},"логи, журнал событий, учёт проблем",{"title":139,"path":140,"stem":141,"description":142},"[`\\Shef\\Problems\\Integration`] События и меню","\u002Fmodules\u002Fproblems\u002Fevents","3.modules\u002F2.problems\u002F1.events","Что модуль регистрирует в ядре при установке. Список — в .settings.php, ключ\ninstallEvents; что каждый обработчик существует, сверяет\ntests\u002Fsettings_test.php.",{"title":144,"path":145,"stem":146,"description":105},"Безопасность логов","\u002Fmodules\u002Fproblems\u002Fsecurity","3.modules\u002F2.problems\u002F10.security",{"title":148,"path":149,"stem":150,"description":151},"Быстрая отладка: `_pr`, `_log`, `_log1`","\u002Fmodules\u002Fproblems\u002Fdeffunctions","3.modules\u002F2.problems\u002F2.deffunctions","Функции объявляет def-functions.php, подключается он из include.php.",{"title":153,"path":154,"stem":155,"description":156},"Уровни логирования","\u002Fmodules\u002Fproblems\u002Floglevel","3.modules\u002F2.problems\u002F3.loglevel","Уровни — стандарт PSR-3, в Monolog это \\Monolog\\Level. Ниже — когда какой брать.",{"title":158,"path":159,"stem":160,"description":105},"[`\\Shef\\Problems\\Integration\\Monolog`] Monolog","\u002Fmodules\u002Fproblems\u002Fmonolog","3.modules\u002F2.problems\u002F4.monolog",{"title":162,"path":163,"stem":164,"description":165},"Ротация логов","\u002Fmodules\u002Fproblems\u002Flogrotate","3.modules\u002F2.problems\u002F5.logrotate","Логи модуля лежат в каталоге логов — по умолчанию на уровень выше корня сайта,\nдля BitrixVM \u002Fhome\u002Fbitrix\u002Fsh_log\u002F*.log, — и сами не чистятся. Ротацию делает\nlogrotate — системная утилита, а не Monolog: Monolog тоже умеет\n(RotatingFileHandler), но тогда за файлами следит каждый PHP-процесс, а не\nодна служба.",{"title":128,"path":167,"stem":168,"description":105},"\u002Fmodules\u002Fproblems\u002Fagent-rules","3.modules\u002F2.problems\u002F6.agent-rules",{"title":82,"path":170,"stem":171,"description":85},"\u002Fmodules\u002Fproblems\u002Fbuild-and-install","3.modules\u002F2.problems\u002F7.build-and-install",{"title":87,"path":173,"stem":174,"description":90},"\u002Fmodules\u002Fproblems\u002Fmodule-structure","3.modules\u002F2.problems\u002F8.module-structure",{"title":92,"path":176,"stem":177,"description":178},"\u002Fmodules\u002Fproblems\u002Fportal-check","3.modules\u002F2.problems\u002F9.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, меню,\nраскладка файлов, журнал событий, поведение при обновлении. Проверять это\nприходится руками — и лучше по списку, потому что забытый шаг находит не\nразработчик, а клиент.",{"title":180,"description":6,"path":181,"stem":182,"children":183},"shef.insync","\u002Fmodules\u002Finsync","3.modules\u002F3.insync\u002Findex",[184,186,191,194,198,203,208,213,218,222,227,232,235],{"title":180,"path":181,"stem":182,"description":185},"агенты, импорт, API-клиенты, модели",{"title":187,"path":188,"stem":189,"description":190},"[`\\Shef\\InSync\\Agents`] Агенты","\u002Fmodules\u002Finsync\u002Fagents","3.modules\u002F3.insync\u002F1.agents","Агент импорта — наследник \\Shef\\InSync\\Agents\\AAgent. Описание агента для\nb_agent — \\Shef\\InSync\\Agents\\Entity, установку и управление берёт на себя\n\\Shef\\InSync\\Agents\\Manager.",{"title":87,"path":192,"stem":193,"description":90},"\u002Fmodules\u002Finsync\u002Fmodule-structure","3.modules\u002F3.insync\u002F10.module-structure",{"title":92,"path":195,"stem":196,"description":197},"\u002Fmodules\u002Finsync\u002Fportal-check","3.modules\u002F3.insync\u002F11.portal-check","Всё, что ниже рантайма Битрикса, тестами не закрыть: установка, права, левое\nменю, раскладка компонентов, агенты, таблица импорта, поведение при\nобновлении. Проверять это приходится руками — и лучше по списку, потому что\nзабытый шаг находит не разработчик, а клиент.",{"title":199,"path":200,"stem":201,"description":202},"Безопасность","\u002Fmodules\u002Finsync\u002Fsecurity","3.modules\u002F3.insync\u002F12.security","Модуль даёт другим модулям заготовки для синхронизаций, и самые опасные места\nу него общие с ними: кто может запускать агенты и импорт, что ложится в каталоги\nимпорта, что уходит в SQL и в лог. Ниже — что держит модуль и что остаётся\nпроекту.",{"title":204,"path":205,"stem":206,"description":207},"[`\\Shef\\InSync\\Sync`] Импорт","\u002Fmodules\u002Finsync\u002Fimport","3.modules\u002F3.insync\u002F2.import","Импорт идёт в два шага через таблицу импорта \\Shef\\InSync\\Sync\\Model\\SyncTable\n(shef_insync_model):",{"title":209,"path":210,"stem":211,"description":212},"[`\\Shef\\InSync\\Api`] API","\u002Fmodules\u002Finsync\u002Fapi","3.modules\u002F3.insync\u002F3.api","Обращение к внешнему API по HTTP — наследник \\Shef\\InSync\\Api\\AConnector.",{"title":214,"path":215,"stem":216,"description":217},"[`\\Shef\\InSync\\TraitList\\Xml`] Парсинг XML","\u002Fmodules\u002Finsync\u002Fxml","3.modules\u002F3.insync\u002F4.xml","Разбор XML сделан через SbWereWolf\u002Fxml-navigator\n(статья на Хабре): XMLReader идёт по\nдокументу потоком, а каждый элемент с нужным тегом HierarchyComposer\nпревращает в массив — n имя, v значение, a атрибуты, s вложенные\nэлементы.",{"title":219,"path":220,"stem":221,"description":105},"Компоненты","\u002Fmodules\u002Finsync\u002Fcomponents","3.modules\u002F3.insync\u002F5.components",{"title":223,"path":224,"stem":225,"description":226},"Страницы","\u002Fmodules\u002Finsync\u002Fpage","3.modules\u002F3.insync\u002F6.page","Страницы модуля — в левом меню, штатным разделом intranet\n(Bitrix\\Intranet\\CustomSection): раздел «SH Импорт», код shinsync.\nВерхней панели и shef.uiclear больше нет.",{"title":228,"path":229,"stem":230,"description":231},"[`\\Shef\\InSync\\Main\\Options`] Опции настроек модуля","\u002Fmodules\u002Finsync\u002Foptions","3.modules\u002F3.insync\u002F7.options","Опции для страницы настроек модуля импорта (на ShOptionsConfig из\nshef.options): добавьте их во вкладку своего options_conf.php.",{"title":128,"path":233,"stem":234,"description":105},"\u002Fmodules\u002Finsync\u002Fagent-rules","3.modules\u002F3.insync\u002F8.agent-rules",{"title":82,"path":236,"stem":237,"description":85},"\u002Fmodules\u002Finsync\u002Fbuild-and-install","3.modules\u002F3.insync\u002F9.build-and-install",{"id":239,"title":158,"body":240,"description":105,"extension":1512,"meta":1513,"navigation":513,"path":159,"rawbody":1514,"seo":1515,"stem":160,"__hash__":1516},"docs\u002F3.modules\u002F2.problems\u002F4.monolog.md",{"type":241,"value":242,"toc":1501},"minimark",[243,248,268,272,299,309,313,346,489,492,521,524,539,549,553,560,572,575,582,815,828,897,905,912,927,1016,1025,1028,1127,1134,1138,1146,1238,1245,1255,1262,1269,1428,1444,1473,1485,1497],[244,245,247],"h2",{"id":246},"почитать","Почитать",[249,250,251,261],"ul",{},[252,253,254],"li",{},[255,256,260],"a",{"href":257,"rel":258},"https:\u002F\u002Fgithub.com\u002FSeldaek\u002Fmonolog",[259],"nofollow","Monolog",[252,262,263],{},[255,264,267],{"href":265,"rel":266},"https:\u002F\u002Fhabr.com\u002Fru\u002Fpost\u002F456676\u002F",[259],"Логирование в распределенном php-приложении",[244,269,271],{"id":270},"откуда-берётся-monolog","Откуда берётся Monolog",[273,274,275,276,280,281,284,285,288,289,291,292,294,295,298],"p",{},"Из Composer проекта, если он там есть, иначе — из своей копии в\n",[277,278,279],"code",{},"vendor\u002Fmonolog\u002Fmonolog"," модуля. Решает ",[277,282,283],{},".settings.php"," модуля через\n",[277,286,287],{},"ShProjectContext"," из ",[277,290,70],{},": если в vendor проекта Monolog лежит, своя\nкопия не регистрируется. Без ",[277,293,70],{}," или при ошибке разбора\n",[277,296,297],{},"composer.json"," проекта — своя копия.",[273,300,301,302,304,305,308],{},"Версия своей копии обязана подходить под ограничение из ",[277,303,297],{}," модуля\nи не давать deprecation на поддерживаемых версиях PHP — сторожит\n",[277,306,307],{},"tests\u002Fvendor_test.php",".",[244,310,312],{"id":311},"предустановленные-логгеры","Предустановленные логгеры",[273,314,315,316,319,320,323,324,326,327,330,331,334,335,338,339,342,343,308],{},"Сервисы ",[277,317,318],{},"\\Bitrix\\Main\\DI\\ServiceLocator",", ключ ",[277,321,322],{},"services"," в ",[277,325,283],{},".\nКаждому соответствует случай enum ",[277,328,329],{},"\\Shef\\Problems\\Logger",". Файлы — в каталоге\nлогов ",[277,332,333],{},"\\Shef\\Problems\\Main\\Constants::getLogDir()",", вне корня сайта: при корне\n",[277,336,337],{},"\u002Fhome\u002Fbitrix\u002Fwww"," это ",[277,340,341],{},"\u002Fhome\u002Fbitrix\u002Fsh_log",", см. ",[255,344,345],{"href":145},"security.md",[347,348,349,368],"table",{},[350,351,352],"thead",{},[353,354,355,359,362,365],"tr",{},[356,357,358],"th",{},"сервис",[356,360,361],{},"enum",[356,363,364],{},"уровень",[356,366,367],{},"куда",[369,370,371,390,407,430,450,471],"tbody",{},[353,372,373,379,384,387],{},[374,375,376],"td",{},[277,377,378],{},"shef.problems.pr.debug",[374,380,381],{},[277,382,383],{},"Pr",[374,385,386],{},"Debug",[374,388,389],{},"на экран, без оформления; только администратору",[353,391,392,397,402,404],{},[374,393,394],{},[277,395,396],{},"shef.problems.prHtml.debug",[374,398,399],{},[277,400,401],{},"PrHtml",[374,403,386],{},[374,405,406],{},"на экран, с цветом по уровню; только администратору",[353,408,409,414,419,421],{},[374,410,411],{},[277,412,413],{},"shef.problems.log.debug",[374,415,416],{},[277,417,418],{},"Log",[374,420,386],{},[374,422,423,426,427],{},[277,424,425],{},"log.log",", потолок 20 МБ — дальше прежний файл уходит в ",[277,428,429],{},"log.log.1",[353,431,432,437,442,444],{},[374,433,434],{},[277,435,436],{},"shef.problems.log1.debug",[374,438,439],{},[277,440,441],{},"Log1",[374,443,386],{},[374,445,446,449],{},[277,447,448],{},"log1.log",", первая запись за запрос стирает файл",[353,451,452,457,462,465],{},[374,453,454],{},[277,455,456],{},"shef.problems.deprecations.alert",[374,458,459],{},[277,460,461],{},"Deprecations",[374,463,464],{},"Alert",[374,466,467,470],{},[277,468,469],{},"deprecations.log",", тоже стирается первой записью",[353,472,473,478,483,486],{},[374,474,475],{},[277,476,477],{},"shef.problems.factory.system.logger",[374,479,480],{},[277,481,482],{},"Problems",[374,484,485],{},"задаёт вызывающий",[374,487,488],{},"фабрика: файл по типу проблемы + журнал событий",[273,490,491],{},"Через enum:",[493,494,498],"pre",{"className":495,"code":496,"language":497,"meta":105,"style":105},"language-php shiki shiki-themes material-theme-lighter github-light github-dark","\\Bitrix\\Main\\Loader::includeModule('shef.problems');\n\n\\Shef\\Problems\\Logger::PrHtml->getLogger()->debug('что пришло', ['fields' => $fields]);\n","php",[277,499,500,508,515],{"__ignoreMap":105},[501,502,505],"span",{"class":503,"line":504},"line",1,[501,506,507],{},"\\Bitrix\\Main\\Loader::includeModule('shef.problems');\n",[501,509,511],{"class":503,"line":510},2,[501,512,514],{"emptyLinePlaceholder":513},true,"\n",[501,516,518],{"class":503,"line":517},3,[501,519,520],{},"\\Shef\\Problems\\Logger::PrHtml->getLogger()->debug('что пришло', ['fields' => $fields]);\n",[273,522,523],{},"Через сервис напрямую:",[493,525,527],{"className":495,"code":526,"language":497,"meta":105,"style":105},"$logger = \\Bitrix\\Main\\DI\\ServiceLocator::getInstance()->get('shef.problems.log.debug');\n$logger->info('Импорт начат');\n",[277,528,529,534],{"__ignoreMap":105},[501,530,531],{"class":503,"line":504},[501,532,533],{},"$logger = \\Bitrix\\Main\\DI\\ServiceLocator::getInstance()->get('shef.problems.log.debug');\n",[501,535,536],{"class":503,"line":510},[501,537,538],{},"$logger->info('Импорт начат');\n",[273,540,541,544,545,548],{},[277,542,543],{},"Logger::Problems->getLogger()"," бросает ",[277,546,547],{},"LogicException",": это фабрика, а не\nлоггер, её строят через трейт — ниже.",[244,550,552],{"id":551},"проблемы-фабрика-и-трейт","Проблемы: фабрика и трейт",[273,554,555,556,559],{},"Проблема — запись, которую надо не только сохранить, но и найти потом в\nжурнале событий по типу и понять, кому она адресована. Фабрика\n",[277,557,558],{},"\\Shef\\Problems\\Factory\\SystemLoggerFactory::build()"," строит логгер, который\nпишет сразу:",[249,561,562,569],{},[252,563,564,565,568],{},"в файл ",[277,566,567],{},"\u003Cтип>.log"," в каталоге логов;",[252,570,571],{},"в журнал событий Битрикса с этим типом.",[273,573,574],{},"В каждую запись добавляются модуль, класс и ответственный.",[273,576,577,578,581],{},"Обычно фабрику напрямую не зовут, а подключают трейт\n",[277,579,580],{},"\\Shef\\Problems\\Factory\\Trait\\LoggerProblems",":",[493,583,585],{"className":495,"code":584,"language":497,"meta":105,"style":105},"\\Bitrix\\Main\\Loader::includeModule('shef.problems');\n\nfinal class OrdersExchange\n{\n    use \\Shef\\Problems\\Factory\\Trait\\LoggerProblems;\n\n    public function __construct()\n    {\n        $this->initLogger();\n    }\n\n    public static function getClassName(): string\n    {\n        return static::class;\n    }\n\n    public static function getModuleId(): string\n    {\n        return 'acme.exchange';\n    }\n\n    \u002F\u002F Необязательное — умолчания: Info, SH_PROBLEMS_PROBLEM, «сотрудник по умолчанию».\n    protected static function getLogLevel(): \\Monolog\\Level\n    {\n        return \\Monolog\\Level::Error;\n    }\n\n    protected static function getAuditType(): string\n    {\n        return \\Shef\\Problems\\Main\\Constants::AuditTypeSync;\n    }\n\n    public static function getAssignedId(): int\n    {\n        return \\Shef\\Problems\\Main\\Constants::getSyncUserId();\n    }\n\n    public function run(): void\n    {\n        $this->logger->critical('1С не ответила за 30 секунд', ['itemId' => 1024]);\n    }\n}\n",[277,586,587,591,595,600,606,612,617,623,629,635,641,646,652,657,663,668,673,679,684,690,695,700,706,712,717,723,728,733,739,744,750,755,760,766,771,777,782,787,793,798,804,809],{"__ignoreMap":105},[501,588,589],{"class":503,"line":504},[501,590,507],{},[501,592,593],{"class":503,"line":510},[501,594,514],{"emptyLinePlaceholder":513},[501,596,597],{"class":503,"line":517},[501,598,599],{},"final class OrdersExchange\n",[501,601,603],{"class":503,"line":602},4,[501,604,605],{},"{\n",[501,607,609],{"class":503,"line":608},5,[501,610,611],{},"    use \\Shef\\Problems\\Factory\\Trait\\LoggerProblems;\n",[501,613,615],{"class":503,"line":614},6,[501,616,514],{"emptyLinePlaceholder":513},[501,618,620],{"class":503,"line":619},7,[501,621,622],{},"    public function __construct()\n",[501,624,626],{"class":503,"line":625},8,[501,627,628],{},"    {\n",[501,630,632],{"class":503,"line":631},9,[501,633,634],{},"        $this->initLogger();\n",[501,636,638],{"class":503,"line":637},10,[501,639,640],{},"    }\n",[501,642,644],{"class":503,"line":643},11,[501,645,514],{"emptyLinePlaceholder":513},[501,647,649],{"class":503,"line":648},12,[501,650,651],{},"    public static function getClassName(): string\n",[501,653,655],{"class":503,"line":654},13,[501,656,628],{},[501,658,660],{"class":503,"line":659},14,[501,661,662],{},"        return static::class;\n",[501,664,666],{"class":503,"line":665},15,[501,667,640],{},[501,669,671],{"class":503,"line":670},16,[501,672,514],{"emptyLinePlaceholder":513},[501,674,676],{"class":503,"line":675},17,[501,677,678],{},"    public static function getModuleId(): string\n",[501,680,682],{"class":503,"line":681},18,[501,683,628],{},[501,685,687],{"class":503,"line":686},19,[501,688,689],{},"        return 'acme.exchange';\n",[501,691,693],{"class":503,"line":692},20,[501,694,640],{},[501,696,698],{"class":503,"line":697},21,[501,699,514],{"emptyLinePlaceholder":513},[501,701,703],{"class":503,"line":702},22,[501,704,705],{},"    \u002F\u002F Необязательное — умолчания: Info, SH_PROBLEMS_PROBLEM, «сотрудник по умолчанию».\n",[501,707,709],{"class":503,"line":708},23,[501,710,711],{},"    protected static function getLogLevel(): \\Monolog\\Level\n",[501,713,715],{"class":503,"line":714},24,[501,716,628],{},[501,718,720],{"class":503,"line":719},25,[501,721,722],{},"        return \\Monolog\\Level::Error;\n",[501,724,726],{"class":503,"line":725},26,[501,727,640],{},[501,729,731],{"class":503,"line":730},27,[501,732,514],{"emptyLinePlaceholder":513},[501,734,736],{"class":503,"line":735},28,[501,737,738],{},"    protected static function getAuditType(): string\n",[501,740,742],{"class":503,"line":741},29,[501,743,628],{},[501,745,747],{"class":503,"line":746},30,[501,748,749],{},"        return \\Shef\\Problems\\Main\\Constants::AuditTypeSync;\n",[501,751,753],{"class":503,"line":752},31,[501,754,640],{},[501,756,758],{"class":503,"line":757},32,[501,759,514],{"emptyLinePlaceholder":513},[501,761,763],{"class":503,"line":762},33,[501,764,765],{},"    public static function getAssignedId(): int\n",[501,767,769],{"class":503,"line":768},34,[501,770,628],{},[501,772,774],{"class":503,"line":773},35,[501,775,776],{},"        return \\Shef\\Problems\\Main\\Constants::getSyncUserId();\n",[501,778,780],{"class":503,"line":779},36,[501,781,640],{},[501,783,785],{"class":503,"line":784},37,[501,786,514],{"emptyLinePlaceholder":513},[501,788,790],{"class":503,"line":789},38,[501,791,792],{},"    public function run(): void\n",[501,794,796],{"class":503,"line":795},39,[501,797,628],{},[501,799,801],{"class":503,"line":800},40,[501,802,803],{},"        $this->logger->critical('1С не ответила за 30 секунд', ['itemId' => 1024]);\n",[501,805,807],{"class":503,"line":806},41,[501,808,640],{},[501,810,812],{"class":503,"line":811},42,[501,813,814],{},"}\n",[273,816,817,818,821,822,825,826,308],{},"Трейт ",[277,819,820],{},"\\Shef\\Problems\\Factory\\Trait\\DebuggerProblems"," — то же для отладки: даёт\n",[277,823,824],{},"$this->debugger"," на сервисе ",[277,827,396],{},[347,829,830,840],{},[350,831,832],{},[353,833,834,837],{},[356,835,836],{},"что",[356,838,839],{},"где",[369,841,842,852,865,875,887],{},[353,843,844,849],{},[374,845,846],{},[277,847,848],{},"\\Shef\\Problems\\Factory\\SystemLoggerFactory::build",[374,850,851],{},"фабрика проблем",[353,853,854,859],{},[374,855,856],{},[277,857,858],{},"\\Shef\\Problems\\Factory\\Trait\\LoggerProblems::initLogger",[374,860,861,864],{},[277,862,863],{},"$this->logger"," на фабрике",[353,866,867,872],{},[374,868,869],{},[277,870,871],{},"\\Shef\\Problems\\Factory\\Trait\\LoggerProblems::configureLogger",[374,873,874],{},"подменить логгер снаружи — например, в тесте",[353,876,877,882],{},[374,878,879],{},[277,880,881],{},"\\Shef\\Problems\\Factory\\Trait\\DebuggerProblems::initDebugger",[374,883,884,886],{},[277,885,824],{}," для отладки",[353,888,889,894],{},[374,890,891],{},[277,892,893],{},"\\Shef\\Problems\\Main\\Constants::getDefUserId",[374,895,896],{},"ответственный по умолчанию, из настроек",[273,898,899,900,308],{},"Запускаемый пример — ",[255,901,904],{"href":902,"rel":903},"https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fexamples\u002Fproblems.php",[259],"examples\u002Fproblems.php",[244,906,908,909],{"id":907},"свои-логгеры-через-bitrixmaindiagloggercreate","Свои логгеры через ",[277,910,911],{},"\\Bitrix\\Main\\Diag\\Logger::create",[273,913,914,915,918,919,922,923,926],{},"Ядро умеет создавать логгеры по имени из ключа ",[277,916,917],{},"loggers"," в\n",[277,920,921],{},"\u002Fbitrix\u002F.settings.php"," или ",[277,924,925],{},"\u002Fbitrix\u002F.settings_extra.php",". Логгеры модуля туда\nвстают так:",[493,928,930],{"className":495,"code":929,"language":497,"meta":105,"style":105},"return [\n    'loggers' => [\n        'value' => [\n            'shef.problems.prHtml' => [\n                'constructor' => static function () {\n                    if(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n                    {\n                        return null;\n                    }\n\n                    return \\Shef\\Problems\\Logger::PrHtml->getLogger();\n                },\n            ],\n        ],\n        'readonly' => true,\n    ],\n];\n",[277,931,932,937,942,947,952,957,962,967,972,977,981,986,991,996,1001,1006,1011],{"__ignoreMap":105},[501,933,934],{"class":503,"line":504},[501,935,936],{},"return [\n",[501,938,939],{"class":503,"line":510},[501,940,941],{},"    'loggers' => [\n",[501,943,944],{"class":503,"line":517},[501,945,946],{},"        'value' => [\n",[501,948,949],{"class":503,"line":602},[501,950,951],{},"            'shef.problems.prHtml' => [\n",[501,953,954],{"class":503,"line":608},[501,955,956],{},"                'constructor' => static function () {\n",[501,958,959],{"class":503,"line":614},[501,960,961],{},"                    if(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n",[501,963,964],{"class":503,"line":619},[501,965,966],{},"                    {\n",[501,968,969],{"class":503,"line":625},[501,970,971],{},"                        return null;\n",[501,973,974],{"class":503,"line":631},[501,975,976],{},"                    }\n",[501,978,979],{"class":503,"line":637},[501,980,514],{"emptyLinePlaceholder":513},[501,982,983],{"class":503,"line":643},[501,984,985],{},"                    return \\Shef\\Problems\\Logger::PrHtml->getLogger();\n",[501,987,988],{"class":503,"line":648},[501,989,990],{},"                },\n",[501,992,993],{"class":503,"line":654},[501,994,995],{},"            ],\n",[501,997,998],{"class":503,"line":659},[501,999,1000],{},"        ],\n",[501,1002,1003],{"class":503,"line":665},[501,1004,1005],{},"        'readonly' => true,\n",[501,1007,1008],{"class":503,"line":670},[501,1009,1010],{},"    ],\n",[501,1012,1013],{"class":503,"line":675},[501,1014,1015],{},"];\n",[493,1017,1019],{"className":495,"code":1018,"language":497,"meta":105,"style":105},"\\Bitrix\\Main\\Diag\\Logger::create('shef.problems.prHtml')?->debug('сообщение', ['контекст']);\n",[277,1020,1021],{"__ignoreMap":105},[501,1022,1023],{"class":503,"line":504},[501,1024,1018],{},[273,1026,1027],{},"Собственный логгер с любыми обработчиками Monolog — например, файл плюс\nTelegram для важного:",[493,1029,1031],{"className":495,"code":1030,"language":497,"meta":105,"style":105},"'test' => [\n    'constructor' => static function () {\n        if(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n        {\n            return null;\n        }\n\n        return (new \\Shef\\Problems\\Integration\\Monolog\\Logger('test'))\n            ->pushHandler(new \\Monolog\\Handler\\StreamHandler(\n                stream: \\Shef\\Problems\\Main\\Constants::getLogFullPath('test'),\n                level: \\Monolog\\Level::Debug\n            ))\n            ->pushHandler(new \\Monolog\\Handler\\TelegramBotHandler(\n                apiKey: '\u003Cключ бота>',\n                channel: '\u003Cid канала>',\n                level: \\Monolog\\Level::Info\n            ));\n    },\n],\n",[277,1032,1033,1038,1043,1048,1053,1058,1063,1067,1072,1077,1082,1087,1092,1097,1102,1107,1112,1117,1122],{"__ignoreMap":105},[501,1034,1035],{"class":503,"line":504},[501,1036,1037],{},"'test' => [\n",[501,1039,1040],{"class":503,"line":510},[501,1041,1042],{},"    'constructor' => static function () {\n",[501,1044,1045],{"class":503,"line":517},[501,1046,1047],{},"        if(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n",[501,1049,1050],{"class":503,"line":602},[501,1051,1052],{},"        {\n",[501,1054,1055],{"class":503,"line":608},[501,1056,1057],{},"            return null;\n",[501,1059,1060],{"class":503,"line":614},[501,1061,1062],{},"        }\n",[501,1064,1065],{"class":503,"line":619},[501,1066,514],{"emptyLinePlaceholder":513},[501,1068,1069],{"class":503,"line":625},[501,1070,1071],{},"        return (new \\Shef\\Problems\\Integration\\Monolog\\Logger('test'))\n",[501,1073,1074],{"class":503,"line":631},[501,1075,1076],{},"            ->pushHandler(new \\Monolog\\Handler\\StreamHandler(\n",[501,1078,1079],{"class":503,"line":637},[501,1080,1081],{},"                stream: \\Shef\\Problems\\Main\\Constants::getLogFullPath('test'),\n",[501,1083,1084],{"class":503,"line":643},[501,1085,1086],{},"                level: \\Monolog\\Level::Debug\n",[501,1088,1089],{"class":503,"line":648},[501,1090,1091],{},"            ))\n",[501,1093,1094],{"class":503,"line":654},[501,1095,1096],{},"            ->pushHandler(new \\Monolog\\Handler\\TelegramBotHandler(\n",[501,1098,1099],{"class":503,"line":659},[501,1100,1101],{},"                apiKey: '\u003Cключ бота>',\n",[501,1103,1104],{"class":503,"line":665},[501,1105,1106],{},"                channel: '\u003Cid канала>',\n",[501,1108,1109],{"class":503,"line":670},[501,1110,1111],{},"                level: \\Monolog\\Level::Info\n",[501,1113,1114],{"class":503,"line":675},[501,1115,1116],{},"            ));\n",[501,1118,1119],{"class":503,"line":681},[501,1120,1121],{},"    },\n",[501,1123,1124],{"class":503,"line":686},[501,1125,1126],{},"],\n",[273,1128,1129,1130,1133],{},"Ключ бота — секрет: держите его в ",[277,1131,1132],{},".settings_extra.php",", который не уезжает в\nрепозиторий проекта.",[244,1135,1137],{"id":1136},"обработчики-модуля","Обработчики модуля",[273,1139,1140,1141,1145],{},"Все обработчики ",[255,1142,260],{"href":1143,"rel":1144},"https:\u002F\u002Fgithub.com\u002FSeldaek\u002Fmonolog\u002Fblob\u002Fmain\u002Fdoc\u002F02-handlers-formatters-processors.md#handlers",[259],"\nплюс свои:",[347,1147,1148,1158],{},[350,1149,1150],{},[353,1151,1152,1155],{},[356,1153,1154],{},"класс",[356,1156,1157],{},"что делает",[369,1159,1160,1171,1179,1190,1198,1210,1222],{},[353,1161,1162,1165],{},[374,1163,1164],{},"Handler\\BitrixCEventLogHandler",[374,1166,1167,1168],{},"запись в журнал событий; уровень сопоставляется с важностью журнала, см. ",[255,1169,1170],{"href":154},"уровни",[353,1172,1173,1176],{},[374,1174,1175],{},"Handler\\PrHandler",[374,1177,1178],{},"вывод на экран; по умолчанию только администратору; всё экранируется",[353,1180,1181,1184],{},[374,1182,1183],{},"Handler\\PrHtmlHandler",[374,1185,1186,1187],{},"то же с оформлением; стили — расширение ",[277,1188,1189],{},"shef-problems.monolog-pr-html",[353,1191,1192,1195],{},[374,1193,1194],{},"Handler\\Log1Handler",[374,1196,1197],{},"файл, который первая запись за жизнь обработчика стирает",[353,1199,1200,1203],{},[374,1201,1202],{},"Handler\\CappedStreamHandler",[374,1204,1205,1206,1209],{},"файл с потолком размера: перерос — откладывается в ",[277,1207,1208],{},"\u003Cимя>.1",", пишется новый",[353,1211,1212,1215],{},[374,1213,1214],{},"Processor\\TraceProcessor",[374,1216,1217,1218,1221],{},"трассировка в ",[277,1219,1220],{},"extra.trace",": откуда позвали логгер или, для исключения, его трассировка",[353,1223,1224,1227],{},[374,1225,1226],{},"Formatter\\BitrixCEventLogFormatter",[374,1228,1229,1230,1233,1234,1237],{},"запись → описание для журнала; ",[277,1231,1232],{},"itemId"," и ",[277,1235,1236],{},"moduleId"," из контекста — в поля журнала",[273,1239,1240,1244],{},[1241,1242,1243],"strong",{},"Вывод на экран экранируется."," В сообщение и контекст попадает что угодно, в\nтом числе ввод посетителя, а смотрит на вывод администратор. До 2.0.0 это шло в\nстраницу как есть.",[273,1246,1247,1250,1251,1254],{},[1241,1248,1249],{},"Трассировка начинается с места вызова",", где бы ни висел ",[277,1252,1253],{},"TraceProcessor"," —\nна обработчике или на логгере: кадры самого Monolog и слоя интеграции\nотрезаются по файлам, а не по счёту.",[244,1256,1258,1259],{"id":1257},"логгер-shefproblemsintegrationmonologlogger","Логгер ",[277,1260,1261],{},"\\Shef\\Problems\\Integration\\Monolog\\Logger",[273,1263,1264,1265,1268],{},"Наследник ",[277,1266,1267],{},"\\Monolog\\Logger",". Первым аргументом принимает не только строку:",[347,1270,1271,1284],{},[350,1272,1273],{},[353,1274,1275,1278,1281],{},[356,1276,1277],{},"что передали",[356,1279,1280],{},"сообщение",[356,1282,1283],{},"контекст",[369,1285,1286,1302,1318,1340,1361,1376,1395,1414],{},[353,1287,1288,1293,1296],{},[374,1289,1290],{},[277,1291,1292],{},"\\Throwable",[374,1294,1295],{},"текст исключения",[374,1297,1298,1299],{},"само исключение под ",[277,1300,1301],{},"throwable",[353,1303,1304,1309,1312],{},[374,1305,1306],{},[277,1307,1308],{},"\\Bitrix\\Main\\Error",[374,1310,1311],{},"текст и код",[374,1313,1314,1315],{},"ошибка под ",[277,1316,1317],{},"BitrixError",[353,1319,1320,1325,1334],{},[374,1321,1322],{},[277,1323,1324],{},"\\Bitrix\\Main\\Result",[374,1326,1327,1330,1331],{},[277,1328,1329],{},"[Result::Error: N] первая ошибка"," либо ",[277,1332,1333],{},"[Result::Success]",[374,1335,1336,1337],{},"ошибки и данные под ",[277,1338,1339],{},"BitrixResult",[353,1341,1342,1347,1352],{},[374,1343,1344],{},[277,1345,1346],{},"\\Bitrix\\Main\\Type\\Contract\\Arrayable",[374,1348,1349],{},[277,1350,1351],{},"Arrayable",[374,1353,1354,1357,1358],{},[277,1355,1356],{},"toArray()"," под ",[277,1359,1360],{},"_message",[353,1362,1363,1366,1371],{},[374,1364,1365],{},"массив",[374,1367,1368],{},[277,1369,1370],{},"Array",[374,1372,1373,1374],{},"массив под ",[277,1375,1360],{},[353,1377,1378,1383,1388],{},[374,1379,1380],{},[277,1381,1382],{},"\\Bitrix\\Main\\Type\\Contract\\Jsonable",[374,1384,1385],{},[277,1386,1387],{},"Jsonable",[374,1389,1390,1357,1393],{},[277,1391,1392],{},"toJson()",[277,1394,1360],{},[353,1396,1397,1402,1407],{},[374,1398,1399],{},[277,1400,1401],{},"\\JsonSerializable",[374,1403,1404],{},[277,1405,1406],{},"JsonSerializable",[374,1408,1409,1357,1412],{},[277,1410,1411],{},"jsonSerialize()",[277,1413,1360],{},[353,1415,1416,1422,1425],{},[374,1417,1418,1419],{},"строка, ",[277,1420,1421],{},"\\Stringable",[374,1423,1424],{},"как есть",[374,1426,1427],{},"как передали",[273,1429,1430,1431,1434,1435,1438,1439,308],{},"Остальное — ",[277,1432,1433],{},"InvalidArgumentException",": лучше увидеть ошибку сразу, чем\nпотерять запись молча. Превращения делают стратегии\n",[277,1436,1437],{},"\\Shef\\Problems\\Integration\\Monolog\\Strategy\\LoggerConverter\\IStrategy",", по одной\nна тип. Запускаемый пример — ",[255,1440,1443],{"href":1441,"rel":1442},"https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fexamples\u002Flogger.php",[259],"examples\u002Flogger.php",[273,1445,1446,1449,1450,1453,1454,1457,1458,1461,1462,1465,1466,1469,1470,1472],{},[1241,1447,1448],{},"Сбой записи не бросает."," Обработчик не смог записать — каталог логов вне\n",[277,1451,1452],{},"open_basedir",", нет прав, упал ",[277,1455,1456],{},"CEventLog"," — и исключение не уходит в\nвызывающий код: логгер пишет в лог PHP строку ",[277,1459,1460],{},"shef.problems: запись логгера \u003Cканал> не прошла: \u003Cкласс>: \u003Cпервая строка сообщения>",". Запись при этом\nпрерывается: обработчики ниже по стеку её не получают — так устроен\n",[277,1463,1464],{},"Monolog\\Logger::addRecord()",". У фабрики проблем журнал событий стоит выше\nфайла, поэтому сбой файла журнал не отменяет, а сбой журнала отменяет файл.\nНужно иначе — свой обработчик через ",[277,1467,1468],{},"setExceptionHandler()",". Неверный тип\nсообщения (",[277,1471,1433],{}," выше) по-прежнему бросает: это ошибка\nвызова, а не записи.",[273,1474,1475,1478,1479,1478,1482],{},[255,1476,1477],{"href":154},"← Уровни логирования"," | ",[255,1480,1481],{"href":133},"↑ Содержание",[255,1483,1484],{"href":163},"Ротация логов →",[1486,1487,1488],"note",{},[273,1489,1490,1491,1496],{},"Источник: ",[255,1492,1495],{"href":1493,"rel":1494},"https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fdocs\u002F4_monolog.md",[259],"problems\u002Fdocs\u002F4_monolog.md"," — правки туда, сайт пересобирается сам.",[1498,1499,1500],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":105,"searchDepth":517,"depth":517,"links":1502},[1503,1504,1505,1506,1507,1509,1510],{"id":246,"depth":510,"text":247},{"id":270,"depth":510,"text":271},{"id":311,"depth":510,"text":312},{"id":551,"depth":510,"text":552},{"id":907,"depth":510,"text":1508},"Свои логгеры через \\Bitrix\\Main\\Diag\\Logger::create",{"id":1136,"depth":510,"text":1137},{"id":1257,"depth":510,"text":1511},"Логгер \\Shef\\Problems\\Integration\\Monolog\\Logger","md",{},"---\ntitle: \"[`\\\\Shef\\\\Problems\\\\Integration\\\\Monolog`] Monolog\"\n---\n\n## Почитать\n\n* [Monolog](https:\u002F\u002Fgithub.com\u002FSeldaek\u002Fmonolog)\n* [Логирование в распределенном php-приложении](https:\u002F\u002Fhabr.com\u002Fru\u002Fpost\u002F456676\u002F)\n\n## Откуда берётся Monolog\n\nИз Composer проекта, если он там есть, иначе — из своей копии в\n`vendor\u002Fmonolog\u002Fmonolog` модуля. Решает `.settings.php` модуля через\n`ShProjectContext` из `shef.options`: если в vendor проекта Monolog лежит, своя\nкопия не регистрируется. Без `shef.options` или при ошибке разбора\n`composer.json` проекта — своя копия.\n\nВерсия своей копии обязана подходить под ограничение из `composer.json` модуля\nи не давать deprecation на поддерживаемых версиях PHP — сторожит\n`tests\u002Fvendor_test.php`.\n\n## Предустановленные логгеры\n\nСервисы `\\Bitrix\\Main\\DI\\ServiceLocator`, ключ `services` в `.settings.php`.\nКаждому соответствует случай enum `\\Shef\\Problems\\Logger`. Файлы — в каталоге\nлогов `\\Shef\\Problems\\Main\\Constants::getLogDir()`, вне корня сайта: при корне\n`\u002Fhome\u002Fbitrix\u002Fwww` это `\u002Fhome\u002Fbitrix\u002Fsh_log`, см. [security.md](\u002Fmodules\u002Fproblems\u002Fsecurity).\n\n| сервис | enum | уровень | куда |\n|---|---|---|---|\n| `shef.problems.pr.debug` | `Pr` | Debug | на экран, без оформления; только администратору |\n| `shef.problems.prHtml.debug` | `PrHtml` | Debug | на экран, с цветом по уровню; только администратору |\n| `shef.problems.log.debug` | `Log` | Debug | `log.log`, потолок 20 МБ — дальше прежний файл уходит в `log.log.1` |\n| `shef.problems.log1.debug` | `Log1` | Debug | `log1.log`, первая запись за запрос стирает файл |\n| `shef.problems.deprecations.alert` | `Deprecations` | Alert | `deprecations.log`, тоже стирается первой записью |\n| `shef.problems.factory.system.logger` | `Problems` | задаёт вызывающий | фабрика: файл по типу проблемы + журнал событий |\n\nЧерез enum:\n\n```php\n\\Bitrix\\Main\\Loader::includeModule('shef.problems');\n\n\\Shef\\Problems\\Logger::PrHtml->getLogger()->debug('что пришло', ['fields' => $fields]);\n```\n\nЧерез сервис напрямую:\n\n```php\n$logger = \\Bitrix\\Main\\DI\\ServiceLocator::getInstance()->get('shef.problems.log.debug');\n$logger->info('Импорт начат');\n```\n\n`Logger::Problems->getLogger()` бросает `LogicException`: это фабрика, а не\nлоггер, её строят через трейт — ниже.\n\n## Проблемы: фабрика и трейт\n\nПроблема — запись, которую надо не только сохранить, но и найти потом в\nжурнале событий по типу и понять, кому она адресована. Фабрика\n`\\Shef\\Problems\\Factory\\SystemLoggerFactory::build()` строит логгер, который\nпишет сразу:\n\n* в файл `\u003Cтип>.log` в каталоге логов;\n* в журнал событий Битрикса с этим типом.\n\nВ каждую запись добавляются модуль, класс и ответственный.\n\nОбычно фабрику напрямую не зовут, а подключают трейт\n`\\Shef\\Problems\\Factory\\Trait\\LoggerProblems`:\n\n```php\n\\Bitrix\\Main\\Loader::includeModule('shef.problems');\n\nfinal class OrdersExchange\n{\n\tuse \\Shef\\Problems\\Factory\\Trait\\LoggerProblems;\n\n\tpublic function __construct()\n\t{\n\t\t$this->initLogger();\n\t}\n\n\tpublic static function getClassName(): string\n\t{\n\t\treturn static::class;\n\t}\n\n\tpublic static function getModuleId(): string\n\t{\n\t\treturn 'acme.exchange';\n\t}\n\n\t\u002F\u002F Необязательное — умолчания: Info, SH_PROBLEMS_PROBLEM, «сотрудник по умолчанию».\n\tprotected static function getLogLevel(): \\Monolog\\Level\n\t{\n\t\treturn \\Monolog\\Level::Error;\n\t}\n\n\tprotected static function getAuditType(): string\n\t{\n\t\treturn \\Shef\\Problems\\Main\\Constants::AuditTypeSync;\n\t}\n\n\tpublic static function getAssignedId(): int\n\t{\n\t\treturn \\Shef\\Problems\\Main\\Constants::getSyncUserId();\n\t}\n\n\tpublic function run(): void\n\t{\n\t\t$this->logger->critical('1С не ответила за 30 секунд', ['itemId' => 1024]);\n\t}\n}\n```\n\nТрейт `\\Shef\\Problems\\Factory\\Trait\\DebuggerProblems` — то же для отладки: даёт\n`$this->debugger` на сервисе `shef.problems.prHtml.debug`.\n\n| что | где |\n|---|---|\n| `\\Shef\\Problems\\Factory\\SystemLoggerFactory::build` | фабрика проблем |\n| `\\Shef\\Problems\\Factory\\Trait\\LoggerProblems::initLogger` | `$this->logger` на фабрике |\n| `\\Shef\\Problems\\Factory\\Trait\\LoggerProblems::configureLogger` | подменить логгер снаружи — например, в тесте |\n| `\\Shef\\Problems\\Factory\\Trait\\DebuggerProblems::initDebugger` | `$this->debugger` для отладки |\n| `\\Shef\\Problems\\Main\\Constants::getDefUserId` | ответственный по умолчанию, из настроек |\n\nЗапускаемый пример — [examples\u002Fproblems.php](https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fexamples\u002Fproblems.php).\n\n## Свои логгеры через `\\Bitrix\\Main\\Diag\\Logger::create`\n\nЯдро умеет создавать логгеры по имени из ключа `loggers` в\n`\u002Fbitrix\u002F.settings.php` или `\u002Fbitrix\u002F.settings_extra.php`. Логгеры модуля туда\nвстают так:\n\n```php\nreturn [\n\t'loggers' => [\n\t\t'value' => [\n\t\t\t'shef.problems.prHtml' => [\n\t\t\t\t'constructor' => static function () {\n\t\t\t\t\tif(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n\t\t\t\t\t{\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn \\Shef\\Problems\\Logger::PrHtml->getLogger();\n\t\t\t\t},\n\t\t\t],\n\t\t],\n\t\t'readonly' => true,\n\t],\n];\n```\n\n```php\n\\Bitrix\\Main\\Diag\\Logger::create('shef.problems.prHtml')?->debug('сообщение', ['контекст']);\n```\n\nСобственный логгер с любыми обработчиками Monolog — например, файл плюс\nTelegram для важного:\n\n```php\n'test' => [\n\t'constructor' => static function () {\n\t\tif(!\\Bitrix\\Main\\Loader::includeModule('shef.problems'))\n\t\t{\n\t\t\treturn null;\n\t\t}\n\n\t\treturn (new \\Shef\\Problems\\Integration\\Monolog\\Logger('test'))\n\t\t\t->pushHandler(new \\Monolog\\Handler\\StreamHandler(\n\t\t\t\tstream: \\Shef\\Problems\\Main\\Constants::getLogFullPath('test'),\n\t\t\t\tlevel: \\Monolog\\Level::Debug\n\t\t\t))\n\t\t\t->pushHandler(new \\Monolog\\Handler\\TelegramBotHandler(\n\t\t\t\tapiKey: '\u003Cключ бота>',\n\t\t\t\tchannel: '\u003Cid канала>',\n\t\t\t\tlevel: \\Monolog\\Level::Info\n\t\t\t));\n\t},\n],\n```\n\nКлюч бота — секрет: держите его в `.settings_extra.php`, который не уезжает в\nрепозиторий проекта.\n\n## Обработчики модуля\n\nВсе обработчики [Monolog](https:\u002F\u002Fgithub.com\u002FSeldaek\u002Fmonolog\u002Fblob\u002Fmain\u002Fdoc\u002F02-handlers-formatters-processors.md#handlers)\nплюс свои:\n\n| класс | что делает |\n|---|---|\n| Handler\\BitrixCEventLogHandler | запись в журнал событий; уровень сопоставляется с важностью журнала, см. [уровни](\u002Fmodules\u002Fproblems\u002Floglevel) |\n| Handler\\PrHandler | вывод на экран; по умолчанию только администратору; всё экранируется |\n| Handler\\PrHtmlHandler | то же с оформлением; стили — расширение `shef-problems.monolog-pr-html` |\n| Handler\\Log1Handler | файл, который первая запись за жизнь обработчика стирает |\n| Handler\\CappedStreamHandler | файл с потолком размера: перерос — откладывается в `\u003Cимя>.1`, пишется новый |\n| Processor\\TraceProcessor | трассировка в `extra.trace`: откуда позвали логгер или, для исключения, его трассировка |\n| Formatter\\BitrixCEventLogFormatter | запись → описание для журнала; `itemId` и `moduleId` из контекста — в поля журнала |\n\n**Вывод на экран экранируется.** В сообщение и контекст попадает что угодно, в\nтом числе ввод посетителя, а смотрит на вывод администратор. До 2.0.0 это шло в\nстраницу как есть.\n\n**Трассировка начинается с места вызова**, где бы ни висел `TraceProcessor` —\nна обработчике или на логгере: кадры самого Monolog и слоя интеграции\nотрезаются по файлам, а не по счёту.\n\n## Логгер `\\Shef\\Problems\\Integration\\Monolog\\Logger`\n\nНаследник `\\Monolog\\Logger`. Первым аргументом принимает не только строку:\n\n| что передали | сообщение | контекст |\n|---|---|---|\n| `\\Throwable` | текст исключения | само исключение под `throwable` |\n| `\\Bitrix\\Main\\Error` | текст и код | ошибка под `BitrixError` |\n| `\\Bitrix\\Main\\Result` | `[Result::Error: N] первая ошибка` либо `[Result::Success]` | ошибки и данные под `BitrixResult` |\n| `\\Bitrix\\Main\\Type\\Contract\\Arrayable` | `Arrayable` | `toArray()` под `_message` |\n| массив | `Array` | массив под `_message` |\n| `\\Bitrix\\Main\\Type\\Contract\\Jsonable` | `Jsonable` | `toJson()` под `_message` |\n| `\\JsonSerializable` | `JsonSerializable` | `jsonSerialize()` под `_message` |\n| строка, `\\Stringable` | как есть | как передали |\n\nОстальное — `InvalidArgumentException`: лучше увидеть ошибку сразу, чем\nпотерять запись молча. Превращения делают стратегии\n`\\Shef\\Problems\\Integration\\Monolog\\Strategy\\LoggerConverter\\IStrategy`, по одной\nна тип. Запускаемый пример — [examples\u002Flogger.php](https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fexamples\u002Flogger.php).\n\n**Сбой записи не бросает.** Обработчик не смог записать — каталог логов вне\n`open_basedir`, нет прав, упал `CEventLog` — и исключение не уходит в\nвызывающий код: логгер пишет в лог PHP строку `shef.problems: запись логгера\n\u003Cканал> не прошла: \u003Cкласс>: \u003Cпервая строка сообщения>`. Запись при этом\nпрерывается: обработчики ниже по стеку её не получают — так устроен\n`Monolog\\Logger::addRecord()`. У фабрики проблем журнал событий стоит выше\nфайла, поэтому сбой файла журнал не отменяет, а сбой журнала отменяет файл.\nНужно иначе — свой обработчик через `setExceptionHandler()`. Неверный тип\nсообщения (`InvalidArgumentException` выше) по-прежнему бросает: это ошибка\nвызова, а не записи.\n\n[← Уровни логирования](\u002Fmodules\u002Fproblems\u002Floglevel) | [↑ Содержание](\u002Fmodules\u002Fproblems) | [Ротация логов →](\u002Fmodules\u002Fproblems\u002Flogrotate)\n\n::note\nИсточник: [problems\u002Fdocs\u002F4_monolog.md](https:\u002F\u002Fgithub.com\u002Fbx-shef\u002Fproblems\u002Fblob\u002Fmain\u002Fdocs\u002F4_monolog.md) — правки туда, сайт пересобирается сам.\n::\n\n",{"title":158,"description":105},"w7-KlxH5FWY_hdZr6xpXqLlAPZhLXJ0MiLRoc5rZUwE",[1518,1519],{"title":153,"path":154,"stem":155,"description":156,"children":-1},{"title":162,"path":163,"stem":164,"description":165,"children":-1},1790857270233]