Logger
Instantiation
Constructor injection can be used to automatically instantiate the logger:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Service;
use Psr\Log\LoggerInterface;
class MyClass
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
}
Tip
For examples of instantiation with Logger
or
General
, switch to an older TYPO3 version for
this page. Instantiation with dependency injection
is now the recommended procedure. Also see the section on
channels for information on grouping classes in
channels.
The log() method
The \TYPO3\
class provides a central point for
submitting log messages, the log
method:
$this->logger->log($level, $message, $data);
which takes three parameters:
$level
-
- Type
- integer
One of the defined log levels, see the section Log levels and shorthand methods.
$message
-
- Type
- string |
\Stringable
The log message itself.
$data
-
- Type
- array
Optional parameter, it can contain additional data, which is added to the log record in the form of an array.
An early return in the log
method prevents unneeded computation work to
be done. So you are safe to call the logger with the debug log level frequently
without slowing down your code too much. The logger will know by its
configuration, what the most explicit severity level is.
As a next step, all registered processors are notified. They can modify the log records or add extra information.
The logger then forwards the log records to all of its configured writers, which will then persist the log record.
Log levels and shorthand methods
The log levels - according to RFC 3164 - start from the lowest level.
For each of the severity levels mentioned below, a shorthand method exists in
\TYPO3\
:
Debug
-
- Class constant
\Psr\
Log\ Log Level:: DEBUG - Shorthand method
$this->logger->debug
($message, $context);
For debug information: give detailed status information during the development of PHP code.
Informational
-
- Class constant
\Psr\
Log\ Log Level:: INFO - Shorthand method
$this->logger->info
($message, $context);
For informational messages, some examples:
- A user logs in.
- Connection to third-party system established.
- Logging of SQL statements.
Notice
-
- Class constant
\Psr\
Log\ Log Level:: NOTICE - Shorthand method
$this->logger->notice
($message, $context);
For significant conditions. Things you should have a look at, nothing to worry about though. Some examples:
- A user logs in.
- Logging of SQL statements.
Warning
-
- Class constant
\Psr\
Log\ Log Level:: WARNING - Shorthand method
$this->logger->warning
($message, $context);
For warning conditions. Some examples:
- Use of a deprecated method.
- Undesirable events that are not necessarily wrong.
Error
-
- Class constant
\Psr\
Log\ Log Level:: ERROR - Shorthand method
$this->logger->error
($message, $context);
For error conditions. Some examples:
- A runtime error occurred.
- Some PHP coding error has happened.
- A white screen is shown.
Critical
-
- Class constant
\Psr\
Log\ Log Level:: CRITICAL - Shorthand method
$this->logger->critical
($message, $context);
For critical conditions. Some examples:
- An unexpected exception occurred.
- An important file has not been found.
- Data is corrupt or outdated.
Alert
-
- Class constant
\Psr\
Log\ Log Level:: ALERT - Shorthand method
$this->logger->alert
($message, $context);
For blocking conditions, action must be taken immediately. Some examples:
- The entire website is down.
- The database is unavailable.
Emergency
-
- Class constant
\Psr\
Log\ Log Level:: EMERGENCY - Shorthand method
$this->logger->emergency
($message, $context);
Nothing works, the system is unusable. You will likely not be able to reach the system. You better have a system administrator reachable when this happens.
Channels
It is possible to group several classes into channels, regardless of the PHP namespace.
Services are able to control the component name that an injected logger is created with. This allows to group logs of related classes and is basically a channel system as often used in Monolog.
The \TYPO3\
attribute is supported for
constructor argument injection as a class and
parameter-specific attribute and for \Psr\
dependency injection services as a class attribute.
Registration via class attribute for \Psr\
injection:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Service\MyClass;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Log\Channel;
#[Channel('security')]
class MyClass
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
}
Registration via parameter attribute for \Psr\
injection, overwrites possible class attributes:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\Service\MyClass;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Core\Log\Channel;
class MyClass
{
public function __construct(
#[Channel('security')]
private readonly LoggerInterface $logger,
) {}
}
The instantiated logger will now have the channel "security",
instead of the default one, which would be a combination of namespace and class
of the instantiating class, such as My
.
Using the channel
The channel "security" can then be used in the logging configuration:
use Psr\Log\LogLevel;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Log\Writer\FileWriter;
$GLOBALS['TYPO3_CONF_VARS']['LOG']['security']['writerConfiguration'] = [
LogLevel::DEBUG => [
FileWriter::class => [
'logFile' => Environment::getVarPath() . '/log/security.log'
]
],
];
The written log messages will then have the component name "security"
, such as:
Fri, 21 Jul 2023 16:26:13 +0000 [DEBUG] ... component="security": ...
For more examples for configuring the logging see the Writer configuration section.
Examples
Examples of the usage of the logger can be found in the extension
t3docs/examples
. in file
/Classes/
Best practices
There are no strict rules or guidelines about logging. Still it can be considered to be best practice to follow these rules:
Use placeholders
Adhere to the PSR-3 placeholder specification. This is necessary in order to use proper PSR-3 logging.
Bad example:
$this->logger->alert(
'Password reset requested for email "'
. $emailAddress . '" but was requested too many times.'
);
Good example:
$this->logger->alert(
'Password reset requested for email "{email}" but was requested too many times.',
['email' => $emailAddress]
);
The first argument is the message, the second (optional) argument is a context.
A message can use {placeholders}
. All Core provided log writers will
substitute placeholders in the message with data from the context array,
if a context array key with same name exists.
Meaningful message
The message itself has to be meaningful, for example, exception messages.
Bad example:
"Something went wrong"
Good example:
"Could not connect to database"
Searchable message
Most of the times log entries will be stored. They are most important, if something goes wrong within the system. In such situations people might search for specific issues or situations, considering this while writing log entries will reduce debugging time in future.
Messages should therefore contain keywords that might be used in searches.
Good example:
"Connection to MySQL database could not be established"
This includes "connection", "mysql" and "database" as possible keywords.
Distinguishable and grouped
Log entries might be collected and people might scroll through them. Therefore it is helpful to write log entries that are distinguishable, but are also grouped.
Bad examples:
"Database not reached"
"Could not establish connection to memcache"
Good examples:
"Connection to MySQL database could not be established"
"Connection to memcache could not be established"
This way the same issue is grouped by the same structure, and one can scan the same position for either "MySQL" or "memcache".
Provide useful information
TYPO3 already uses the component of the logger to give some context. Still further individual context might be available that should be added. In case of an exception, the code, stacktrace, file and line number would be helpful.
Keep in mind that it is hard to add information afterwards. Logging is there to get information if something got wrong. All necessary information should be available to get the state of the system and why something happened.