Last active
May 7, 2026 14:22
-
-
Save aklump/a90467ce56d3f555c2af6f8c9a11e159 to your computer and use it in GitHub Desktop.
Provides class-scoped logging helpers for Drupal custom classes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| use Drupal\Core\Logger\LoggerChannelInterface; | |
| use Psr\Log\LogLevel; | |
| /** | |
| * Provides class-scoped logging helpers. | |
| * | |
| * Classes using this trait get a logger channel derived from the consuming | |
| * class name. The channel uses the short class name so Drupal's log UI remains | |
| * easy to scan and filter. Long class names are shortened and given a stable | |
| * hash suffix to stay within watchdog channel length limits while reducing | |
| * collision risk. | |
| * | |
| * The logger channel name is public so callers can use the same class-derived | |
| * channel with Drupal's logging APIs directly when they need behavior not | |
| * provided by this trait. | |
| * | |
| * The exception helper is intentionally lightweight and PSR-3 oriented. It is | |
| * intended for quick, severity-aware exception logging during normal | |
| * application flow. Use \Drupal\Core\Utility\Error::logException() directly | |
| * when Drupal's full exception logging format is required. | |
| */ | |
| trait ClassLoggerTrait { | |
| /** | |
| * Gets the logger channel name for the calling class. | |
| * | |
| * The short class name is used because Drupal's log UI exposes the channel as | |
| * a primary scanning/filtering column. Long names are shortened with a hash | |
| * suffix to avoid watchdog channel length issues while reducing collision | |
| * risk. | |
| */ | |
| public static function loggerChannelName(): string { | |
| static $channels = []; | |
| $class = static::class; | |
| if (!isset($channels[$class])) { | |
| $namespace_position = strrpos($class, '\\'); | |
| $short_name = $namespace_position === FALSE | |
| ? $class | |
| : substr($class, $namespace_position + 1); | |
| if (strlen($short_name) > 32) { | |
| $short_name = substr($short_name, 0, 25) . '_' . substr(hash('crc32b', $class), 0, 6); | |
| } | |
| $channels[$class] = $short_name; | |
| } | |
| return $channels[$class]; | |
| } | |
| /** | |
| * Gets the class logger. | |
| * | |
| * Intentionally do not statically cache the logger factory. Drupal's | |
| * container already returns shared service instances, and keeping our own | |
| * copy can make tests/container rebuilds harder to reason about for | |
| * negligible gain. | |
| */ | |
| protected static function logger(): LoggerChannelInterface { | |
| return \Drupal::logger(static::loggerChannelName()); | |
| } | |
| /** | |
| * Logs an exception using the class-derived logger channel. | |
| * | |
| * @param \Throwable $exception | |
| * The exception or error to log. | |
| * @param string $level | |
| * A PSR-3 log level from \Psr\Log\LogLevel. | |
| * @param array $context | |
| * Additional context variables for the log message. | |
| */ | |
| protected static function logException(\Throwable $exception, string $level = LogLevel::ERROR, array $context = []): void { | |
| if ($exception instanceof \Exception) { | |
| watchdog_exception(static::loggerChannelName(), $exception, NULL, $context, $level); | |
| return; | |
| } | |
| // Drupal 9's watchdog_exception() only accepts \Exception. Log other | |
| // \Throwable instances directly until this can use Error::logException(). | |
| $context += [ | |
| 'exception' => $exception, | |
| '@type' => get_class($exception), | |
| '@message' => $exception->getMessage(), | |
| '@file' => $exception->getFile(), | |
| '@line' => $exception->getLine(), | |
| ]; | |
| static::logger()->log($level, '@type: @message in @file on line @line', $context); | |
| // TODO Drupal 10+: replace this method body with: | |
| // \Drupal\Core\Utility\Error::logException(static::logger(), $exception, NULL, $context, $level); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment