Есть некий код выгрузки. Выгружает в несколько этапов товары со склада. Операция долгая и может что-то отвалиться, придется потом разбираться что пошло не так и как с этим жить. Нужно пошаговое логирование, какие данные получили, какие получились после обработки, и т.п. Выдуманный пример: PHP: class Export { function exportByManager($data) { log('начало операции в 12:00'); log('данные: '.$data); $this->step1(); log('шаг 1 завершен в 12:01'); $this->step2(); log('шаг 2 завершен в 12:01'); .... Helper::transformProductCodes($positions); .... log('Выгрузка завершена'); } } И вот вопрос. Как правильно логировать внутри функций (step1, transformProductCodes)? Если у нас общий класс, тут все просто. В конструкторе или другим путем создаем объект логгера и дальше везде его используем через $this->log(). Но если у нас местами (как в примере Helper::transformProductCodes()) используются внешний код, как в них продолжить это логирование? Рассматривал такие варианты: 1) Можно передать логгер аргументом внутрь функции. Мне не нравится это тем, что мы как бы засоряем функцию аргументами, которые к основной деятельности функции не имеют прямого отношения. И еще мы навязываем свой класс логирования. 2) Можно внутри этих методов вести свое логирование в свой файл. Но тогда будет очень сложно увидеть всю картину главного процесса выгрузки, придется бегать от файла к файлу и собирать все необходимые данные. А хочется открыть один файл, exportByManager_2021_03_04.log и четко по шагам все проверить. 3) Можно логировать внутри функции в какую-нибудь переменную, а потом возвращать массив, где будет и результат работы функции и логи ее работы. Тогда во внешней системе мы сможем эти логи сохранить как мы хотим. Но как это совсем неправильно, т.к. мы получим на выходе из функции массивы какой бы результат не ожидался, в общем совсем не вариант. В идеале хотелось бы что-то вроде try catch блока, когда ты поверх вызова функции можешь отлавливать логи, если они тебе нужны. Если не нужны, то ничего не ловишь. Типа такого: PHP: try { Helper::transformProductCodes($positions); } catch(\log $e) { log($e->getLogs()) } Буду рад если расскажите как это реализовано в ваших проектах или о какие практиках слышали.
PHP: <?php // Логер final class Logger { private array $lines; private string $file; public int $time; public function __construct ( string $filelog, int $time = null, bool $save = true ) { $this -> file = $filelog; $this -> time = $time ?? time (); if ( $save ) { register_shutdown_function ( [ $this, 'save' ] ); } } public function set( ...$val ): \Logger { if ( func_num_args () > 1 ) { $this -> lines[] = sprintf ( ...$val ); return $this; } $this -> lines[] = $val[0]; return $this; } public function save(): void { if ( ! empty ( $this -> lines ) ) { $start = date ( 'Y-m-d H:i:s - ', $this -> time ); file_put_contents ( $this -> file, $start . implode ( PHP_EOL . $start, $this -> lines ) . PHP_EOL, FILE_APPEND ); $this -> lines = []; } } } PHP: <?php $file_log = __DIR__ . '/file.log'; $logger = new Logger( $file_log ); $logger -> set( 'Hello' ); $logger -> set( 'Hi' ); $logger -> set( $file_log ); $logger -> set( 'afk' ); --- Добавлено --- Код (Text): 2021-03-03 23:34:02 - Hello 2021-03-03 23:34:02 - Hi 2021-03-03 23:34:02 - .../file.log 2021-03-03 23:34:02 - afk
нет, он обращается к внешней системе. Скажем так, шаги: 1) обращение к внешней системе и просьба подготовить выгрузку 2) я получил оповещение что выгрузка подготовлена и я запускаю скачивание данных 3) данные скачаны, я заношу их в свою систему 4) я занес данные в систему и теперь могу запустить последующие процессы для проведения полученного товара. Каждый шаг это сложный процесс который дергает различные сервисы.
Ну так и записывайте в базу, после каждого шага. Уникальный ID цепочки "шагов", id шага. Потом элементарный подсчет шагов в цепочках поможет выявить незавершенные.
@MouseZver спасибо за код, нечто похожее у меня сейчас и используется. Но вопрос был не в коде, а в подходе к логированию. Чтобы ваш логгер записывал все в нужный файл, нужно передавать его экземпляр через аргумент в функцию, например, step1($logger). И внутри этой функции использовать именно его для логирования шагов. У меня сейчас это так и сделано и я хотел бы от этого уйти, чтобы пробрасывать во все методы новый аргумент. Потом в этих функциях появится новый аргумент и будет каша, где-то логгер передается вторым, где-то третьим аргументом, где-то не передается. Вот не хочу передавать логгер туда.
Переделай в статический класс PHP: Logger :: init( ... ); Logger :: set( ... ); Logger :: set( ... ); Logger :: set( ... );