Let the platform do the work

PSR-3 Logger

Monolog\Handler\HandlerInterface

Overview

Sugar's PSR-3 compliant logging solution has been implemented based on PHP Monolog. Accessing the PSR-3 Logger Objects can be done by via the \Sugarcrm\Sugarcrm\Logger namespace. Currently, this logging implementation is only used in a few areas of the system to allow for more in-depth logging in those areas, see the Usage section for more details.

Architecture

The PSR-3 Logging solution is found in ./src/Logger directory, which is mapped to the \Sugarcrm\Sugarcrm\Logger namespace in the application. The following outlines the architecture and current implementations of those core objects.

Factory

The Logger Factory Object, namespaced as \Sugarcrm\Sugarcrm\Logger\Factory, is a singleton factory that creates and manages all Loggers currently being used by the system. The Logger Factory uses 'channels' to allow for multiple Loggers to be utilized and configured independently of default log level set for the system. It also allows for each channel to use different handlers, formatters, and processors. Check out the Usage section below for further details on configuration and usage.

Methods

getLogger($channel)

Returns the requested channels \Psr\Log\LoggerInterface implementation

Arguments
Name Type Description
$channel String The channel for which you are logging against
Example

Handlers

Handlers are the primary object used to manage the logs. They control how logs are submitted and where the logs will go. Since the PSR-3 Logging solution is based on Monolog, there are a number of default Handlers included in Sugar, however, they are not set up in the Sugar architecture to be utilized right off the bat. Sugar utilizes a Factory implementation for the handlers so that the Logger Factory Object can build out specific Handlers for each channel based on the Sugar Config.

Factory Interface

The Factory Interface for Handlers is used to implement a Logging Handler, so that the Logger Factory can build out the configured handler for a channel, without a lot of work from external developers. The Handler Factory Interface is located in ./src/Logger/Handlers/Factory.php or in code at the \Sugarcrm\Sugarcrm\Logger\Handler\Factory namespace.

Methods

There is only one method to implement for a Handler Factory, which is the create() method. This will contain the necessary Logic to set up the Logger Handler

create($level, $config)
Arguments
Name Type Description
$level String The log level the Logger Handler will operate at 
$config Array The config parameters set for the handler in the Sugar config file
Returns

The Monolog\Handler\HandlerInterface implementation

Implementations

By default, Sugar only comes with a single Handler implementation, which is the File Handler implementation. This is used to log directly to the ./sugarcrm.log file to mirror the functionality of the previous logging framework. For information on adding custom handlers, or implementing the built-in Monolog handlers inside of Sugar PSR-3 Logging framework, see the Customization section below.

Configuration

To configure the default logging handler for Sugar the following configuration setting is used:

                              $sugar_config['logger']['handler'] = '<handler>';













You can also configure a different Logging Handler or Handlers for a specific channel:

                              //Single Handler
$sugar_config['logger']['channels']['test_channel']['handlers'] = '<handler>';

//Multiple Channels
$sugar_config['logger']['channels']['test_channel']['handlers'] = array('<handler1>','<handler2>');













To pass configuration properties to a Handler your configuration will need to look as follows:

                              //For system handler
$sugar_config['logger']['handlers']['<handler>']['host'] = '127.0.0.1';
$sugar_config['logger']['handlers']['<handler>']['port'] = 12201;

//For channel handlers
$sugar_config['logger']['channels']['test_channel']['handlers']['<handler>']['host'] = '127.0.0.1';
$sugar_config['logger']['channels']['test_channel']['handlers']['<handler>']['port'] = 12201;
$sugar_config['logger']['channels']['test_channel']['handlers']['<handler>']['level'] = 'debug';













Note: For more information, please refer to the logger.channels.channel.handlers documentation.

Formatters

