diff --git a/.docker/php/Dockerfile b/.docker/php/Dockerfile new file mode 100644 index 0000000..600190b --- /dev/null +++ b/.docker/php/Dockerfile @@ -0,0 +1,11 @@ +FROM php:8.1-cli-alpine AS php-base + +# Install composer +ENV COMPOSER_ALLOW_SUPERUSER 1 +ENV COMPOSER_HOME /var/www/.composer + +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +RUN apk add --update --no-cache --virtual .build-deps linux-headers $PHPIZE_DEPS && \ + pecl install xdebug && docker-php-ext-enable xdebug && \ + apk del .build-deps \ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f6773ec --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +/.docker export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/compose.yaml export-ignore +/README.md export-ignore +/phpunit.xml.dist export-ignore +/tests export-ignore +/QUICKSTART.md export-ignore +/phpstan.neon export-ignore +/Makefile export-ignore +/phpcs.xml export-ignore +/CHANGELOG.md export-ignore +/examples.php export-ignore +/qodana.yaml export-ignore \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..311cb5d --- /dev/null +++ b/compose.yaml @@ -0,0 +1,13 @@ +services: + php: + build: + dockerfile: .docker/php/Dockerfile + context: . + environment: + COMPOSER_HOME: /.composer + volumes: + - .:/app + - ~/.composer:/.composer + user: 1000:1000 + working_dir: /app + command: tail -f /dev/null \ No newline at end of file diff --git a/composer.json b/composer.json index e5f8ead..1a1f02a 100644 --- a/composer.json +++ b/composer.json @@ -24,12 +24,17 @@ "require-dev": { "phpunit/phpunit": "^10.0", "phpstan/phpstan": "^1.10", - "squizlabs/php_codesniffer": "^3.7" + "squizlabs/php_codesniffer": "^3.7", + "php-parallel-lint/php-parallel-lint": "^1.4" }, "autoload": { "psr-4": { "Logdash\\": "src/" - } + }, + "files": [ + "src/Sync/CreateLogSync.php", + "src/Metrics/CreateMetrics.php" + ] }, "autoload-dev": { "psr-4": { @@ -40,6 +45,15 @@ "test": "phpunit", "phpstan": "phpstan analyse", "cs": "phpcs", - "cbf": "phpcbf" + "cbf": "phpcbf", + "qa": { + "Codestyle": "@cs", + "Lint": "parallel-lint src tests", + "Static analyse": "@phpstan", + "Tests": "@test" + }, + "qa:fix": { + "Codestyle": "@cbf" + } } } diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..215af62 --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,32 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +php: + version: 8.1 #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-php:2025.1 diff --git a/src/Logdash.php b/src/Logdash.php index 5981ac3..98aa5e5 100644 --- a/src/Logdash.php +++ b/src/Logdash.php @@ -7,12 +7,7 @@ use Logdash\Logger\Logger; use Logdash\Metrics\BaseMetrics; use Logdash\Types\InitializationParams; -use Logdash\Types\RequiredInitializationParams; - -require_once __DIR__ . '/Types/LogLevel.php'; -require_once __DIR__ . '/Logger/InternalLogger.php'; -require_once __DIR__ . '/Metrics/CreateMetrics.php'; -require_once __DIR__ . '/Sync/CreateLogSync.php'; +use Logdash\Types\LogLevel; use function Logdash\Metrics\createMetrics; use function Logdash\Sync\createLogSync; @@ -30,6 +25,9 @@ private function __construct( $this->metrics = $metrics; } + /** + * @param array{apiKey?:string, host?:string, verbose?:bool}|null $params + */ public static function create(?array $params = null): self { $initParams = new InitializationParams( diff --git a/src/Logger/InternalLogger.php b/src/Logger/InternalLogger.php index 1185c1d..cf3041a 100644 --- a/src/Logger/InternalLogger.php +++ b/src/Logger/InternalLogger.php @@ -4,18 +4,19 @@ namespace Logdash\Logger; -use Logdash\LogLevel; +use Logdash\Types\LogLevel; class InternalLogger { + private static self $instance; private const LOG_LEVEL_COLORS = [ - LogLevel::ERROR->value => [231, 0, 11], - LogLevel::WARN->value => [254, 154, 0], - LogLevel::INFO->value => [21, 93, 252], - LogLevel::HTTP->value => [0, 166, 166], - LogLevel::VERBOSE->value => [0, 166, 0], - LogLevel::DEBUG->value => [0, 166, 0], - LogLevel::SILLY->value => [80, 80, 80], + 'error' => [231, 0, 11], + 'warning' => [254, 154, 0], + 'info' => [21, 93, 252], + 'http' => [0, 166, 166], + 'verbose' => [0, 166, 0], + 'debug' => [0, 166, 0], + 'silly' => [80, 80, 80], ]; public function log(string ...$data): void @@ -39,17 +40,17 @@ private function internalLog(LogLevel $level, string $message): void $color[2], strtoupper($level->value) . ' ' ); - - echo "{$datePrefix} {$levelPrefix}{$message}" . PHP_EOL; + + file_put_contents( + 'php://stdout', + "{$datePrefix} {$levelPrefix}{$message}" . PHP_EOL + ); } -} -// Create a global instance function -function getInternalLogger(): InternalLogger -{ - static $instance = null; - if ($instance === null) { - $instance = new InternalLogger(); + public static function getInternalLogger(): InternalLogger + { + return self::$instance ??= new self(); } - return $instance; } + +// Create a global instance function diff --git a/src/Logger/Logger.php b/src/Logger/Logger.php index 6b40ece..8544044 100644 --- a/src/Logger/Logger.php +++ b/src/Logger/Logger.php @@ -4,18 +4,18 @@ namespace Logdash\Logger; -use Logdash\LogLevel; +use Logdash\Types\LogLevel; class Logger { private const LOG_LEVEL_COLORS = [ - LogLevel::ERROR->value => [231, 0, 11], - LogLevel::WARN->value => [254, 154, 0], - LogLevel::INFO->value => [21, 93, 252], - LogLevel::HTTP->value => [0, 166, 166], - LogLevel::VERBOSE->value => [0, 166, 0], - LogLevel::DEBUG->value => [0, 166, 0], - LogLevel::SILLY->value => [80, 80, 80], + 'error' => [231, 0, 11], + 'warning' => [254, 154, 0], + 'info' => [21, 93, 252], + 'http' => [0, 166, 166], + 'verbose' => [0, 166, 0], + 'debug' => [0, 166, 0], + 'silly' => [80, 80, 80], ]; private readonly mixed $logMethod; @@ -100,8 +100,11 @@ private function internalLog(LogLevel $level, string $message): void ); $formattedMessage = "{$datePrefix} {$prefix}{$message}"; - $logMethod = $this->logMethod ?? function(string $msg): void { - echo $msg . PHP_EOL; + $logMethod = $this->logMethod ?? function (string $msg): void { + file_put_contents( + 'php://stdout', + $msg . PHP_EOL + ); }; $logMethod($formattedMessage); @@ -120,6 +123,10 @@ private function getPrefix(LogLevel $level): string return strtoupper($level->value) . ' '; } + /** + * @param array $data + * @return array + */ private function convertToStrings(array $data): array { return array_map(function ($item): string { diff --git a/src/Metrics/BaseMetrics.php b/src/Metrics/BaseMetrics.php index 42deb5e..412460d 100644 --- a/src/Metrics/BaseMetrics.php +++ b/src/Metrics/BaseMetrics.php @@ -7,6 +7,6 @@ interface BaseMetrics { public function set(string $key, float $value): void; - + public function mutate(string $key, float $value): void; } diff --git a/src/Metrics/MetricOperation.php b/src/Metrics/MetricOperation.php new file mode 100644 index 0000000..be0ab72 --- /dev/null +++ b/src/Metrics/MetricOperation.php @@ -0,0 +1,9 @@ +params->verbose) { - \Logdash\Logger\getInternalLogger()->verbose("Setting metric {$name} to {$value}"); + InternalLogger::getInternalLogger()->verbose("Setting metric {$name} to {$value}"); } $this->sendMetric($name, $value, MetricOperation::SET); @@ -36,7 +31,7 @@ public function set(string $name, float $value): void public function mutate(string $name, float $value): void { if ($this->params->verbose) { - \Logdash\Logger\getInternalLogger()->verbose("Mutating metric {$name} by {$value}"); + InternalLogger::getInternalLogger()->verbose("Mutating metric {$name} by {$value}"); } $this->sendMetric($name, $value, MetricOperation::CHANGE); @@ -59,7 +54,7 @@ private function sendMetric(string $name, float $value, MetricOperation $operati ]); } catch (GuzzleException $e) { if ($this->params->verbose) { - \Logdash\Logger\getInternalLogger()->verbose("Failed to send metric: " . $e->getMessage()); + InternalLogger::getInternalLogger()->verbose("Failed to send metric: " . $e->getMessage()); } // Fail silently in production } diff --git a/src/Sync/CreateLogSync.php b/src/Sync/CreateLogSync.php index 8b5dc10..f6aeaab 100644 --- a/src/Sync/CreateLogSync.php +++ b/src/Sync/CreateLogSync.php @@ -9,7 +9,8 @@ function createLogSync(RequiredInitializationParams $params): LogSync { if (empty($params->apiKey)) { - \Logdash\Logger\getInternalLogger()->log( + \Logdash\Logger\InternalLogger::getInternalLogger()->log( + // phpcs:ignore 'Api key was not provided in the InitializationParams when calling Logdash::create(), using only local logger.' ); return new NoopLogSync(); diff --git a/src/Sync/HttpLogSync.php b/src/Sync/HttpLogSync.php index a58b07b..2eb9896 100644 --- a/src/Sync/HttpLogSync.php +++ b/src/Sync/HttpLogSync.php @@ -6,7 +6,8 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; -use Logdash\LogLevel; +use Logdash\Logger\InternalLogger; +use Logdash\Types\LogLevel; use Logdash\Types\RequiredInitializationParams; class HttpLogSync implements LogSync @@ -44,7 +45,7 @@ public function send(string $message, LogLevel $level, string $createdAt): void ]); } catch (GuzzleException $e) { if ($this->params->verbose) { - \Logdash\Logger\getInternalLogger()->verbose("Failed to send log: " . $e->getMessage()); + InternalLogger::getInternalLogger()->verbose("Failed to send log: " . $e->getMessage()); } // Fail silently in production } diff --git a/src/Sync/LogSync.php b/src/Sync/LogSync.php index 119d843..973d73d 100644 --- a/src/Sync/LogSync.php +++ b/src/Sync/LogSync.php @@ -4,7 +4,7 @@ namespace Logdash\Sync; -use Logdash\LogLevel; +use Logdash\Types\LogLevel; interface LogSync { diff --git a/src/Sync/NoopLogSync.php b/src/Sync/NoopLogSync.php index 904b5ba..baa4cef 100644 --- a/src/Sync/NoopLogSync.php +++ b/src/Sync/NoopLogSync.php @@ -4,7 +4,7 @@ namespace Logdash\Sync; -use Logdash\LogLevel; +use Logdash\Types\LogLevel; class NoopLogSync implements LogSync { diff --git a/src/Types/InitializationParams.php b/src/Types/InitializationParams.php index 14581d6..6fceda1 100644 --- a/src/Types/InitializationParams.php +++ b/src/Types/InitializationParams.php @@ -10,7 +10,8 @@ public function __construct( public readonly ?string $apiKey = null, public readonly string $host = 'https://api.logdash.io', public readonly bool $verbose = false - ) {} + ) { + } public function toRequired(): RequiredInitializationParams { @@ -21,12 +22,3 @@ public function toRequired(): RequiredInitializationParams ); } } - -class RequiredInitializationParams -{ - public function __construct( - public readonly string $apiKey, - public readonly string $host, - public readonly bool $verbose - ) {} -} diff --git a/src/Types/LogLevel.php b/src/Types/LogLevel.php index 41a133c..62327f3 100644 --- a/src/Types/LogLevel.php +++ b/src/Types/LogLevel.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Logdash; +namespace Logdash\Types; enum LogLevel: string { diff --git a/src/Types/RequiredInitializationParams.php b/src/Types/RequiredInitializationParams.php new file mode 100644 index 0000000..ae21d81 --- /dev/null +++ b/src/Types/RequiredInitializationParams.php @@ -0,0 +1,13 @@ +assertInstanceOf(Logdash::class, $logdash); $this->assertNotNull($logdash->logger()); $this->assertNotNull($logdash->metrics()); @@ -26,7 +26,7 @@ public function testCreateWithApiKey(): void 'host' => 'https://test.logdash.io', 'verbose' => true ]); - + $this->assertInstanceOf(Logdash::class, $logdash); $this->assertNotNull($logdash->logger()); $this->assertNotNull($logdash->metrics());