prod/php/ElasticOTel/BootstrapStageLogger.php (173 lines of code) (raw):

<?php /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch B.V. licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /** @noinspection PhpIllegalPsrClassPathInspection */ declare(strict_types=1); namespace Elastic\OTel; use Throwable; use function elastic_otel_log_feature; /** * Code in this file is part of implementation internals, and thus it is not covered by the backward compatibility. * * @internal */ final class BootstrapStageLogger { public const LEVEL_OFF = 0; public const LEVEL_CRITICAL = 1; public const LEVEL_ERROR = 2; public const LEVEL_WARNING = 3; public const LEVEL_INFO = 4; public const LEVEL_DEBUG = 5; public const LEVEL_TRACE = 6; private const LEVEL_AS_STRING = [ self::LEVEL_OFF => 'OFF', self::LEVEL_CRITICAL => 'CRITICAL', self::LEVEL_ERROR => 'ERROR', self::LEVEL_WARNING => 'WARNING', self::LEVEL_INFO => 'INFO', self::LEVEL_DEBUG => 'DEBUG', self::LEVEL_TRACE => 'TRACE', ]; private static int $maxEnabledLevel = self::LEVEL_OFF; private static string $phpSrcCodePathPrefixToRemove; private static string $classNamePrefixToRemove; private static ?int $pid = null; private static ?bool $isStderrDefined = null; private static function levelToString(int $level): string { if (array_key_exists($level, self::LEVEL_AS_STRING)) { return self::LEVEL_AS_STRING[$level]; } return "LEVEL ($level)"; } private static function ensureStdErrIsDefined(): bool { if (self::$isStderrDefined === null) { if (defined('STDERR')) { self::$isStderrDefined = true; } else { define('STDERR', fopen('php://stderr', 'w')); self::$isStderrDefined = defined('STDERR'); } } return self::$isStderrDefined; } /** @noinspection PhpUnused */ public static function writeLineToStdErr(string $text): void { if (self::ensureStdErrIsDefined()) { fwrite(STDERR, $text . PHP_EOL); } } public static function nullableToLog(null|int|string $str): string { return $str === null ? 'null' : strval($str); } public static function configure(int $maxEnabledLevel, string $phpSrcCodeRootDir, string $rootNamespace): void { require __DIR__ . DIRECTORY_SEPARATOR . 'Log' . DIRECTORY_SEPARATOR . 'LogFeature.php'; self::$maxEnabledLevel = $maxEnabledLevel; if (is_int($pid = getmypid())) { self::$pid = $pid; } self::$phpSrcCodePathPrefixToRemove = $phpSrcCodeRootDir . DIRECTORY_SEPARATOR; self::$classNamePrefixToRemove = $rootNamespace . '\\'; self::logDebug( 'Exiting...' . '; maxEnabledLevel: ' . self::levelToString($maxEnabledLevel) . '; phpSrcCodePathPrefixToRemove: ' . self::$phpSrcCodePathPrefixToRemove . '; classNamePrefixToRemove: ' . self::$classNamePrefixToRemove . '; pid: ' . self::nullableToLog(self::$pid), __FILE__, __LINE__, __CLASS__, __FUNCTION__ ); } /** * @noinspection PhpUnused */ public static function logCritical(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_CRITICAL, $message, $file, $line, $class, $func); } /** * @noinspection PhpUnused */ public static function logError(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_ERROR, $message, $file, $line, $class, $func); } /** * @noinspection PhpUnused */ public static function logWarning(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_WARNING, $message, $file, $line, $class, $func); } /** * @noinspection PhpUnused */ public static function logInfo(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_INFO, $message, $file, $line, $class, $func); } /** * @noinspection PhpUnused */ public static function logDebug(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_DEBUG, $message, $file, $line, $class, $func); } /** * @noinspection PhpUnused */ public static function logTrace(string $message, string $file, int $line, string $class, string $func): void { self::logWithLevel(self::LEVEL_TRACE, $message, $file, $line, $class, $func); } public static function isEnabledForLevel(int $statementLevel): bool { return $statementLevel <= self::$maxEnabledLevel; } public static function logCriticalThrowable(Throwable $throwable, string $message, string $file, int $line, string $class, string $func): void { self::logCritical( $message . '.' . ' ' . get_class($throwable) . ': ' . $throwable->getMessage() . PHP_EOL . 'Stack trace:' . PHP_EOL . $throwable->getTraceAsString(), $file, $line, $class, $func ); } private static function isPrefixOf(string $prefix, string $text, bool $isCaseSensitive = true): bool { $prefixLen = strlen($prefix); if ($prefixLen === 0) { return true; } return substr_compare( $text /* <- haystack */, $prefix /* <- needle */, 0 /* <- offset */, $prefixLen /* <- length */, !$isCaseSensitive /* <- case_insensitivity */ ) === 0; } private static function processSourceCodeFilePathForLog(string $file): string { return self::isPrefixOf(self::$phpSrcCodePathPrefixToRemove, $file, /* isCaseSensitive: */ false) ? substr($file, strlen(self::$phpSrcCodePathPrefixToRemove)) : $file; } private static function processClassNameForLog(string $class): string { return self::isPrefixOf(self::$classNamePrefixToRemove, $class, /* isCaseSensitive: */ false) ? substr($class, strlen(self::$classNamePrefixToRemove)) : $class; } private static function processClassFunctionNameForLog(string $class, string $func): string { if ($class === '') { return $func; } return self::processClassNameForLog($class) . '::' . $func; } private static function logWithLevel(int $statementLevel, string $message, string $file, int $line, string $class, string $func): void { if (!self::isEnabledForLevel($statementLevel)) { return; } elastic_otel_log_feature( 0 /* $isForced */, $statementLevel, Log\LogFeature::BOOTSTRAP, self::processSourceCodeFilePathForLog($file), $line, self::processClassFunctionNameForLog($class, $func), $message ); } /** * @noinspection PhpUnused */ public static function possiblySecuritySensitive(mixed $value): mixed { return self::isEnabledForLevel(self::LEVEL_TRACE) ? $value : 'REDACTED (POSSIBLY SECURITY SENSITIVE) DATA'; } }