Formatters are a component of the Handler Object. By default Sugar only comes with a single Formatter, which is the BackwardCompatibleFormatter used by the File Handler, which simply assures that Log messages are consistent with the legacy Logging functionality. Formatters are used and built out in accordance with the Monolog framework, and under the majority of circumstances, building out a custom formatter is not necessary. For more information on Formatters, you can review the Monolog repository.

Processors

Processors provide a way to add more information to Log messages, without having to hard code this information inside the Handler, so that it can be used only when necessary. For example, Sugar's PSR-3 Logging implementation provides two default Processors that can be enabled for a given channel or handler via configuration. These Processors provide functionality such as adding a stack trace or the web request information to the log message to provide further debugging context.

Factory Interface

The Factory Interface for processors is used to implement a Logging Processor, so that the Logger Factory can build out the configured handler for a channel, without a lot of work from external developers. The Processor Factory Interface is located in ./src/Logger/Processor/Factory.php or in code at \Sugarcrm\Sugarcrm\Logger\Handler\Factory namespace.

Methods

There is only one method to implement for a Processor Factory, which is the create() method. This method will contain the necessary Logic to set up the Processor object. 

create($config)
Arguments
Name Type Description
$config Array The config parameters set for the processor in the Sugar config file
Returns

Callable - See Customization section for an example of implementation, otherwise review the included Processor Factory implementations in code in ./src/Logger/Processor/Factory/.

Implementations

By default, Sugar comes with two Processor implementations, \Sugarcrm\Sugarcrm\Logger\Processor\BacktraceProcessor and \Sugarcrm\Sugarcrm\Logger\Processor\RequestProcessor

BacktraceProcessor

As the name implies, the BacktraceProcessor appends a backtrace or stack trace to the log message output, to easily trace where and how the log message came from. The Factory implementation for this Processor lives in ./src/Logger/Processor/Factory/Backtrace.php, and is referenced in the sugar config as backtrace.

The following shows an example of the output that occurs when utilizing this processor:

                              Fri Mar 23 09:24:19 2018 [90627][1][FATAL] <log message>; Call Trace: \n0: /var/www/Ent/71110/custom/SugarQueryLogger.php:43 - Sugarcrm\Sugarcrm\Logger\BackwardCompatibleAdapter::fatal()\n2: /var/www/Ent/71110/include/utils/LogicHook.php:270\n3: /var/www/Ent/71110/include/utils/LogicHook.php:160 - LogicHook::process_hooks()\n4: /var/www/Ent/71110/data/SugarBean.php:6684 - LogicHook::call_custom_logic()\n5: /var/www/Ent/71110/data/SugarBean.php:3317 - SugarBean::call_custom_logic()\n6: /var/www/Ent/71110/clients/base/api/FilterApi.php:632 - SugarBean::fetchFromQuery()\n7: /var/www/Ent/71110/clients/base/api/FilterApi.php:397 - FilterApi::runQuery()\n8: /var/www/Ent/71110/include/api/RestService.php:257 - FilterApi::filterList()\n9: /var/www/Ent/71110/api/rest.php:23 - RestService::execute()













Note: when viewing complex logs, you can use the following to print log entries in a more human readable format:

                              cat sugarcrm.log | sed s/\\n/\n/g













RequestProcessor

The RequestProcessor appends Session properties to the Log message that would help identify the request that caused the log message. The Factory implementation for this Processor lives in ./src/Logger/Processor/Factory/Request.php, and is referenced in the sugar config as request.

The following list of session properties are appended to the log:

  • User ID
  • Client ID (OAuth2 Client)
  • Platform

The following shows an example of the output that occurs when utilizing this processor:

                              Mon Mar 26 09:48:58 2018 [5930][1][FATAL] <log message>; User ID=1; Client ID=sugar; Platform=base













Configuration

To configure the default Logging Handler for Sugar to utilize the built-in processors:

                              //Configure a single Processor
$sugar_config['logger']['channels']['default']['processors'] = 'backtrace';

//Configure multiple Processors
$sugar_config['logger']['channels']['default']['processors'] = array('backtrace','request');













