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';
}
}