Navigation

Logger

Spryker contains a PSR-3 compatible logger which can be customized to any needs.

You can use the logger everywhere in the system, as in the example below:

<?php

use Spryker\Shared\Log\LoggerTrait;

class MyClass
{

    use LoggerTrait;

    public function myFunction()
    {
        // This will use the default logger configuration to add a new info message into the log file.
        $this->getLogger()->info('Lorem ipsum dolor sit amet...');
    }

}

According to PSR-3 there are 8 log levels:

Log level Purpose

emergency

System is unusable.

alert

Action must be taken immediately.

critical

Critical conditions.

error

Runtime errors that do not require immediate action but should typically be logged and monitored.

warning

Exceptional occurrences that are not errors.

notice

Normal but significant events.

info

Interesting events.

debug

Detailed debug information.

We recommend to use a trait instead of direct usage of the logger. This way you can mock the logger in unit tests.

<?php

use Psr\Log\LoggerInterface;

...
$loggerMock = $this->getMockBuilder(LoggerInterface::class)->getMock();
...

What is Logged by Default?

By default the Log module has a default configuration according to which, it will log everything into a file, up from a specified log level (both defined in the project config). The following configurations can be modified in the project config.

<?php
// path of the log file
$config[LogConstants::LOG_FILE_PATH] = APPLICATION_ROOT_DIR . '/data/DE/logs/application.log';
// Log level (higher level messages will be logged as well)
$config[LogConstants::LOG_LEVEL] = Monolog\Logger::ERROR;

For a more detailed logging, the Application module provides a SprykerLoggerConfig which adds some data to every log entry and uses some processors to manipulate data.

E.g. sanitizing entity data when a propel entity is passed to the logger context ($this->getLogger()->info('Message', ['entity' => $myEntity])).

Custom Logging Configuration

Every configuration defines a different logger “channel”. Each channel contains a stack of HandlerInterface which will be used to define how the logger should work(e.g. where to log what level, etc.).

<?php

use Monolog\Handler\FingersCrossedHandler;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\SlackHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

MyLoggerConfig implements LoggerConfigInterface
{

    /**
     * @return string
     */
    public function getChannelName()
    {
        // Make sure this string is not used already for other logger channels
        // otherwise it won't work as expected.
        return 'custom logger';
    }

    /**
     * @return HandlerInterface[]
     */
    public function getHandlers()
    {
        return [
            new StreamHandler('/log/file/path', Logger::INFO),
            new FingersCrossedHandler(
                new SlackHandler('mysecrettoken', 'channel'),
                new ErrorLevelActivationStrategy(Logger::CRITICAL)
            ),
        ];
    }

    /**
     * @return \callable[]
     */
    public function getProcessors()
    {
        return [];
    }

}

The configuration in the example above will log every message to a file which is higher than INFO level and use the FingersCrossed wrapper to log into a Slack channel if a CRITICAL level event occurs.

For additional information about the available monolog handlers and processors, please read the following documentation.

You can also have separated configurations for Yves and Zed. To use separated configs you need to add dedicated LoggerConfig plugins to your configuration.


<?php

...

// ---------- Logging
$config[LogConstants::LOGGER_CONFIG_ZED] = MyZedLoggerConfigPlugin::class;
$config[LogConstants::LOGGER_CONFIG_YVES] = MyYvesLoggerConfigPlugin::class;

...

        

Those plugins need to implement the LoggerConfigInterface from the Log module

Enable Detailed Logging

The log code is implemented in a way that it’s very easy to enable/disable specific logging. The ServiceProvider and other mechanisms are used in order to achieve this.

Request / Response Logging

If you want to log all the requests and responses, you just need to add the KernelLogServiceProvider to your application bootstrap. This will then log all master requests.

The request message will look like:

WEB Request Yves [GET] /foo/bar

and the response message:

WEB Response Yves [200]

ZedRequest / ZedResponse Logging

Yves sometimes calls Zed, if you want to get detailed information about what is going on, you just need to add the ZedRequestLogServiceProvider from the ZedRequest module to your Yves bootstrap.

This will add a middleware to Guzzle, which logs all calls to Zed and their responses.

The request message will look like this:

Transfer request [POST] /foo/bar

The response message will look like this:

Transfer response [200]

Propel Logging

By default ObjectBuilder is set by demoshop propel configuration, which does NOT log outgoing queries.

If you want to log the save, update and delete operations of your entities, you need to use ObjectBuilderWithLogger::class as builder in your config_propel.php. This will add the logger to the generated entities.

<?php
$config[PropelConstants::PROPEL] = [
    ...
    'generator' => [
        ...
        'objectModel' => [
            ...
            'builders' => [
                ...
                'object' => ObjectBuilderWithLogger::class,
                ...
            ]
        ]
    ]
    ...
];

The log messages will look like:

Entity save (new) Entity save (update) or Entity delete

Console Command Logging

If you want to log the execution of console commands, you just need to add the ConsoleLogPlugin as event subscriber to your ConsoleDependencyProvider as described in Console.

The log messages will look like this:

CLI command "my:command" started CLI command "my:command" terminated

and if something goes wrong:

CLI command "my:command" exception, message "Something went wrong"

Spryker collects all incoming and all saved data. This way you make sure that all data is persisted and can be used for analysis.

  • All incoming data (GET and POST data from HTTP request)
  • All data that is transferred between Yves and Zed
  • All data that is stored to entities
  • All errors that reach the global error handler

If you want to have different logging functionality, then you can create your own implementation of Spryker\Shared\Log\Config\LoggerConfigInterface.

<?php

use Spryker\Shared\Log\LoggerTrait;

class MyClass
{
    use LoggerTrait;

    public function myFunction()
    {
        // This will use MyLoggerConfig configuration to log a new warning message in the appropriate way.
        $this->getLogger(new MyLoggerConfig())->warning('Lorem ipsum dolog sit amet...');
    }
}

Tip: You could even create a different LoggerTrait to avoid instantiating and passing the config as parameter all the time. This might be useful on project level to implement a global logging logic and use it all around the code.

Handler

You can use all handler shipped with Monolog within your Logger configuration. Spryker also ships with special handlers to be used in the Spryker context.

QueueHandler

Yves and Zed provide a QueueHandler to be used for log messages in a queue. This handler should always be used inside the BufferHandler from Monolog.

Using the QueueHandler is pretty easy. You only need to pass the QueueClient and a name of the queue you want to send your log messages to.

Currently, there is a Loggly module in the SprykerEco namespace you can use to send your log messages from the queue to the Loggly Service. For more details go to Loggly Queue Integration

Performance

Always keep an eye on your performance when you change the configuration for logging. The already mentioned BufferHandler is always something you should consider in your log environment. Logging can slow your system dramatically down.

Configuration

Spryker uses Monolog which supports several configuration options.

With Spryker you can configure them in the config-files.

 

See also:

 

Last review date: Oct. 23rd, 2017