You can also configure different channels to utilize the processors:

                              //Single Processors
$sugar_config['logger']['channels']['<channel>']['processors'] = 'backtrace';

//Multiple Channels
$sugar_config['logger']['channels']['<channel>']['processors'] = array('backtrace','request');













To pass configuration properties to a Processor your configuration will need to look as follows:

                              $sugar_config['logger']['channels']['<channel>']['processors']['<processor>']['config_key'] = 'test_value';













Note: For more information, please refer to the logger.channels.channel.processors documentation.

Usage

As previously mentioned the Logger Factory allows for multiple channels to be configured independently of each other. The following examples will showcase how to use the Logger Factory to get a channel's Logger and use it in code, as well as how to configure the system to use multiple channels with different configurations.

Basic Usage

                              use \Sugarcrm\Sugarcrm\Logger\Factory;

//Retrieve the default Logger
$DefaultLogger = Factory::getLogger('default');
$DefaultLogger->alert('This is a log message');













Configuring Channels

The following is an example of the ./config_override.php file that would configure two different channels at different log levels, using the logger.channel.channel.level configuration setting. These two channels would allow for portions of the code to Log messages at Debug (and higher) levels, and other portions to only log Info (and higher) levels.

                              $config['logger']['channels']['default'] = array(
    'level' => 'alert'
);
$config['logger']['channels']['channel1'] = array(
    'level' => 'debug'
);













The following code example shows how to retrieve the above-configured channels and use the Logger for each channel. 

                              use \Sugarcrm\Sugarcrm\Logger\Factory;

//Retrieve the default Logger
$DefaultLogger = Factory::getLogger('default');
$DefaultLogger->info("This message will not display");

//Channel1 Logger
$Channel1Logger = Factory::getLogger('channel1');
$Channel1Logger->info("This message will display");













In the example above, assuming a stock system with the previously mentioned config values set in config_override.php, the default channel logger would be set to Alert level logging, and therefore would not add the info level log to the Log file, however, the channel11 Logger would add the log message to the Sugar log file, since it is configured at the info log level.

Default Channels

By default, Sugar has a few areas of the system that utilize a different channel than the default. The usage of these channels means that you can configure the log level and processors differently for those areas, without inundating the log file with lower level logs from other areas of the system.

Channel Description
authentication This logger channel is utilized by the AuthenticationController, and can show useful information about failed login attempts. 
input_validation This logger channel is utilized by the Input Validation framework and can display information regarding failed validations.
metadata This logger channel is utilized by the MetadataManager and mainly provides information on when rebuilds occur.
rest This channel is utilized by the RestService object.
db This channel is utilized by the databse for query logging.

Customization

You can use custom channels where ever you would like in your customizations, and configure them as outlined above, but if you need to send logs somewhere other than the Sugar log file, you will need to build out your own Handler. The following will walk through adding a custom Logging Handler that utilizes Monologs built-in Chrome Logger.

Adding a Custom Handler

Create the following file in ./custom/src/Logger/Handler/Factory/ folder.

./custom/src/Logger/Handler/Factory/Chrome.php

                              <?php

namespace Sugarcrm\Sugarcrm\custom\Logger\Handler\Factory;

use Monolog\Handler\ChromePHPHandler;
use Sugarcrm\Sugarcrm\Logger\Handler\Factory;

class Chrome implements Factory
{

    public function create($level, array $config)
    {
        return new ChromePHPHandler($level);
    }
}













Once you have the new class in place, you will need to run a Quick Repair and Rebuild so that the new class is auto-loaded correctly. Then you can configure the handler for use in the Sugar config:

                              $sugar_config['logger']['channels']['<channel>']['handlers'] = 'Chrome';













 Note: For more information, please refer to the logger.channels.channel.handlers documentation

                              use \Sugarcrm\Sugarcrm\Logger\Factory;

$Logger = Factory::getLogger('default');