├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── composer.json ├── event-log.png └── src ├── ExceptionHandlerLog.php ├── Formatter └── BitrixFormatter.php ├── Handler └── BitrixHandler.php ├── MonologAdapter.php └── bootstrap.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: php 4 | 5 | php: 6 | - 5.3 7 | - 5.4 8 | - 5.5 9 | - 5.6 10 | 11 | script: 12 | - composer self-update 13 | - composer install --prefer-source --no-interaction -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2015 — 2016 Nik Samokhvalov (http://samokhvalov.info) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Monolog adapter for Bitrix CMS 2 | 3 | [![Build Status](https://travis-ci.org/bitrix-expert/monolog-adapter.svg)](https://travis-ci.org/bitrix-expert/monolog-adapter) 4 | [![Latest Stable Version](https://poser.pugx.org/bitrix-expert/monolog-adapter/v/stable)](https://packagist.org/packages/bitrix-expert/monolog-adapter) 5 | [![Total Downloads](https://poser.pugx.org/bitrix-expert/monolog-adapter/downloads)](https://packagist.org/packages/bitrix-expert/monolog-adapter) 6 | [![License](https://poser.pugx.org/bitrix-expert/monolog-adapter/license)](https://packagist.org/packages/bitrix-expert/monolog-adapter) 7 | 8 | [Monolog](https://github.com/Seldaek/monolog) adapter for Bitrix CMS: 9 | 10 | * Bitrix handler and formatter for Monolog. 11 | * Handler for logger uncaught exceptions of the Bitrix. 12 | * Configuration loggers with using the `.settings.php`. 13 | 14 | ## Installation 15 | 16 | Download the library using Composer: 17 | 18 | ```bash 19 | composer require bitrix-expert/monolog-adapter 20 | ``` 21 | 22 | Write in the [`init.php`](https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&LESSON_ID=2916) file: 23 | 24 | ```php 25 | array( 39 | 'value' => array( 40 | 'log' => array( 41 | 'class_name' => '\Bex\Monolog\ExceptionHandlerLog', 42 | 'settings' => array( 43 | 'logger' => 'app' 44 | ), 45 | ), 46 | ), 47 | 'readonly' => false 48 | ), 49 | 'monolog' => array( 50 | 'value' => array( 51 | 'handlers' => array( 52 | 'default' => array( 53 | 'class' => '\Monolog\Handler\StreamHandler', 54 | 'level' => 'DEBUG', 55 | 'stream' => '/path/to/logs/app.log' 56 | ), 57 | 'feedback_event_log' => array( 58 | 'class' => '\Bex\Monolog\Handler\BitrixHandler', 59 | 'level' => 'DEBUG', 60 | 'event' => 'TYPE_FOR_EVENT_LOG', 61 | 'module' => 'vendor.module' 62 | ), 63 | ), 64 | 'loggers' => array( 65 | 'app' => array( 66 | 'handlers'=> array('default'), 67 | ), 68 | 'feedback' => array( 69 | 'handlers'=> array('feedback_event_log'), 70 | ) 71 | ) 72 | ), 73 | 'readonly' => false 74 | ) 75 | ); 76 | ``` 77 | 78 | Use rules property for filter logging uncaught exceptions by instanceof logic: 79 | ```php 80 | 'exception_handling' => array( 81 | 'value' => array( 82 | 'log' => array( 83 | 'class_name' => '\Bex\Monolog\ExceptionHandlerLog', 84 | 'settings' => array( 85 | 'logger' => 'app', 86 | 'rules' => array( 87 | 'instanceof' => '\Vendor\Exception\UnloggedInterface', // or opposite: !instanceof 88 | ) 89 | ), 90 | ), 91 | ), 92 | 'readonly' => false 93 | ) 94 | ``` 95 | 96 | Use context property for change log debug data format: 97 | ```php 98 | 'exception_handling' => array( 99 | 'value' => array( 100 | 'log' => array( 101 | 'class_name' => '\Bex\Monolog\ExceptionHandlerLog', 102 | 'settings' => array( 103 | 'logger' => 'app', 104 | 'context' => function ($exception) { 105 | return array( 106 | 'file' => $exception->getFile(), 107 | 'line' => $exception->getLine(), 108 | 'trace' => $exception->getTrace(), 109 | 'some_param' => $exception->getSomeParam(), 110 | ); 111 | }, 112 | ), 113 | ), 114 | ), 115 | 'readonly' => false 116 | ) 117 | ``` 118 | 119 | ### Write logs 120 | 121 | Write logs from your application. For example, write logs when created new message from the feedback form: 122 | 123 | ```php 124 | info('Failed create new message on feedback form', array( 132 | 'item_id' => 21, 133 | 'Invalid data' => $addResult->getErrorMessages(), // error savings 134 | 'Form data' => $formRequest // data from feedback form 135 | )); 136 | ``` 137 | 138 | The result in the Control Panel of Bitrix: 139 | 140 | ![Event Log](event-log.png) 141 | 142 | ## Requirements 143 | 144 | * PHP >= 5.3 145 | * Bitrix CMS >= 16.5.6 146 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bitrix-expert/monolog-adapter", 3 | "description": "Monolog adapter for Bitrix CMS", 4 | "keywords": ["bitrix", "monolog", "logger", "log", "psr-3"], 5 | "type": "library", 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/bitrix-expert/monolog-adapter/issues", 9 | "source": "https://github.com/bitrix-expert/monolog-adapter/" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "Nik Samokhvalov", 14 | "email": "nik@samokhvalov.info" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.3", 19 | "monolog/monolog": "1.*", 20 | "theorchard/monolog-cascade": "~0.2" 21 | }, 22 | "autoload": { 23 | "psr-4": {"Bex\\Monolog\\": "src/"}, 24 | "files": ["src/bootstrap.php"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /event-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitrix-expert/monolog-adapter/112d6524f217c7d7cac29249faad0217a549ce27/event-log.png -------------------------------------------------------------------------------- /src/ExceptionHandlerLog.php: -------------------------------------------------------------------------------- 1 | array( 20 | * 'value' => array( 21 | * 'loggers' => array( 22 | * 'app' => array( 23 | * // Logger configs 24 | * ) 25 | * ) 26 | * ), 27 | * 'readonly' => false, 28 | * ), 29 | * 'exception_handling' => array( 30 | * 'value' => array( 31 | * 'log' => array( 32 | * 'class_name' => '\Bex\Monolog\ExceptionHandlerLog', 33 | * 'settings' => array( 34 | * 'logger' => 'app', 35 | * 'context' => function($exception) { 36 | * return [ 37 | * 'file' => $exception->getFile(), 38 | * 'line' => $exception->getLine(), 39 | * 'trace' => $exception->getTrace(), 40 | * 'some_param' => $exception->getSomeParam(), 41 | * ]; 42 | * }, 43 | * 'rules' => array( 44 | * '!instanceof' => '\Vendor\Exception\UnloggedInterface', 45 | * ) 46 | * ), 47 | * ), 48 | * ), 49 | * 'readonly' => false, 50 | * ), 51 | * ); 52 | * ``` 53 | * 54 | * @author Nik Samokhvalov 55 | */ 56 | class ExceptionHandlerLog extends \Bitrix\Main\Diag\ExceptionHandlerLog 57 | { 58 | /** 59 | * @var Logger 60 | */ 61 | protected $logger; 62 | 63 | /** 64 | * @var callable 65 | */ 66 | protected $context; 67 | 68 | /** 69 | * @var string[] 70 | */ 71 | protected $rules = array(); 72 | 73 | /** 74 | * {@inheritdoc} 75 | */ 76 | public function initialize(array $options) 77 | { 78 | if (!isset($options['logger'])) 79 | { 80 | throw new ArgumentNullException('logger'); 81 | } 82 | 83 | if (is_array($options['rules']) && !empty($options['rules'])) 84 | { 85 | $this->rules = $options['rules']; 86 | } 87 | 88 | if (is_callable($options['context'])) 89 | { 90 | $this->context = $options['context']; 91 | } 92 | 93 | $this->logger = Registry::getInstance($options['logger']); 94 | } 95 | 96 | /** 97 | * {@inheritdoc} 98 | */ 99 | public function write($exception, $logType) 100 | { 101 | foreach ($this->rules as $rule => $condition) 102 | { 103 | switch ($rule) 104 | { 105 | case '!instanceof': 106 | if ($exception instanceof $condition) 107 | { 108 | return; 109 | } 110 | break; 111 | case 'instanceof': 112 | if (!($exception instanceof $condition)) 113 | { 114 | return; 115 | } 116 | break; 117 | } 118 | } 119 | 120 | $context = is_callable($this->context) ? call_user_func($this->context, $exception) : null; 121 | 122 | if ($context === null) 123 | { 124 | $context = array( 125 | 'file' => $exception->getFile(), 126 | 'line' => $exception->getLine(), 127 | 'trace' => $exception->getTrace(), 128 | 'logType' => $logType 129 | ); 130 | } 131 | 132 | $this->logger->emergency($exception->getMessage(), $context); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Formatter/BitrixFormatter.php: -------------------------------------------------------------------------------- 1 | error('message', array('item_id' => 21)); 20 | * ``` 21 | * 22 | * @author Nik Samokhvalov 23 | */ 24 | class BitrixFormatter implements FormatterInterface 25 | { 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function format(array $record) 30 | { 31 | $record['item_id'] = null; 32 | $record['level'] = static::toBitrixLevel($record['level']); 33 | 34 | if (!empty($record['context'])) 35 | { 36 | foreach ($record['context'] as $field => $value) 37 | { 38 | if ($field === 'item_id') 39 | { 40 | $record['item_id'] = $value; 41 | } 42 | else 43 | { 44 | if (is_array($value)) 45 | { 46 | $value = var_export($value, true); 47 | } 48 | 49 | $record['message'] .= '

' . $field . ': ' . $value; 50 | } 51 | } 52 | } 53 | 54 | return $record; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function formatBatch(array $records) 61 | { 62 | $formatted = array(); 63 | 64 | foreach ($records as $record) 65 | { 66 | $formatted[] = $this->format($record); 67 | } 68 | 69 | return $formatted; 70 | } 71 | 72 | /** 73 | * Converts Monolog levels to Bitrix ones if necessary. 74 | * 75 | * @param int $level Level number. 76 | * 77 | * @return string|bool 78 | */ 79 | public static function toBitrixLevel($level) 80 | { 81 | $levels = static::logLevels(); 82 | 83 | if (isset($levels[$level])) 84 | { 85 | return $levels[$level]; 86 | } 87 | 88 | return false; 89 | } 90 | 91 | /** 92 | * Translates Monolog log levels to Bitrix levels. 93 | * 94 | * @return array 95 | */ 96 | public static function logLevels() 97 | { 98 | return array( 99 | Logger::DEBUG => 'DEBUG', 100 | Logger::INFO => 'INFO', 101 | Logger::NOTICE => 'WARNING', 102 | Logger::WARNING => 'WARNING', 103 | Logger::ERROR => 'ERROR', 104 | Logger::CRITICAL => 'ERROR', 105 | Logger::ALERT => 'ERROR', 106 | Logger::EMERGENCY => 'ERROR', 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Handler/BitrixHandler.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class BitrixHandler extends AbstractProcessingHandler 19 | { 20 | private $event; 21 | private $module; 22 | private $siteId; 23 | 24 | /** 25 | * @param string $event Type of event in the event log of Bitrix. 26 | * @param string $module Code of the module in Bitrix. 27 | * @param int $level The minimum logging level at which this handler will be triggered. 28 | * @param bool $bubble Whether the messages that are handled can bubble up the stack or not. 29 | */ 30 | public function __construct($event = null, $module = null, $level = Logger::DEBUG, $bubble = true) 31 | { 32 | parent::__construct($level, $bubble); 33 | 34 | $this->setEvent($event); 35 | $this->setModule($module); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | protected function write(array $record) 42 | { 43 | \CEventLog::Log( 44 | $record['formatted']['level'], 45 | $this->getEvent(), 46 | $this->getModule(), 47 | $record['formatted']['item_id'], 48 | $record['formatted']['message'], 49 | $this->getSite() 50 | ); 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | protected function getDefaultFormatter() 57 | { 58 | return new BitrixFormatter(); 59 | } 60 | 61 | /** 62 | * Sets event type for log of Bitrix. 63 | * 64 | * @param string $event 65 | */ 66 | public function setEvent($event) 67 | { 68 | $this->event = $event; 69 | } 70 | 71 | /** 72 | * Gets event type. 73 | * 74 | * @return string 75 | */ 76 | public function getEvent() 77 | { 78 | return $this->event; 79 | } 80 | 81 | /** 82 | * Sets module for log of Bitrix. 83 | * 84 | * @param string $module 85 | */ 86 | public function setModule($module) 87 | { 88 | $this->module = $module; 89 | } 90 | 91 | /** 92 | * Gets module. 93 | * 94 | * @return string 95 | */ 96 | public function getModule() 97 | { 98 | return $this->module; 99 | } 100 | 101 | /** 102 | * Sets site ID for log of Bitrix. 103 | * 104 | * @param string $siteId 105 | */ 106 | public function setSite($siteId) 107 | { 108 | $this->siteId = $siteId; 109 | } 110 | 111 | /** 112 | * Gets site ID. 113 | * 114 | * @return string 115 | */ 116 | public function getSite() 117 | { 118 | return $this->siteId; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/MonologAdapter.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class MonologAdapter 18 | { 19 | protected static $isConfigurationLoaded = false; 20 | 21 | /** 22 | * Load a configuration for the loggers from `.settings.php` or `.settings_extra.php`. 23 | * 24 | * @param bool $force Load even if the configuration has already been loaded. 25 | * 26 | * @return bool 27 | */ 28 | public static function loadConfiguration($force = false) 29 | { 30 | if ($force === false && static::$isConfigurationLoaded === true) 31 | { 32 | return true; 33 | } 34 | 35 | if (class_exists('\Bitrix\Main\Config\Configuration')) 36 | { 37 | $config = Configuration::getInstance()->get('monolog'); 38 | 39 | if (is_array($config) && !empty($config)) 40 | { 41 | Cascade::fileConfig($config); 42 | 43 | return true; 44 | } 45 | } 46 | 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/bootstrap.php: -------------------------------------------------------------------------------- 1 |