From a66153e105c66247b2fe612b26b118949259bcf1 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Fri, 30 Aug 2019 12:20:46 +0800 Subject: [PATCH 01/27] add send log with swoole network,support console,seasstash,websocket,kafka --- README.md | 130 ++++++-- composer.json | 82 +++--- docker/docker-compose.yaml | 93 ++++++ docker/superset_config.py | 14 + example/logger.php | 80 +++++ sql/seaslog.sql | 52 ++++ src/AbstractConfig.php | 86 ++++++ src/ArrayHelper.php | 71 +++++ src/ConsoleColor.php | 190 ++++++++++++ src/Context.php | 49 +++ src/Exceptions/Exception.php | 12 + src/HtmlColor.php | 170 +++++++++++ src/Kafka/Broker.php | 115 ++++++++ src/Kafka/Config.php | 221 ++++++++++++++ src/Kafka/InvalidRecordInSet.php | 52 ++++ src/Kafka/Metadata.php | 133 +++++++++ src/Kafka/Produce.php | 300 +++++++++++++++++++ src/Kafka/Producter.php | 166 +++++++++++ src/Kafka/ProducterConfig.php | 87 ++++++ src/Kafka/Protocol.php | 491 +++++++++++++++++++++++++++++++ src/Kafka/ProtocolTool.php | 66 +++++ src/Kafka/RecordValidator.php | 49 +++ src/Kafka/Socket/Pool.php | 111 +++++++ src/Kafka/Socket/SocketIO.php | 106 +++++++ src/Logger.php | 387 +++++++++++++++--------- src/LoggerConfig.php | 203 +++++++++++++ src/SeaslogConfig.php | 70 +++++ src/Targets/AbstractTarget.php | 51 ++++ src/Targets/EchoTarget.php | 29 ++ src/Targets/KafkaTarget.php | 108 +++++++ src/Targets/SeasStashTarget.php | 62 ++++ src/Targets/StyleTarget.php | 130 ++++++++ src/Targets/WebsocketTarget.php | 127 ++++++++ src/functions.php | 25 ++ tests/ArrayHelperTest.php | 35 +++ tests/ConsoleColorTest.php | 28 ++ tests/ContestTest.php | 43 +++ tests/StyleTargetTest.php | 86 ++++++ tests/SwooleLoggerTest.php | 291 ++++++++++++++++++ 39 files changed, 4408 insertions(+), 193 deletions(-) create mode 100644 docker/docker-compose.yaml create mode 100644 docker/superset_config.py create mode 100644 example/logger.php create mode 100644 sql/seaslog.sql create mode 100644 src/AbstractConfig.php create mode 100644 src/ArrayHelper.php create mode 100644 src/ConsoleColor.php create mode 100644 src/Context.php create mode 100644 src/Exceptions/Exception.php create mode 100644 src/HtmlColor.php create mode 100644 src/Kafka/Broker.php create mode 100644 src/Kafka/Config.php create mode 100644 src/Kafka/InvalidRecordInSet.php create mode 100644 src/Kafka/Metadata.php create mode 100644 src/Kafka/Produce.php create mode 100644 src/Kafka/Producter.php create mode 100644 src/Kafka/ProducterConfig.php create mode 100644 src/Kafka/Protocol.php create mode 100644 src/Kafka/ProtocolTool.php create mode 100644 src/Kafka/RecordValidator.php create mode 100644 src/Kafka/Socket/Pool.php create mode 100644 src/Kafka/Socket/SocketIO.php create mode 100644 src/LoggerConfig.php create mode 100644 src/SeaslogConfig.php create mode 100644 src/Targets/AbstractTarget.php create mode 100644 src/Targets/EchoTarget.php create mode 100644 src/Targets/KafkaTarget.php create mode 100644 src/Targets/SeasStashTarget.php create mode 100644 src/Targets/StyleTarget.php create mode 100644 src/Targets/WebsocketTarget.php create mode 100644 src/functions.php create mode 100644 tests/ArrayHelperTest.php create mode 100644 tests/ConsoleColorTest.php create mode 100644 tests/ContestTest.php create mode 100644 tests/StyleTargetTest.php create mode 100644 tests/SwooleLoggerTest.php diff --git a/README.md b/README.md index fb1ea1f..a80f1cc 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,29 @@ Install the latest version with $ composer require seasx/seas-logger ``` -### Basic Usage +简介 +--------- +* 基于`Swoole`的日志组件,采用`Seaslog`日志模板,符合PSR-3,内置多种Target输出,包括`Kafka`,`SeasStash`,`Websocket`,`Console`。 +* 没有做文件`Target`,在高并发下同时写同一个文件,加锁会影响性能,不加锁的方案还没通过,暂时不支持。(有好的方案请多多指教) + +功能 +------------------------ +* 支持多种输出`Target`,可以自定义输出,继承`AbstractTarget`抽象类 +* 可选记录日志等级,`Target`可以单独过滤已选的日志等级日志 +* `Console`,`Websocket`输出支持彩色字体 +* 支持日志Buffer,定时或定量输出。注意:定时或定量输出由于在输出前日志存在内存,宕机会有丢的风险。 +* 支持`Seaslog`,`LoggerConfig`替换为`SeaslogConfig`,需要最新版`Seaslog`(还没发布),暂时可以用`LoggerConfig` + + +食用方式 +---- +* 日志收集建议使用`docker`目录下的`docker-compose.yaml`启动套件,用`sql`目录下的`seaslog.sql`在`Clickhouse`中创建数据库和表,`LoggerConfig`中添加`KafkaTarget`,即可在`Clickhouse`中看到日志 +* 默认带的BI套件为`Superset`,可以做一些分析 +* 生成环境建议`StyleTarget`或者`WebsocketTarget`仅输出`Warning`和`Error`日志或仅保留`KafkaTarget`,使用`SeaslogConfig`(等待最新版本发布)配置,`KafkaTarget`可以输出所有日志 +* `WebsocketTarget`由于各个框架的`Server`获取方式不一致,需要自己注册一个`getServer():?\Swoole\Server`的`function`返回`\Swoole\Server`即可,默认会往所有`Websocket`连接发送日志,如果需要过滤`fd`可以自定义`Target`。 +* 撸码建议采用`DI`依赖注入的方式注册`Logger` + +### 非Swoole用法 ```php warning('Hello'); $logger->error('SeasLogger'); ``` -### configuration for laravel/lumen >=5.6 -add seaslog configuration in config/logging.php +### Swoole用法 ```php -'channels' => [ - ... - 'seaslog' => [ - 'driver' => 'custom', - 'via' => \Seasx\SeasLogger\Logger::class, - 'path' => '/path/to/logfile', - ], - ... -] -``` + new StyleTarget([ + 'info',//过滤等级,默认为[]全部输出 + ]), + 'kafka' => new KafkaTarget( + new Producter( + new ProducterConfig([ + 'requiredAck' => 0, + ]), + new Broker([ + 'brokerVersion' => '1.0.0', + ]), new Pool([ + 'uri' => 'kafka:9092' + ])), [], 'seaslog') + ], [ + 'appName' => 'Seaslog',//应用名:远程发送日志的时候用于区分是哪个应用发送来的 + 'bufferSize' => 1000,//定量:buffer>=时会输出,默认为1 + 'tick' => 3,//定时:每tick秒输出,默认为0,不开启定时 + 'recall_depth' => 2,//与Seaslog配置参数一样,默认为0 + ])); + /** + * 这里可以注册一个回调函数,用来处理RequestID,Request URI,Request Method,Client IP的值 + * 下面是示例代码,具体的设置根据自己需要 + */ + $logger->getConfig()->registerTemplate(function () { + $possibleStyles = (new ConsoleColor())->getPossibleStyles(); + $htmlColors = HtmlColor::getPossibleColors(); + if (($requestVar = Context::get(Logger::CONTEXT_KEY)) === null) { + /** @var Request $serverRequest */ + if (($serverRequest = Context::get('request')) !== null) { + $uri = $serverRequest->getUri(); + $requestId = $serverRequest->getAttribute(AttributeEnum::REQUESTID_ATTRIBUTE); + !$requestId && $requestId = uniqid(); + $requestVar = array_filter([ + '%Q' => $requestId, + '%R' => $uri->getPath(), + '%m' => $serverRequest->getMethod(), + '%I' => ArrayHelper::getValue($serverRequest->getServerParams(), 'remote_addr'), + '%c' => [ + $possibleStyles[rand(0, count($possibleStyles) - 1)], + $htmlColors[rand(0, count($htmlColors) - 1)] + ] + ]); + } else { + $requestVar = array_filter([ + '%Q' => uniqid(), + '%c' => [ + $possibleStyles[rand(0, count($possibleStyles) - 1)], + $htmlColors[rand(0, count($htmlColors) - 1)] + ] + ]); + } + Context::set(Logger::CONTEXT_KEY, $requestVar); + } + return $requestVar; + }); + //这里区别于标准PSR-3,Context占用一个固定key(module),作用与Seaslog的Logger配置一样,默认值为System + $logger->info("test logger", ['module' => 'logger']); +}); -edit .env file to use seaslog -```php -LOG_CHANNEL=seaslog ``` -### See more -[https://github.com/SeasX/SeasLog](https://github.com/SeasX/SeasLog) +配套组件`docker-compose` +------------------------ +* `Clickhouse`日志存储。 +* `ClickhouseWeb`Clickhouse操作Web界面 +* `Zookeeper`Kafka依赖 +* `Kafka`日志队列 +* `Manager`Kafka管理Web界面 +* `Superset`BI分析 +* `mysql`Superset依赖 +* `redis`Superset依赖 +* `Grafana`监控(还没添加) + diff --git a/composer.json b/composer.json index 4582b7d..1b9d6c5 100644 --- a/composer.json +++ b/composer.json @@ -1,41 +1,45 @@ { - "name": "seasx/seas-logger", - "description": "An effective,fast,stable log package for PHP", - "keywords": [ - "log", - "logging", - "psr-3" - ], - "type": "library", - "license": "MIT", - "authors": [ - { - "name": "SeasX", - "homepage": "https://github.com/SeasX" + "name": "seasx/seas-logger", + "description": "An effective,fast,stable log package for PHP", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "SeasX", + "homepage": "https://github.com/SeasX" + } + ], + "require": { + "php": "^7.1", + "psr/log": "^1.0.2", + "lcobucci/clock": "^1.0", + "friendsofphp/php-cs-fixer": "^2.11" + }, + "require-dev": { + "swoft/swoole-ide-helper": "^4.3", + "phpunit/phpunit": "^6.5" + }, + "autoload": { + "psr-4": { + "Seasx\\SeasLogger\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Seasx\\SeasLogger\\Tests\\": "tests/" + } + }, + "scripts": { + "test": [ + "phpunit" + ] } - ], - "require": { - "php": "^7.0", - "psr/log": "^1.0.2", - "ext-SeasLog": "^2.0", - "friendsofphp/php-cs-fixer": "^2.11" - }, - "require-dev": { - "phpunit/phpunit": "^6.5" - }, - "autoload": { - "psr-4": { - "Seasx\\SeasLogger\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Seasx\\SeasLogger\\Tests\\": "tests/" - } - }, - "scripts": { - "test": [ - "phpunit" - ] - } -} +} \ No newline at end of file diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml new file mode 100644 index 0000000..317c08d --- /dev/null +++ b/docker/docker-compose.yaml @@ -0,0 +1,93 @@ +# Use root/example as user/password credentials +version: '3' + +services: + zookeeper: + image: confluentinc/cp-zookeeper:latest + restart: unless-stopped + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + + kafka: + image: confluentinc/cp-kafka:latest + restart: unless-stopped + ports: + - 9092:9092 + - 5555:5555 + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_JMX_PORT: "5555" + depends_on: + - zookeeper + container_name: kafka + + manager: + image: zenko/kafka-manager + restart: unless-stopped + ports: + - 9000:9000 + environment: + ZK_HOSTS: "zookeeper:2181" + depends_on: + - zookeeper + - kafka + + clickhouse: + image: yandex/clickhouse-server + restart: unless-stopped + ports: + - 8123:8123 + - 9000:9000 + - 9009:9009 + ulimits: + nofile: + soft: 262144 + hard: 262144 + + ckweb: + image: spoonest/clickhouse-tabix-web-client + restart: unless-stopped + environment: + CH_NAME: default + CH_HOST: clickhouse:8123 + depends_on: + - clickhouse + ports: + - 8080:80 + + superset: + image: amancevice/superset + restart: unless-stopped + depends_on: + - redis + - mysql + ports: + - 8088:8088 + volumes: + - ./superset_config.py:/etc/superset/superset_config.py + + mysql: + image: mysql:5 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: root + command: --max_connections=512 --explicit_defaults_for_timestamp=1 --sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" + --tmp_table_size=18M --thread_cache_size=8 --query_cache_size=0 --myisam_max_sort_file_size=64G + --myisam_sort_buffer_size=35M --key_buffer_size=25M --read_buffer_size=64K --read_rnd_buffer_size=256K --sort_buffer_size=256K + --max_allowed_packet=128M --innodb_flush_log_at_trx_commit=1 --innodb_log_buffer_size=1M + --innodb_buffer_pool_size=47M --innodb_log_file_size=24M --innodb_thread_concurrency=8 --default-time-zone="+8:00" + ports: + - 3306:3306 + + redis: + image: redis:alpine + restart: unless-stopped + ports: + - 6379:6379 + diff --git a/docker/superset_config.py b/docker/superset_config.py new file mode 100644 index 0000000..d6d9871 --- /dev/null +++ b/docker/superset_config.py @@ -0,0 +1,14 @@ +import os + +MAPBOX_API_KEY = os.getenv('MAPBOX_API_KEY', '') +CACHE_CONFIG = { + 'CACHE_TYPE': 'redis', + 'CACHE_DEFAULT_TIMEOUT': 300, + 'CACHE_KEY_PREFIX': 'superset_', + 'CACHE_REDIS_HOST': 'redis', + 'CACHE_REDIS_PORT': 6379, + 'CACHE_REDIS_DB': 1, + 'CACHE_REDIS_URL': 'redis://redis:6379/1'} +SQLALCHEMY_DATABASE_URI = 'mysql://root:root@mysql:3306/superset' +SQLALCHEMY_TRACK_MODIFICATIONS = True +SECRET_KEY = 'thisISaSECRET_1234' \ No newline at end of file diff --git a/example/logger.php b/example/logger.php new file mode 100644 index 0000000..a37cca9 --- /dev/null +++ b/example/logger.php @@ -0,0 +1,80 @@ + new StyleTarget([ + 'info',//过滤等级,默认为[]全部输出 + ]), + 'kafka' => new KafkaTarget( + new Producter( + new ProducterConfig([ + 'requiredAck' => 0, + ]), + new Broker([ + 'brokerVersion' => '1.0.0', + ]), new Pool([ + 'uri' => '192.168.5.134:9092' + ])), [], 'seaslog_test') + ], [ + 'appName' => 'Seaslog',//应用名:远程发送日志的时候用于区分是哪个应用发送来的 + 'bufferSize' => 1,//定量:buffer>=时会输出,默认为1,每次记录都会输出 + 'tick' => 0,//定时:每tick秒输出,默认为0,不开启定时 + 'recall_depth' => 2,//与Seaslog配置参数一样,默认为0 + ])); + /** + * 这里可以注册两个回调函数,会在log方法前执行,可以用来处理RequestID,Request URI,Request Method,Client IP的值 + * 下面是示例代码,具体的设置根据自己需要 + */ + $logger->getConfig()->registerTemplate(function () { + $possibleStyles = (new ConsoleColor())->getPossibleStyles(); + $htmlColors = HtmlColor::getPossibleColors(); + if (($requestVar = Context::get(Logger::CONTEXT_KEY)) === null) { + /** @var Request $serverRequest */ + if (($serverRequest = Context::get('request')) !== null) { + $uri = $serverRequest->getUri(); + $requestId = $serverRequest->getAttribute(AttributeEnum::REQUESTID_ATTRIBUTE); + !$requestId && $requestId = uniqid(); + $requestVar = array_filter([ + '%Q' => $requestId, + '%R' => $uri->getPath(), + '%m' => $serverRequest->getMethod(), + '%I' => ArrayHelper::getValue($serverRequest->getServerParams(), 'remote_addr'), + '%c' => [ + $possibleStyles[rand(0, count($possibleStyles) - 1)], + $htmlColors[rand(0, count($htmlColors) - 1)] + ] + ]); + } else { + $requestVar = array_filter([ + '%Q' => uniqid(), + '%c' => [ + $possibleStyles[rand(0, count($possibleStyles) - 1)], + $htmlColors[rand(0, count($htmlColors) - 1)] + ] + ]); + } + Context::set(Logger::CONTEXT_KEY, $requestVar); + } + return $requestVar; + }); + //这里区别于标准PSR-3,Context占用一个固定key(module),作用与Seaslog的Logger参数一样,默认值为System + for ($i = 0; $i < 100; $i++) { + $logger->info("test logger $i", ['module' => 'logger']); + } +}); \ No newline at end of file diff --git a/sql/seaslog.sql b/sql/seaslog.sql new file mode 100644 index 0000000..e9b0946 --- /dev/null +++ b/sql/seaslog.sql @@ -0,0 +1,52 @@ +create database if not exists logs; + +CREATE TABLE if not exists logs.seaslog +( + `appname` String, + `datetime` DateTime, + `level` String, + `request_uri` String, + `request_method` String, + `clientip` String, + `requestid` String, + `filename` String, + `memoryusage` UInt64, + `message` String +) ENGINE = MergeTree PARTITION BY toYYYYMM(datetime) + ORDER BY + (datetime, + appname) SETTINGS index_granularity = 8192; + +CREATE MATERIALIZED VIEW if not exists logs.consumer_seaslog TO logs.seaslog AS +SELECT appname, + datetime, + level, + request_uri, + request_method, + clientip, + requestid, + filename, + memoryusage, + message +FROM logs.kafka_seaslog; + +CREATE TABLE if not exists logs.kafka_seaslog +( + `appname` String, + `datetime` DateTime, + `level` String, + `request_uri` String, + `request_method` String, + `clientip` String, + `requestid` String, + `filename` String, + `memoryusage` UInt64, + `message` String +) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka:29092', + kafka_topic_list = 'seaslog', + kafka_group_name = 'clickhouse', + kafka_format = 'JSONEachRow', + kafka_skip_broken_messages = 1, + kafka_num_consumers = 1; +--消费者数量根据情况自定义 + diff --git a/src/AbstractConfig.php b/src/AbstractConfig.php new file mode 100644 index 0000000..fd1b63f --- /dev/null +++ b/src/AbstractConfig.php @@ -0,0 +1,86 @@ +targetList = $target; + if (empty($this->targetList)) { + $this->targetList = [ + 'echo' => new StyleTarget() + ]; + } + foreach ($configs as $name => $value) { + if (property_exists($this, $name) && $name !== 'targetList') { + $this->$name = $value; + } + } + register_shutdown_function(function () { + $this->flush(true); + }); + $this->tick > 0 && Timer::tick($this->tick * 1000, [$this, 'flush'], [true]); + } + + /** + * @param bool $flush + */ + abstract public function flush(bool $flush = false): void; + + /** + * @param callable $userTemplate + */ + public function registerTemplate(callable $userTemplate): void + { + $this->userTemplate = $userTemplate; + } + + /** + * @param string $level + * @param string $message + * @param array $context + */ + abstract public function log(string $level, string $message, array $context = []): void; + + /** + * @return array + */ + protected function getTemplate(): array + { + if ($this->userTemplate) { + $template = call_user_func($this->userTemplate); + $template = $template ?? []; + } else { + $template = []; + } + return $template; + } +} \ No newline at end of file diff --git a/src/ArrayHelper.php b/src/ArrayHelper.php new file mode 100644 index 0000000..8bbadce --- /dev/null +++ b/src/ArrayHelper.php @@ -0,0 +1,71 @@ +$key; + } elseif (is_array($array)) { + return (isset($array[$key]) || array_key_exists($key, $array)) ? $array[$key] : $default; + } + + return $default; + } + + /** + * @param $array + * @param $key + * @param null $default + * @return mixed|null + */ + public static function remove(&$array, $key, $default = null) + { + if (is_array($array) && (isset($array[$key]) || array_key_exists($key, $array))) { + $value = $array[$key]; + unset($array[$key]); + + return $value; + } + + return $default; + } +} diff --git a/src/ConsoleColor.php b/src/ConsoleColor.php new file mode 100644 index 0000000..4493881 --- /dev/null +++ b/src/ConsoleColor.php @@ -0,0 +1,190 @@ + null, + 'bold' => '1', + 'dark' => '2', + 'italic' => '3', + 'underline' => '4', + 'blink' => '5', + 'reverse' => '7', + 'concealed' => '8', + 'default' => '39', + 'black' => '30', + 'red' => '31', + 'green' => '32', + 'yellow' => '33', + 'blue' => '34', + 'magenta' => '35', + 'cyan' => '36', + 'light_gray' => '37', + 'dark_gray' => '90', + 'light_red' => '91', + 'light_green' => '92', + 'light_yellow' => '93', + 'light_blue' => '94', + 'light_magenta' => '95', + 'light_cyan' => '96', + 'white' => '97', + 'bg_default' => '49', + 'bg_black' => '40', + 'bg_red' => '41', + 'bg_green' => '42', + 'bg_yellow' => '43', + 'bg_blue' => '44', + 'bg_magenta' => '45', + 'bg_cyan' => '46', + 'bg_light_gray' => '47', + 'bg_dark_gray' => '100', + 'bg_light_red' => '101', + 'bg_light_green' => '102', + 'bg_light_yellow' => '103', + 'bg_light_blue' => '104', + 'bg_light_magenta' => '105', + 'bg_light_cyan' => '106', + 'bg_white' => '107', + ); + + /** + * ConsoleColor constructor. + * @param bool $forceStyle + */ + public function __construct(bool $forceStyle = true) + { + $this->forceStyle = $forceStyle; + $this->isSupported = $this->isSupported(); + } + + /** + * @return bool + */ + public function isSupported(): bool + { + if (DIRECTORY_SEPARATOR === '\\') { + if (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) { + return true; + } elseif (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON') { + return true; + } + return false; + } else { + return function_exists('posix_isatty') && @posix_isatty(STDOUT); + } + } + + /** + * @param string $style + * @param string $text + * @return string + */ + public function apply(string $style, string $text): string + { + if (empty($style)) { + return $text; + } + if (!$this->isStyleForced() && !$this->isSupported()) { + return $text; + } + if ($this->isValidStyle($style)) { + $sequences = $this->styleSequence($style); + } else { + throw new InvalidArgumentException($style); + } + if (empty($sequences)) { + return $text; + } + return $this->escSequence($sequences) . $text . $this->escSequence((string)self::RESET_STYLE); + } + + /** + * @return bool + */ + public function isStyleForced(): bool + { + return $this->forceStyle; + } + + /** + * @param string $style + * @return string + */ + private function styleSequence(string $style): ?string + { + if (array_key_exists($style, $this->styles)) { + return $this->styles[$style]; + } + if (!$this->are256ColorsSupported()) { + return null; + } + preg_match(self::COLOR256_REGEXP, $style, $matches); + $type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND; + $value = $matches[2]; + return "$type;5;$value"; + } + + /** + * @return bool + */ + public function are256ColorsSupported(): bool + { + if (DIRECTORY_SEPARATOR === '\\') { + return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT); + } else { + return strpos(getenv('TERM'), '256color') !== false; + } + } + + /** + * @param string $style + * @return bool + */ + private function isValidStyle(string $style): bool + { + return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); + } + + /** + * @param string|int $value + * @return string + */ + private function escSequence(string $value): string + { + return "\033[{$value}m"; + } + + /** + * @param bool $forceStyle + */ + public function setForceStyle(bool $forceStyle) + { + $this->forceStyle = (bool)$forceStyle; + } + + /** + * @return array + */ + public function getPossibleStyles(): array + { + return array_keys($this->styles); + } +} \ No newline at end of file diff --git a/src/Context.php b/src/Context.php new file mode 100644 index 0000000..408e12b --- /dev/null +++ b/src/Context.php @@ -0,0 +1,49 @@ + '#F0F8FF', + 'AntiqueWhite' => '#FAEBD7', + 'Aqua' => '#00FFFF', + 'Aquamarine' => '#7FFFD4', + 'Azure ' => '#F0FFFF', + 'Beige' => '#F5F5DC', + 'Bisque' => '#FFE4C4', + 'Black' => '#000000', + 'BlanchedAlmond' => '#FFEBCD', + 'Blue' => '#0000FF', + 'BlueViolet' => '#8A2BE2', + 'Brown' => '#A52A2A', + 'BurlyWood' => '#DEB887', + 'CadetBlue' => '#5F9EA0', + 'Chartreuse' => '#7FFF00', + 'Chocolate' => '#D2691E', + 'Coral' => '#FF7F50', + 'CornflowerBlue' => '#6495ED', + 'Cornsilk' => '#FFF8DC', + 'Crimson' => '#DC143C', + 'Cyan' => '#00FFFF', + 'DarkBlue' => '#00008B', + 'DarkCyan' => '#008B8B', + 'DarkGoldenRod' => '#B8860B', + 'DarkGray' => '#A9A9A9', + 'DarkGreen' => '#006400', + 'DarkKhaki' => '#BDB76B', + 'DarkMagenta' => '#8B008B', + 'DarkOliveGreen' => '#556B2F', + 'Darkorange' => '#FF8C00', + 'DarkOrchid' => '#9932CC', + 'DarkRed' => '#8B0000', + 'DarkSalmon' => '#E9967A', + 'DarkSeaGreen' => '#8FBC8F', + 'DarkSlateBlue' => '#483D8B', + 'DarkSlateGray' => '#2F4F4F', + 'DarkTurquoise' => '#00CED1', + 'DarkViolet' => '#9400D3', + 'DeepPink' => '#FF1493', + 'DeepSkyBlue' => '#00BFFF', + 'DimGray' => '#696969', + 'DodgerBlue' => '#1E90FF', + 'Feldspar' => '#D19275', + 'FireBrick' => '#B22222', + 'FloralWhite' => '#FFFAF0', + 'ForestGreen' => '#228B22', + 'Fuchsia' => '#FF00FF', + 'Gainsboro' => '#DCDCDC', + 'GhostWhite' => '#F8F8FF', + 'Gold' => '#FFD700', + 'GoldenRod' => '#DAA520', + 'Gray' => '#808080', + 'Green' => '#008000', + 'GreenYellow' => '#ADFF2F', + 'HoneyDew' => '#F0FFF0', + 'HotPink' => '#FF69B4', + 'IndianRed' => '#CD5C5C', + 'Indigo' => '#4B0082', + 'Ivory' => '#FFFFF0', + 'Khaki' => '#F0E68C', + 'Lavender' => '#E6E6FA', + 'LavenderBlush' => '#FFF0F5', + 'LawnGreen' => '#7CFC00', + 'LemonChiffon' => '#FFFACD', + 'LightBlue' => '#ADD8E6', + 'LightCoral' => '#F08080', + 'LightCyan' => '#E0FFFF', + 'LightGray' => '#D3D3D3', + 'LightGreen' => '#90EE90', + 'LightPink' => '#FFB6C1', + 'LightSalmon' => '#FFA07A', + 'LightSeaGreen' => '#20B2AA', + 'LightSkyBlue' => '#87CEFA', + 'LightSlateGray' => '#778899', + 'LightSteelBlue' => '#B0C4DE', + 'LightYellow' => '#FFFFE0', + 'Lime' => '#00FF00', + 'LimeGreen' => '#32CD32', + 'Magenta' => '#FF00FF', + 'Maroon' => '#800000', + 'MediumAquaMarine' => '#66CDAA', + 'MediumBlue' => '#0000CD', + 'MediumOrchid' => '#BA55D3', + 'MediumPurple' => '#9370DB', + 'MediumSeaGreen' => '#3CB371', + 'MediumSlateBlue' => '#7B68EE', + 'MediumSpringGreen' => '#00FA9A', + 'MediumTurquoise' => '#48D1CC', + 'MediumVioletRed' => '#C71585', + 'MidnightBlue' => '#191970', + 'MintCream' => '#F5FFFA', + 'MistyRose' => '#FFE4E1', + 'Moccasin' => '#FFE4B5', + 'NavajoWhite' => '#FFDEAD', + 'Navy' => '#000080', + 'OldLace' => '#FDF5E6', + 'Olive' => '#808000', + 'OliveDrab' => '#6B8E23', + 'Orange' => '#FFA500', + 'OrangeRed' => '#FF4500', + 'Orchid' => '#DA70D6', + 'PaleGoldenRod' => '#EEE8AA', + 'PaleGreen' => '#98FB98', + 'PaleTurquoise' => '#AFEEEE', + 'PaleVioletRed' => '#DB7093', + 'PapayaWhip' => '#FFEFD5', + 'PeachPuff' => '#FFDAB9', + 'Peru' => '#CD853F', + 'Pink' => '#FFC0CB', + 'Plum' => '#DDA0DD', + 'PowderBlue' => '#B0E0E6', + 'Purple' => '#800080', + 'Red' => '#FF0000', + 'RosyBrown' => '#BC8F8F', + 'RoyalBlue' => '#4169E1', + 'SaddleBrown' => '#8B4513', + 'Salmon' => '#FA8072', + 'SandyBrown' => '#F4A460', + 'SeaGreen' => '#2E8B57', + 'SeaShell' => '#FFF5EE', + 'Sienna' => '#A0522D', + 'Silver' => '#C0C0C0', + 'SkyBlue' => '#87CEEB', + 'SlateBlue' => '#6A5ACD', + 'SlateGray' => '#708090', + 'Snow' => '#FFFAFA', + 'SpringGreen' => '#00FF7F', + 'SteelBlue' => '#4682B4', + 'Tan' => '#D2B48C', + 'Teal' => '#008080', + 'Thistle' => '#D8BFD8', + 'Tomato' => '#FF6347', + 'Turquoise' => '#40E0D0', + 'Violet' => '#EE82EE', + 'Wheat' => '#F5DEB3', + 'White' => '#FFFFFF', + 'WhiteSmoke' => '#F5F5F5', + 'Yellow' => '#FFFF00', + 'YellowGreen' => '#9ACD32' + ); + + /** + * @param string $color + * @return string + */ + public static function getColor(string $color): string + { + return (string)ArrayHelper::getValue(static::$colors, $color, ''); + } + + /** + * @return array + */ + public static function getPossibleColors(): array + { + return array_keys(self::$colors); + } +} \ No newline at end of file diff --git a/src/Kafka/Broker.php b/src/Kafka/Broker.php new file mode 100644 index 0000000..0c4040d --- /dev/null +++ b/src/Kafka/Broker.php @@ -0,0 +1,115 @@ + $value) { + if (property_exists($this, $name)) { + $this->$name = $value; + } + } + } + + /** + * @return int + */ + public function getGroupBrokerId(): int + { + return $this->groupBrokerId; + } + + /** + * @param int $brokerId + */ + public function setGroupBrokerId(int $brokerId): void + { + $this->groupBrokerId = $brokerId; + } + + /** + * @param array $topics + * @param array $brokersResult + * @return bool + */ + public function setData(array $topics, array $brokersResult): bool + { + $brokers = []; + + foreach ($brokersResult as $value) { + $brokers[$value['nodeId']] = $value['host'] . ':' . $value['port']; + } + + $changed = false; + + if (serialize($this->brokers) !== serialize($brokers)) { + $this->brokers = $brokers; + + $changed = true; + } + + $newTopics = []; + foreach ($topics as $topic) { + if ((int)$topic['errorCode'] !== Protocol::NO_ERROR) { + continue; + } + + $item = []; + + foreach ($topic['partitions'] as $part) { + $item[$part['partitionId']] = $part['leader']; + } + + $newTopics[$topic['topicName']] = $item; + } + + if (serialize($this->topics) !== serialize($newTopics)) { + $this->topics = $newTopics; + + $changed = true; + } + + return $changed; + } + + /** + * @return array + */ + public function getTopics(): array + { + return $this->topics; + } + + /** + * @return string[] + */ + public function getBrokers(): array + { + return $this->brokers; + } + + public function clear(): void + { + $this->brokers = []; + } +} \ No newline at end of file diff --git a/src/Kafka/Config.php b/src/Kafka/Config.php new file mode 100644 index 0000000..35ee954 --- /dev/null +++ b/src/Kafka/Config.php @@ -0,0 +1,221 @@ + 'seaslog-kafka', + 'brokerVersion' => '0.10.1.0', + 'metadataBrokerList' => '', + 'messageMaxBytes' => 1000000, + 'metadataRequestTimeoutMs' => 60000, + 'metadataRefreshIntervalMs' => 300000, + 'metadataMaxAgeMs' => -1 + ]; + /** + * @var mixed[] + */ + protected $options = []; + + /** + * Config constructor. + * @param array $configs + */ + public function __construct(array $configs) + { + foreach ($configs as $name => $value) { + $method = 'set' . ucfirst($name); + $this->$method($value); + } + } + + /** + * @param string $name + * @param mixed[] $args + * + * @return bool|mixed + */ + public function __call(string $name, array $args) + { + $isGetter = strpos($name, 'get') === 0 || strpos($name, 'iet') === 0; + $isSetter = strpos($name, 'set') === 0; + + if (!$isGetter && !$isSetter) { + return false; + } + + $option = lcfirst(substr($name, 3)); + + if ($isGetter) { + if (isset($this->options[$option])) { + return $this->options[$option]; + } + + if (isset(self::$defaults[$option])) { + return self::$defaults[$option]; + } + + if (isset(static::$defaults[$option])) { + return static::$defaults[$option]; + } + + return false; + } + + if (count($args) !== 1) { + return false; + } + + $this->options[$option] = array_shift($args); + + // check todo + return true; + } + + /** + * @param string $val + * @throws Exception + */ + public function setClientId(string $val): void + { + $client = trim($val); + + if ($client === '') { + throw new Exception('Set clientId value is invalid, must is not empty string.'); + } + + $this->options['clientId'] = $client; + } + + /** + * @param string $version + * @throws Exception + */ + public function setBrokerVersion(string $version): void + { + $version = trim($version); + + if ($version === '' || version_compare($version, '0.8.0', '<')) { + throw new Exception('Set broker version value is invalid, must is not empty string and gt 0.8.0.'); + } + + $this->options['brokerVersion'] = $version; + } + + /** + * @param string $brokerList + * @throws Exception + */ + public function setMetadataBrokerList(string $brokerList): void + { + $brokerList = trim($brokerList); + + $brokers = array_filter( + explode(',', $brokerList), + function (string $broker): bool { + return preg_match('/^(.*:[\d]+)$/', $broker) === 1; + } + ); + + if (empty($brokers)) { + throw new Exception( + 'Broker list must be a comma-separated list of brokers (format: "host:port"), with at least one broker' + ); + } + + $this->options['metadataBrokerList'] = $brokers; + } + + public function clear(): void + { + $this->options = []; + } + + /** + * @param int $messageMaxBytes + * @throws Exception + */ + public function setMessageMaxBytes(int $messageMaxBytes): void + { + if ($messageMaxBytes < 1000 || $messageMaxBytes > 1000000000) { + throw new Exception('Set message max bytes value is invalid, must set it 1000 .. 1000000000'); + } + $this->options['messageMaxBytes'] = $messageMaxBytes; + } + + /** + * @param int $metadataRequestTimeoutMs + * @throws Exception + */ + public function setMetadataRequestTimeoutMs(int $metadataRequestTimeoutMs): void + { + if ($metadataRequestTimeoutMs < 10 || $metadataRequestTimeoutMs > 900000) { + throw new Exception('Set metadata request timeout value is invalid, must set it 10 .. 900000'); + } + $this->options['metadataRequestTimeoutMs'] = $metadataRequestTimeoutMs; + } + + /** + * @param int $metadataRefreshIntervalMs + * @throws Exception + */ + public function setMetadataRefreshIntervalMs(int $metadataRefreshIntervalMs): void + { + if ($metadataRefreshIntervalMs < 10 || $metadataRefreshIntervalMs > 3600000) { + throw new Exception('Set metadata refresh interval value is invalid, must set it 10 .. 3600000'); + } + $this->options['metadataRefreshIntervalMs'] = $metadataRefreshIntervalMs; + } + + /** + * @param int $metadataMaxAgeMs + * @throws Exception + */ + public function setMetadataMaxAgeMs(int $metadataMaxAgeMs): void + { + if ($metadataMaxAgeMs < 1 || $metadataMaxAgeMs > 86400000) { + throw new Exception('Set metadata max age value is invalid, must set it 1 .. 86400000'); + } + $this->options['metadataMaxAgeMs'] = $metadataMaxAgeMs; + } +} diff --git a/src/Kafka/InvalidRecordInSet.php b/src/Kafka/InvalidRecordInSet.php new file mode 100644 index 0000000..ccd08f4 --- /dev/null +++ b/src/Kafka/InvalidRecordInSet.php @@ -0,0 +1,52 @@ +requestHeader('seaslog-kafka', self::METADATA_REQUEST, self::METADATA_REQUEST); + $data = self::encodeArray($payloads, [$this, 'encodeString'], self::PACK_INT16); + $data = self::encodeString($header . $data, self::PACK_INT32); + + return $data; + } + + /** + * @param string $data + * @return array + * @throws Exception + */ + public function decode(string $data): array + { + $offset = 0; + $brokerRet = $this->decodeArray(substr($data, $offset), [$this, 'metaBroker']); + $offset += $brokerRet['length']; + $topicMetaRet = $this->decodeArray(substr($data, $offset), [$this, 'metaTopicMetaData']); + $offset += $topicMetaRet['length']; + + $result = [ + 'brokers' => $brokerRet['data'], + 'topics' => $topicMetaRet['data'], + ]; + + return $result; + } + + /** + * @param string $data + * @return array + * @throws Exception + */ + protected function metaBroker(string $data): array + { + $offset = 0; + $nodeId = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + $hostNameInfo = $this->decodeString(substr($data, $offset), self::BIT_B16); + $offset += $hostNameInfo['length']; + $port = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + + return [ + 'length' => $offset, + 'data' => [ + 'host' => $hostNameInfo['data'], + 'port' => $port, + 'nodeId' => $nodeId, + ], + ]; + } + + /** + * @param string $data + * @return array + * @throws Exception + */ + protected function metaTopicMetaData(string $data): array + { + $offset = 0; + $topicErrCode = self::unpack(self::BIT_B16_SIGNED, substr($data, $offset, 2)); + $offset += 2; + $topicInfo = $this->decodeString(substr($data, $offset), self::BIT_B16); + $offset += $topicInfo['length']; + $partitionsMeta = $this->decodeArray(substr($data, $offset), [$this, 'metaPartitionMetaData']); + $offset += $partitionsMeta['length']; + + return [ + 'length' => $offset, + 'data' => [ + 'topicName' => $topicInfo['data'], + 'errorCode' => $topicErrCode, + 'partitions' => $partitionsMeta['data'], + ], + ]; + } + + /** + * @param string $data + * @return array + * @throws Exception + */ + protected function metaPartitionMetaData(string $data): array + { + $offset = 0; + $errcode = self::unpack(self::BIT_B16_SIGNED, substr($data, $offset, 2)); + $offset += 2; + $partId = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + $leader = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + $replicas = $this->decodePrimitiveArray(substr($data, $offset), self::BIT_B32); + $offset += $replicas['length']; + $isr = $this->decodePrimitiveArray(substr($data, $offset), self::BIT_B32); + $offset += $isr['length']; + + return [ + 'length' => $offset, + 'data' => [ + 'partitionId' => $partId, + 'errorCode' => $errcode, + 'replicas' => $replicas['data'], + 'leader' => $leader, + 'isr' => $isr['data'], + ], + ]; + } +} diff --git a/src/Kafka/Produce.php b/src/Kafka/Produce.php new file mode 100644 index 0000000..21194a9 --- /dev/null +++ b/src/Kafka/Produce.php @@ -0,0 +1,300 @@ +clock = $clock ?: new SystemClock(); + } + + /** + * @param mixed[] $payloads + * + * @throws NotSupported + * @throws ProtocolException + */ + public function encode(array $payloads = []): string + { + if (!isset($payloads['data'])) { + throw new InvalidArgumentException('given procude data invalid. `data` is undefined.'); + } + + $header = $this->requestHeader('seaslog-kafka', 0, self::PRODUCE_REQUEST); + $data = self::pack(self::BIT_B16, (string)($payloads['required_ack'] ?? 0)); + $data .= self::pack(self::BIT_B32, (string)($payloads['timeout'] ?? 100)); + $data .= self::encodeArray( + $payloads['data'], + [$this, 'encodeProduceTopic'], + $payloads['compression'] ?? self::COMPRESSION_NONE + ); + + return self::encodeString($header . $data, self::PACK_INT32); + } + + /** + * @return mixed[] + * + * @throws ProtocolException + */ + public function decode(string $data): array + { + $offset = 0; + $version = $this->getApiVersion(self::PRODUCE_REQUEST); + $ret = $this->decodeArray(substr($data, $offset), [$this, 'produceTopicPair'], $version); + $offset += $ret['length']; + $throttleTime = 0; + + if ($version === self::API_VERSION2) { + $throttleTime = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + } + + return ['throttleTime' => $throttleTime, 'data' => $ret['data']]; + } + + /** + * encode signal part + * + * @param mixed[] $values + * + * @throws NotSupported + * @throws ProtocolException + */ + protected function encodeProducePartition(array $values, int $compression): string + { + if (!isset($values['partition_id'])) { + throw new ProtocolException('given produce data invalid. `partition_id` is undefined.'); + } + + if (!isset($values['messages']) || empty($values['messages'])) { + throw new ProtocolException('given produce data invalid. `messages` is undefined.'); + } + + $data = self::pack(self::BIT_B32, (string)$values['partition_id']); + $data .= self::encodeString( + $this->encodeMessageSet((array)$values['messages'], $compression), + self::PACK_INT32 + ); + + return $data; + } + + /** + * encode message set + * N.B., MessageSets are not preceded by an int32 like other array elements + * in the protocol. + * + * @param string[]|string[][] $messages + * + * @throws NotSupported + */ + protected function encodeMessageSet(array $messages, int $compression = self::COMPRESSION_NONE): string + { + $data = ''; + $next = 0; + + foreach ($messages as $message) { + $encodedMessage = $this->encodeMessage($message); + + $data .= self::pack(self::BIT_B64, (string)$next) + . self::encodeString($encodedMessage, self::PACK_INT32); + + ++$next; + } + + if ($compression === self::COMPRESSION_NONE) { + return $data; + } + + return self::pack(self::BIT_B64, '0') + . self::encodeString($this->encodeMessage($data, $compression), self::PACK_INT32); + } + + /** + * @param string[]|string $message + * + * @throws NotSupported + */ + protected function encodeMessage($message, int $compression = self::COMPRESSION_NONE): string + { + $magic = $this->computeMagicByte(); + $attributes = $this->computeAttributes($magic, $compression, $this->computeTimestampType($magic)); + + $data = self::pack(self::BIT_B8, (string)$magic); + $data .= self::pack(self::BIT_B8, (string)$attributes); + + if ($magic >= self::MESSAGE_MAGIC_VERSION1) { + $data .= self::pack(self::BIT_B64, $this->clock->now()->format('Uv')); + } + + $key = ''; + + if (is_array($message)) { + $key = $message['key']; + $message = $message['value']; + } + + // message key + $data .= self::encodeString($key, self::PACK_INT32); + + // message value + $data .= self::encodeString($message, self::PACK_INT32, $compression); + + $crc = (string)crc32($data); + + // int32 -- crc code string data + $message = self::pack(self::BIT_B32, $crc) . $data; + + return $message; + } + + private function computeMagicByte(): int + { + if ($this->getApiVersion(self::PRODUCE_REQUEST) === self::API_VERSION2) { + return self::MESSAGE_MAGIC_VERSION1; + } + + return self::MESSAGE_MAGIC_VERSION0; + } + + private function computeAttributes(int $magic, int $compression, int $timestampType): int + { + $attributes = 0; + + if ($compression !== self::COMPRESSION_NONE) { + $attributes |= self::COMPRESSION_CODEC_MASK & $compression; + } + + if ($magic === self::MESSAGE_MAGIC_VERSION0) { + return $attributes; + } + + if ($timestampType === self::TIMESTAMP_LOG_APPEND_TIME) { + $attributes |= self::TIMESTAMP_TYPE_MASK; + } + + return $attributes; + } + + public function computeTimestampType(int $magic): int + { + if ($magic === self::MESSAGE_MAGIC_VERSION0) { + return self::TIMESTAMP_NONE; + } + + return self::TIMESTAMP_CREATE_TIME; + } + + /** + * encode signal topic + * + * @param mixed[] $values + * + * @throws NotSupported + * @throws ProtocolException + */ + protected function encodeProduceTopic(array $values, int $compression): string + { + if (!isset($values['topic_name'])) { + throw new ProtocolException('given produce data invalid. `topic_name` is undefined.'); + } + + if (!isset($values['partitions']) || empty($values['partitions'])) { + throw new ProtocolException('given produce data invalid. `partitions` is undefined.'); + } + + $topic = self::encodeString($values['topic_name'], self::PACK_INT16); + $partitions = self::encodeArray($values['partitions'], [$this, 'encodeProducePartition'], $compression); + + return $topic . $partitions; + } + + /** + * decode produce topic pair response + * + * @return mixed[] + * + * @throws ProtocolException + */ + protected function produceTopicPair(string $data, int $version): array + { + $offset = 0; + $topicInfo = $this->decodeString($data, self::BIT_B16); + $offset += $topicInfo['length']; + $ret = $this->decodeArray(substr($data, $offset), [$this, 'producePartitionPair'], $version); + $offset += $ret['length']; + + return [ + 'length' => $offset, + 'data' => [ + 'topicName' => $topicInfo['data'], + 'partitions' => $ret['data'], + ], + ]; + } + + /** + * decode produce partition pair response + * + * @return mixed[] + * + * @throws ProtocolException + */ + protected function producePartitionPair(string $data, int $version): array + { + $offset = 0; + $partitionId = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + $errorCode = self::unpack(self::BIT_B16_SIGNED, substr($data, $offset, 2)); + $offset += 2; + $partitionOffset = self::unpack(self::BIT_B64, substr($data, $offset, 8)); + $offset += 8; + $timestamp = 0; + + if ($version === self::API_VERSION2) { + $timestamp = self::unpack(self::BIT_B64, substr($data, $offset, 8)); + $offset += 8; + } + + return [ + 'length' => $offset, + 'data' => [ + 'partition' => $partitionId, + 'errorCode' => $errorCode, + 'offset' => $offset, + 'timestamp' => $timestamp, + ], + ]; + } +} diff --git a/src/Kafka/Producter.php b/src/Kafka/Producter.php new file mode 100644 index 0000000..820a11e --- /dev/null +++ b/src/Kafka/Producter.php @@ -0,0 +1,166 @@ +config = $config; + $this->broker = $broker; + $this->pool = $pool; + $this->recordValidator = new RecordValidator(); + ProtocolTool::init($config->getBrokerVersion()); + } + + /** + * @param array $recordSet + * @param callable|null $callback + * @throws Exception + */ + public function send(array $recordSet, ?callable $callback = null): void + { + static $isInit = false; + if (!$isInit) { + $isInit = true; + $this->syncMeta(); + } + $requiredAck = $this->config->getRequiredAck(); + $timeout = $this->config->getTimeout(); + $compression = $this->config->getCompression(); + if (empty($recordSet)) { + return; + } + + $recordSet = array_merge($recordSet, array_splice($this->msgBuffer, 0)); + $sendData = $this->convertRecordSet($recordSet); + foreach ($sendData as $brokerId => $topicList) { + $connect = $this->pool->getConnection(); + $params = [ + 'required_ack' => $requiredAck, + 'timeout' => $timeout, + 'data' => $topicList, + 'compression' => $compression, + ]; + + $requestData = ProtocolTool::encode(ProtocolTool::PRODUCE_REQUEST, $params); + rgo(function () use ($connect, $requestData, $requiredAck, $callback) { + if ($requiredAck !== 0) { + $connect->send($requestData); + $dataLen = Protocol::unpack(Protocol::BIT_B32, $connect->recv(4)); + $recordSet = $connect->recv($dataLen); + $this->pool->release($connect); + $correlationId = Protocol::unpack(Protocol::BIT_B32, substr($recordSet, 0, 4)); + $callback && $callback(ProtocolTool::decode(ProtocolTool::PRODUCE_REQUEST, + substr($recordSet, 4))); + } else { + $connect->send($requestData); + $this->pool->release($connect); + } + }); + } + } + + /** + * @throws Exception + */ + public function syncMeta(): void + { + $socket = $this->pool->getConnection(); + rgo(function () use ($socket) { + while (true) { + try { + $params = []; + $requestData = ProtocolTool::encode(ProtocolTool::METADATA_REQUEST, $params); + $socket->send($requestData); + $dataLen = Protocol::unpack(Protocol::BIT_B32, $socket->recv(4)); + $data = $socket->recv($dataLen); + $correlationId = Protocol::unpack(Protocol::BIT_B32, substr($data, 0, 4)); + $result = ProtocolTool::decode(ProtocolTool::METADATA_REQUEST, substr($data, 4)); + if (!isset($result['brokers'], $result['topics'])) { + throw new Exception('Get metadata is fail, brokers or topics is null.'); + } + $this->broker->setData($result['topics'], $result['brokers']); + } finally { + Co::sleep(30); + } + } + }); + } + + /** + * @param string[][] $recordSet + * + * @return mixed[] + * @throws InvalidRecordInSet + */ + protected function convertRecordSet(array $recordSet): array + { + $sendData = []; + while (empty($topics = $this->broker->getTopics())) { + Co::sleep(0.5); + } + + foreach ($recordSet as $record) { + + $this->recordValidator->validate($record, $topics); + + $topicMeta = $topics[$record['topic']]; + $partNums = array_keys($topicMeta); + shuffle($partNums); + + $partId = isset($record['partId'], $topicMeta[$record['partId']]) ? $record['partId'] : $partNums[0]; + + $brokerId = $topicMeta[$partId]; + $topicData = []; + if (isset($sendData[$brokerId][$record['topic']])) { + $topicData = $sendData[$brokerId][$record['topic']]; + } + + $partition = []; + if (isset($topicData['partitions'][$partId])) { + $partition = $topicData['partitions'][$partId]; + } + + $partition['partition_id'] = $partId; + + if (trim($record['key'] ?? '') !== '') { + $partition['messages'][] = ['value' => $record['value'], 'key' => $record['key']]; + } else { + $partition['messages'][] = $record['value']; + } + + $topicData['partitions'][$partId] = $partition; + $topicData['topic_name'] = $record['topic']; + $sendData[$brokerId][$record['topic']] = $topicData; + } + + return $sendData; + } +} diff --git a/src/Kafka/ProducterConfig.php b/src/Kafka/ProducterConfig.php new file mode 100644 index 0000000..f178b0d --- /dev/null +++ b/src/Kafka/ProducterConfig.php @@ -0,0 +1,87 @@ + 1, + 'timeout' => 5000, + 'requestTimeout' => 6000, + 'compression' => Protocol::COMPRESSION_NONE, + ]; + + /** + * @param int $requestTimeout + * @throws Exception + */ + public function setRequestTimeout(int $requestTimeout): void + { + if ($requestTimeout < 1 || $requestTimeout > 900000) { + throw new NotSupportedException('Set request timeout value is invalid, must set it 1 .. 900000'); + } + + $this->options['requestTimeout'] = $requestTimeout; + } + + /** + * @param int $timeout + * @throws Exception + */ + public function setTimeout(int $timeout): void + { + if ($timeout < 1 || $timeout > 900000) { + throw new NotSupportedException('Set timeout value is invalid, must set it 1 .. 900000'); + } + + $this->options['timeout'] = $timeout; + } + + /** + * @param int $requiredAck + * @throws Exception + */ + public function setRequiredAck(int $requiredAck): void + { + if ($requiredAck < -1 || $requiredAck > 1000) { + throw new NotSupportedException('Set required ack value is invalid, must set it -1 .. 1000'); + } + + $this->options['requiredAck'] = $requiredAck; + } + + /** + * @param int $compression + * @throws Exception + */ + public function setCompression(int $compression): void + { + if (!in_array($compression, self::COMPRESSION_OPTIONS, true)) { + throw new NotSupportedException('Compression must be one the Produce::COMPRESSION_* constants'); + } + + $this->options['compression'] = $compression; + } +} diff --git a/src/Kafka/Protocol.php b/src/Kafka/Protocol.php new file mode 100644 index 0000000..e76c92a --- /dev/null +++ b/src/Kafka/Protocol.php @@ -0,0 +1,491 @@ +version = $version; + } + + /** + * @param array $array + * @param callable $func + * @param int|null $options + * @return string + */ + public static function encodeArray(array $array, callable $func, ?int $options = null): string + { + $arrayCount = count($array); + + $body = ''; + foreach ($array as $value) { + $body .= $options !== null ? $func($value, $options) : $func($value); + } + + return self::pack(self::BIT_B32, (string)$arrayCount) . $body; + } + + public static function pack(string $type, string $data): string + { + if ($type !== self::BIT_B64) { + return pack($type, $data); + } + + if ((int)$data === -1) { // -1L + return hex2bin('ffffffffffffffff'); + } + + if ((int)$data === -2) { // -2L + return hex2bin('fffffffffffffffe'); + } + + $left = 0xffffffff00000000; + $right = 0x00000000ffffffff; + + $l = ($data & $left) >> 32; + $r = $data & $right; + + return pack($type, $l, $r); + } + + /** + * Get kafka api text + * @param int $apikey + * @return string + */ + public static function getApiText(int $apikey): string + { + $apis = [ + self::PRODUCE_REQUEST => 'ProduceRequest', + self::METADATA_REQUEST => 'MetadataRequest' + ]; + + return $apis[$apikey] ?? 'Unknown message'; + } + + /** + * @param string $clientId + * @param int $correlationId + * @param int $apiKey + * @return string + */ + public function requestHeader(string $clientId, int $correlationId, int $apiKey): string + { + // int16 -- apiKey int16 -- apiVersion int32 correlationId + $binData = self::pack(self::BIT_B16, (string)$apiKey); + $binData .= self::pack(self::BIT_B16, (string)$this->getApiVersion($apiKey)); + $binData .= self::pack(self::BIT_B32, (string)$correlationId); + + // concat client id + $binData .= self::encodeString($clientId, self::PACK_INT16); + + return $binData; + } + + /** + * Get kafka api version according to specify kafka broker version + * @param int $apikey + * @return int + */ + public function getApiVersion(int $apikey): int + { + switch ($apikey) { + case self::METADATA_REQUEST: + return self::API_VERSION0; + case self::PRODUCE_REQUEST: + if (version_compare($this->version, '0.10.0') >= 0) { + return self::API_VERSION2; + } + + if (version_compare($this->version, '0.9.0') >= 0) { + return self::API_VERSION1; + } + + return self::API_VERSION0; + } + + // default + return self::API_VERSION0; + } + + /** + * @param string $string + * @param int $bytes + * @param int $compression + * @return string + */ + public static function encodeString(string $string, int $bytes, int $compression = self::COMPRESSION_NONE): string + { + $packLen = $bytes === self::PACK_INT32 ? self::BIT_B32 : self::BIT_B16; + $string = self::compress($string, $compression); + + return self::pack($packLen, (string)strlen($string)) . $string; + } + + /** + * @param string $string + * @param int $compression + * @return string + */ + private static function compress(string $string, int $compression): string + { + if ($compression === self::COMPRESSION_NONE) { + return $string; + } + + if ($compression === self::COMPRESSION_SNAPPY) { + throw new BadMethodCallException('SNAPPY compression not yet implemented'); + } + + if ($compression !== self::COMPRESSION_GZIP) { + throw new BadMethodCallException('Unknown compression flag: ' . $compression); + } + + return gzencode($string); + } + + /** + * @param string $data + * @param string $bytes + * @param int $compression + * @return mixed[] + * + * @throws Exception + */ + public function decodeString(string $data, string $bytes, int $compression = self::COMPRESSION_NONE): array + { + $offset = $bytes === self::BIT_B32 ? 4 : 2; + $packLen = self::unpack($bytes, substr($data, 0, $offset)); // int16 topic name length + + if ($packLen === 4294967295) { // uint32(4294967295) is int32 (-1) + $packLen = 0; + } + + if ($packLen === 0) { + return ['length' => $offset, 'data' => '']; + } + + $data = (string)substr($data, $offset, $packLen); + $offset += $packLen; + + return ['length' => $offset, 'data' => self::decompress($data, $compression)]; + } + + /** + * Unpack a bit integer as big endian long + * + * @param string $type + * @param string $bytes + * @return mixed + * @throws Exception + */ + public static function unpack(string $type, string $bytes) + { + self::checkLen($type, $bytes); + + if ($type === self::BIT_B64) { + $set = unpack($type, $bytes); + $result = ($set[1] & 0xFFFFFFFF) << 32 | ($set[2] & 0xFFFFFFFF); + } elseif ($type === self::BIT_B16_SIGNED) { + // According to PHP docs: 's' = signed short (always 16 bit, machine byte order) + // So lets unpack it.. + $set = unpack($type, $bytes); + + // But if our system is little endian + if (self::isSystemLittleEndian()) { + // We need to flip the endianess because coming from kafka it is big endian + $set = self::convertSignedShortFromLittleEndianToBigEndian($set); + } + $result = $set; + } else { + $result = unpack($type, $bytes); + } + + return is_array($result) ? array_shift($result) : $result; + } + + /** + * check unpack bit is valid + * + * @param string $type + * @param string $bytes + * @throws Exception + */ + protected static function checkLen(string $type, string $bytes): void + { + $expectedLength = 0; + + switch ($type) { + case self::BIT_B64: + $expectedLength = 8; + break; + case self::BIT_B32: + $expectedLength = 4; + break; + case self::BIT_B16_SIGNED: + case self::BIT_B16: + $expectedLength = 2; + break; + case self::BIT_B8: + $expectedLength = 1; + break; + } + + $length = strlen($bytes); + + if ($length !== $expectedLength) { + throw new Exception('unpack failed. string(raw) length is ' . $length . ' , TO ' . $type); + } + } + + /** + * Determines if the computer currently running this code is big endian or little endian. + */ + public static function isSystemLittleEndian(): bool + { + // If we don't know if our system is big endian or not yet... + if (self::$isLittleEndianSystem === null) { + [$endianTest] = array_values(unpack('L1L', pack('V', 1))); + + self::$isLittleEndianSystem = (int)$endianTest === 1; + } + + return self::$isLittleEndianSystem; + } + + /** + * Converts a signed short (16 bits) from little endian to big endian. + * + * @param int[] $bits + * + * @return int[] + */ + public static function convertSignedShortFromLittleEndianToBigEndian(array $bits): array + { + $convert = function (int $bit): int { + $lsb = $bit & 0xff; + $msb = $bit >> 8 & 0xff; + $bit = $lsb << 8 | $msb; + + if ($bit >= 32768) { + $bit -= 65536; + } + + return $bit; + }; + + return array_map($convert, $bits); + } + + private static function decompress(string $string, int $compression): string + { + if ($compression === self::COMPRESSION_NONE) { + return $string; + } + + if ($compression === self::COMPRESSION_SNAPPY) { + throw new BadMethodCallException('SNAPPY compression not yet implemented'); + } + + if ($compression !== self::COMPRESSION_GZIP) { + throw new BadMethodCallException('Unknown compression flag: ' . $compression); + } + + return gzdecode($string); + } + + /** + * @param string $data + * @param callable $func + * @param mixed|null $options + * + * @return mixed[] + * + * @throws Exception + */ + public function decodeArray(string $data, callable $func, $options = null): array + { + $offset = 0; + $arrayCount = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + + $result = []; + + for ($i = 0; $i < $arrayCount; $i++) { + $value = substr($data, $offset); + $ret = $options !== null ? $func($value, $options) : $func($value); + + if (!is_array($ret) && $ret === false) { + break; + } + + if (!isset($ret['length'], $ret['data'])) { + throw new Exception('Decode array failed, given function return format is invalid'); + } + if ((int)$ret['length'] === 0) { + continue; + } + + $offset += $ret['length']; + $result[] = $ret['data']; + } + + return ['length' => $offset, 'data' => $result]; + } + + /** + * @param string $data + * @param string $bit + * @return mixed[] + * + */ + public function decodePrimitiveArray(string $data, string $bit): array + { + $offset = 0; + $arrayCount = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + + if ($arrayCount === 4294967295) { + $arrayCount = 0; + } + + $result = []; + + for ($i = 0; $i < $arrayCount; $i++) { + if ($bit === self::BIT_B64) { + $result[] = self::unpack(self::BIT_B64, substr($data, $offset, 8)); + $offset += 8; + } elseif ($bit === self::BIT_B32) { + $result[] = self::unpack(self::BIT_B32, substr($data, $offset, 4)); + $offset += 4; + } elseif (in_array($bit, [self::BIT_B16, self::BIT_B16_SIGNED], true)) { + $result[] = self::unpack($bit, substr($data, $offset, 2)); + $offset += 2; + } elseif ($bit === self::BIT_B8) { + $result[] = self::unpack($bit, substr($data, $offset, 1)); + ++$offset; + } + } + + return ['length' => $offset, 'data' => $result]; + } + + /** + * @param array $payloads + * @return string + */ + abstract public function encode(array $payloads = []): string; + + /** + * @param string $data + * @return array + */ + abstract public function decode(string $data): array; +} diff --git a/src/Kafka/ProtocolTool.php b/src/Kafka/ProtocolTool.php new file mode 100644 index 0000000..0da4642 --- /dev/null +++ b/src/Kafka/ProtocolTool.php @@ -0,0 +1,66 @@ + Produce::class, + Protocol::METADATA_REQUEST => Metadata::class, + ]; + + foreach ($class as $key => $className) { + self::$objects[$key] = new $className($version); + } + } + + /** + * @param int $key + * @param array $payloads + * @return string + * @throws Exception + */ + public static function encode(int $key, array $payloads): string + { + if (!isset(self::$objects[$key])) { + throw new NotSupportedException('Not support api key, key:' . $key); + } + + return self::$objects[$key]->encode($payloads); + } + + /** + * @param int $key + * @param string $data + * @return array + * @throws Exception + */ + public static function decode(int $key, string $data): array + { + if (!isset(self::$objects[$key])) { + throw new NotSupportedException('Not support api key, key:' . $key); + } + + return self::$objects[$key]->decode($data); + } +} diff --git a/src/Kafka/RecordValidator.php b/src/Kafka/RecordValidator.php new file mode 100644 index 0000000..58f4f2c --- /dev/null +++ b/src/Kafka/RecordValidator.php @@ -0,0 +1,49 @@ + $value) { + if (property_exists($this, $name)) { + $this->$name = $value; + } + } + $this->queue = new SplQueue(); + $this->waitStack = new SplQueue(); + } + + /** + * @param SocketIO $connection + */ + public function release(SocketIO $connection) + { + if ($this->queue->count() < $this->active) { + $this->queue->push($connection); + if ($this->waitStack->count() > 0) { + $id = $this->waitStack->shift(); + Co::resume($id); + } + } + } + + /** + * @return SocketIO + * @throws Exception + */ + public function getConnection(): SocketIO + { + if (!$this->queue->isEmpty()) { + return $this->queue->shift(); + } + + if ($this->currentCount >= $this->active) { + if ($this->maxWait > 0 && $this->waitStack->count() > $this->maxWait) { + throw new Exception('Connection pool queue is full'); + } + $this->waitStack->push(Co::getCid()); + if (Co::suspend() == false) { + $this->waitStack->pop(); + throw new Exception('Reach max connections! Can not pending fetch!'); + } + return $this->queue->shift(); + } + + $connection = $this->createConnection(); + $this->currentCount++; + if ($connection->check() === false) { + $connection->reconnect(); + } + return $connection; + } + + /** + * @return SocketIO + * @throws Exception + */ + public function createConnection(): SocketIO + { + $socket = new SocketIO(); + $socket->createConnection([ + 'uri' => $this->uri, + 'retry' => $this->retry, + 'sleep' => $this->waitReconnect, + 'timeout' => $this->timeout + ]); + return $socket; + } +} \ No newline at end of file diff --git a/src/Kafka/Socket/SocketIO.php b/src/Kafka/Socket/SocketIO.php new file mode 100644 index 0000000..9a499b0 --- /dev/null +++ b/src/Kafka/Socket/SocketIO.php @@ -0,0 +1,106 @@ + 0) { + $result = $this->connection->sendAll($data, $timeout); + if ($result === false) { + $this->reconnect(); + } + $data = substr($data, $result); + } + $this->recv = false; + return $ln; + } + + /** + * @throws Exception + */ + public function reconnect(): void + { + $this->createConnection(); + } + + /** + * @param array $config + * @throws Exception + */ + public function createConnection(array $config = []): void + { + !empty($config) && ($this->config = $config); + $client = new Socket(AF_INET, SOCK_STREAM, 0); + list($host, $port) = explode(':', $this->config['uri']); + $maxRetry = $this->config['retry']; + $reconnectCount = 0; + while (true) { + $isConnect = $this->config['timeout'] ? $client->connect($host, (int)$port, + $this->config['timeout']) : $client->connect($host, (int)$port); + if (!$isConnect) { + $reconnectCount++; + if ($maxRetry > 0 && $reconnectCount >= $maxRetry) { + $error = sprintf('Service connect fail error=%s host=%s port=%s', socket_strerror($client->errCode), + $host, $port); + throw new Exception($error); + } + Co::sleep($this->config['sleep']); + } else { + break; + } + } + $this->connection = $client; + } + + /** + * @param int $length + * @param float $timeout + * @return string + */ + public function recv(int $length = 65535, float $timeout = -1): string + { + $data = $this->connection->recvAll($length, $timeout); + return $data; + } + + /** + * @return bool + */ + public function check(): bool + { + return $this->connection->errCode === 0; + } + + /** + * @return bool + */ + public function close(): bool + { + return $this->connection->close(); + } +} \ No newline at end of file diff --git a/src/Logger.php b/src/Logger.php index 17452c1..61dc878 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -1,5 +1,5 @@ 'ALERT', self::EMERGENCY => 'EMERGENCY', ]; + /** @var AbstractConfig */ + private static $config; /** - * set request level for seaslog. - * - * @param int $level + * Logger constructor. + * @param AbstractConfig|null $config */ - public function setRequestLevel($level = self::ALL) + public function __construct(?AbstractConfig $config = null) { - self::$RequestLevel = $level; - } - - /** - * @param string $message - * @param array $context - */ - public function emergency($message, array $context = []) - { - SeasLog::emergency($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function alert($message, array $context = []) - { - SeasLog::alert($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function critical($message, array $context = []) - { - SeasLog::critical($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function error($message, array $context = []) - { - SeasLog::error($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function warning($message, array $context = []) - { - SeasLog::warning($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function notice($message, array $context = []) - { - SeasLog::notice($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function info($message, array $context = []) - { - SeasLog::info($message, $context); - } - - /** - * @param string $message - * @param array $context - */ - public function debug($message, array $context = []) - { - SeasLog::debug($message, $context); - } - - /** - * @param mixed $level - * @param string $message - * @param array $context - */ - public function log($level, $message, array $context = []) - { - if ((int)$level < self::$RequestLevel) { - return; - } - - if (!array_key_exists($level, self::$levels)) { - return; + if ($config !== null && !extension_loaded('swoole')) { + throw new NotSupportedException("This usage must have swoole version>=4"); } - - $levelFunction = strtolower(self::$levels[$level]); - - SeasLog::$levelFunction($message, $context); - } - - /** - * @param string $basePath - * - * @return bool - */ - public function setBasePath(string $basePath) - { - return SeasLog::setBasePath($basePath); - } - - /** - * @return string - */ - public function getBasePath() - { - return SeasLog::getBasePath(); + static::$config = $config; } /** @@ -224,6 +114,9 @@ public function getBasePath() */ public static function setRequestID($request_id) { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support setRequestID"); + } return SeasLog::setRequestID($request_id); } @@ -234,6 +127,9 @@ public static function setRequestID($request_id) */ public static function getRequestID() { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support getRequestID"); + } return SeasLog::getRequestID(); } @@ -246,6 +142,9 @@ public static function getRequestID() */ public static function setLogger($module) { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support setLogger"); + } return SeasLog::setLogger($module); } @@ -256,6 +155,9 @@ public static function setLogger($module) */ public static function getLastLogger() { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support getLastLogger"); + } return SeasLog::getLastLogger(); } @@ -268,6 +170,9 @@ public static function getLastLogger() */ public static function setDatetimeFormat($format) { + if (static::$config instanceof LoggerConfig) { + LoggerConfig::setDatetimeFormat($format); + } return SeasLog::setDatetimeFormat($format); } @@ -278,6 +183,9 @@ public static function setDatetimeFormat($format) */ public static function getDatetimeFormat() { + if (static::$config instanceof LoggerConfig) { + return LoggerConfig::getDatetimeFormat(); + } return SeasLog::getDatetimeFormat(); } @@ -292,6 +200,9 @@ public static function getDatetimeFormat() */ public static function analyzerCount($level = 'all', $log_path = '*', $key_word = null) { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support analyzerCount"); + } return SeasLog::analyzerCount($level, $log_path, $key_word); } @@ -315,6 +226,9 @@ public static function analyzerDetail( $limit = 20, $order = SEASLOG_DETAIL_ORDER_ASC ) { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support analyzerDetail"); + } return SeasLog::analyzerDetail( $level, $log_path, @@ -332,6 +246,9 @@ public static function analyzerDetail( */ public static function getBuffer() { + if (static::$config instanceof LoggerConfig) { + return LoggerConfig::getBuffer(); + } return SeasLog::getBuffer(); } @@ -342,9 +259,211 @@ public static function getBuffer() */ public static function flushBuffer() { + if (static::$config) { + throw new NotSupportedException("This ENV not support flushBuffer"); + } return SeasLog::flushBuffer(); } + /** + * Manually release stream flow from logger + * + * @param $type + * @param string $name + * @return bool + */ + public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL, $name = '') + { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException("LoggerConfig not support closeLoggerStream"); + } + if (empty($name)) { + return SeasLog::closeLoggerStream($type); + } + + return SeasLog::closeLoggerStream($type, $name); + + } + + /** + * @return AbstractConfig + */ + public function getConfig(): ?AbstractConfig + { + return static::$config; + } + + /** + * set request level for seaslog. + * + * @param int $level + */ + public function setRequestLevel($level = self::ALL) + { + self::$RequestLevel = $level; + } + + /** + * System is unusable. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function emergency($message, array $context = array()) + { + empty(static::$config) ? SeasLog::emergency($message, $context) : $this->log(self::EMERGENCY, $message, + $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + if ((int)$level < self::$RequestLevel) { + return; + } + + if (!array_key_exists($level, self::$levels)) { + return; + } + + if (empty(static::$config)) { + $levelFunction = strtolower(self::$levels[$level]); + SeasLog::$levelFunction($message, $context); + } else { + if (is_string($message)) { + static::$config->log(self::$levels[$level], $message, $context); + } elseif (is_array($message)) { + foreach ($message as $m) { + static::$config->log(self::$levels[$level], $m, $context); + } + } + } + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + empty(static::$config) ? SeasLog::alert($message, $context) : $this->log(self::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + empty(static::$config) ? SeasLog::critical($message, $context) : $this->log(self::CRITICAL, $message, + $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + empty(static::$config) ? SeasLog::error($message, $context) : $this->log(self::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + empty(static::$config) ? SeasLog::warning($message, $context) : $this->log(self::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + empty(static::$config) ? SeasLog::notice($message, $context) : $this->log(self::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + empty(static::$config) ? SeasLog::info($message, $context) : $this->log(self::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + empty(static::$config) ? SeasLog::debug($message, $context) : $this->log(self::DEBUG, $message, $context); + } + + /** + * @return string + */ + public function getBasePath() + { + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); + } + return SeasLog::getBasePath(); + } + /** * Create a custom SeasLog instance. * @@ -363,19 +482,15 @@ public function __invoke(array $config) } /** - * Manually release stream flow from logger + * @param string $basePath * - * @param $type - * @param string $name * @return bool */ - public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL, $name = '') + public function setBasePath(string $basePath) { - if (empty($name)) { - return SeasLog::closeLoggerStream($type); + if (static::$config instanceof LoggerConfig) { + throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); } - - return SeasLog::closeLoggerStream($type, $name); - + return SeasLog::setBasePath($basePath); } -} +} \ No newline at end of file diff --git a/src/LoggerConfig.php b/src/LoggerConfig.php new file mode 100644 index 0000000..b514912 --- /dev/null +++ b/src/LoggerConfig.php @@ -0,0 +1,203 @@ +template = $template; + } + + /** + * @return string + */ + public static function getDatetimeFormat(): string + { + return static::$datetime_format; + } + + /** + * @param string $format + * @return bool + */ + public static function setDatetimeFormat(string $format): bool + { + if (date($format, time()) !== false) { + static::$datetime_format = $format; + return true; + } + return false; + } + + /** + * 获得当前日志buffer中的内容. + * + * @return array + */ + public static function getBuffer(): array + { + return static::$buffer; + } + + /** + * @param string $level + * @param string $message + * @param array $context + * @throws Exception + */ + public function log(string $level, string $message, array $context = []): void + { + $template = $this->getTemplate(); + $msg = []; + $module = ArrayHelper::getValue($context, 'module', 'System'); + foreach ($this->template as $tmp) { + switch ($tmp) { + case '%W': + $msg[] = ArrayHelper::getValue($template, $tmp, -1); + break; + case '%L': + $msg[] = $level; + break; + case '%M': + $msg[] = str_replace($this->split, ' ', empty($context) ? $message : strtr($message, $context)); + break; + case '%T': + case '%t': + if ($this->isMicroTime > 0) { + $micsec = $this->isMicroTime > 3 ? 3 : $this->isMicroTime; + $mtimestamp = sprintf("%.{$micsec}f", microtime(true)); // 带毫秒的时间戳 + $timestamp = floor($mtimestamp); // 时间戳 + $milliseconds = round(($mtimestamp - $timestamp) * 1000); // 毫秒 + } else { + $timestamp = time(); + $milliseconds = 0; + } + if ($tmp === '%T') { + $msg[] = date(static::$datetime_format, (int)$timestamp) . '.' . (int)$milliseconds; + } else { + $msg[] = date(static::$datetime_format, (int)$timestamp); + } + break; + case '%Q': + $msg[] = ArrayHelper::getValue($template, $tmp, uniqid()); + break; + case '%H': + $msg[] = ArrayHelper::getValue($template, $tmp, + isset($_SERVER['HOSTNAME']) ? $_SERVER['HOSTNAME'] : 'local'); + break; + case '%P': + $msg[] = ArrayHelper::getValue($template, $tmp, getmypid()); + break; + case '%D': + $msg[] = ArrayHelper::getValue($template, $tmp, 'cli'); + break; + case '%R': + $msg[] = ArrayHelper::getValue($template, $tmp, + isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '/'); + break; + case '%m': + $msg[] = strtoupper(ArrayHelper::getValue($template, $tmp, 'unknow')); + break; + case '%I': + $msg[] = ArrayHelper::getValue($template, $tmp, '127.0.0.1'); + break; + case '%F': + case '%C': + $trace = Co::getBackTrace(Co::getCid(), DEBUG_BACKTRACE_IGNORE_ARGS, + $this->recall_depth + 2); + if ($tmp === '%F') { + $trace = $trace[$this->recall_depth]; + $msg[] = $this->useBasename ? basename($trace['file']) . ':' . $trace['line'] : $trace['file'] . ':' . $trace['line']; + } else { + $trace = $trace[$this->recall_depth + 1]; + $msg[] = $trace['class'] . $trace['type'] . $trace['function']; + } + break; + case '%U': + $msg[] = memory_get_usage(); + break; + case '%u': + $msg[] = memory_get_peak_usage(); + break; + } + } + $color = ArrayHelper::getValue($template, '%c'); + $color && $msg['%c'] = $color; + $key = $this->appName . '_' . $module; + static::$buffer[$key][] = $msg; + $this->flush(); + } + + /** + * @param bool $flush + * @throws Exception + */ + public function flush(bool $flush = false): void + { + if (!empty(static::$buffer) && $flush || ($this->bufferSize !== 0 && $this->bufferSize <= count(static::$buffer))) { + foreach ($this->targetList as $index => $target) { + rgo(function () use ($target, $flush) { + $target->export(static::$buffer); + }); + } + array_splice(static::$buffer, 0); + } + } +} \ No newline at end of file diff --git a/src/SeaslogConfig.php b/src/SeaslogConfig.php new file mode 100644 index 0000000..71c9cf3 --- /dev/null +++ b/src/SeaslogConfig.php @@ -0,0 +1,70 @@ +recall_depth); + } + + /** + * @param string $level + * @param string $message + * @param array $context + * @throws Exception + */ + public function log(string $level, string $message, array $context = []): void + { + $template = $this->getTemplate(); + $module = ArrayHelper::remove($context, 'module', 'System'); + if ($module !== null) { + Seaslog::setLogger($this->appName . '_' . $module); + } + isset($template['%Q']) && Seaslog::setRequestID($template['%Q']); + foreach (array_filter([ + SEASLOG_REQUEST_VARIABLE_DOMAIN_PORT => isset($template['%D']) ? $template['%D'] : null, + SEASLOG_REQUEST_VARIABLE_REQUEST_URI => isset($template['%R']) ? $template['%R'] : null, + SEASLOG_REQUEST_VARIABLE_REQUEST_METHOD => isset($template['%m']) ? $template['%m'] : null, + SEASLOG_REQUEST_VARIABLE_CLIENT_IP => isset($template['%I']) ? $template['%I'] : null + ]) as $key => $value) { + Seaslog::setRequestVariable($key, $value); + } + Seaslog::$level($message); + $this->flush(); + } + + /** + * @param bool $flush + * @throws Exception + */ + public function flush(bool $flush = false): void + { + $total = Seaslog::getBufferCount(); + if ($flush || $total >= $this->bufferSize) { + $buffer = Seaslog::getBuffer(); + Seaslog::flushBuffer(0); + foreach ($this->targetList as $index => $target) { + rgo(function () use ($target, $buffer, $flush) { + $target->export($buffer); + }); + } + unset($buffer); + } + } +} \ No newline at end of file diff --git a/src/Targets/AbstractTarget.php b/src/Targets/AbstractTarget.php new file mode 100644 index 0000000..7d9880d --- /dev/null +++ b/src/Targets/AbstractTarget.php @@ -0,0 +1,51 @@ +split = $split; + $this->levelList = $levelList; + } + + /** + * @param array $messages + */ + abstract public function export(array $messages): void; + + /** + * @param string $str + * @param string $find + * @param int $n + * @return int + */ + protected function str_n_pos(string $str, string $find, int $n): int + { + $pos_val = 0; + for ($i = 1; $i <= $n; $i++) { + $pos = strpos($str, $find); + $str = substr($str, $pos + 1); + $pos_val = $pos + $pos_val + 1; + } + return $pos_val - 1; + } +} \ No newline at end of file diff --git a/src/Targets/EchoTarget.php b/src/Targets/EchoTarget.php new file mode 100644 index 0000000..0e0c479 --- /dev/null +++ b/src/Targets/EchoTarget.php @@ -0,0 +1,29 @@ +split, trim($msg)); + } + if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { + continue; + } + echo implode($this->split, $msg) . PHP_EOL; + } + } + } +} \ No newline at end of file diff --git a/src/Targets/KafkaTarget.php b/src/Targets/KafkaTarget.php new file mode 100644 index 0000000..28ad512 --- /dev/null +++ b/src/Targets/KafkaTarget.php @@ -0,0 +1,108 @@ +client = $client; + $this->topic = $topic; + $this->template = $template; + $this->levelList = $levelList; + } + + /** + * @param array $messages + * @throws Exception + */ + public function export(array $messages): void + { + foreach ($messages as $module => $message) { + foreach ($message as $msg) { + if (is_string($msg)) { + switch (ini_get('seaslog.appender')) { + case '2': + case '3': + $msg = trim(substr($msg, $this->str_n_pos($msg, ' ', 6))); + break; + case '1': + default: + $fileName = basename($module); + $module = substr($fileName, 0, strrpos($fileName, '_')); + } + $msg = explode($this->split, trim($msg)); + } else { + ArrayHelper::remove($msg, '%c'); + } + if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { + continue; + } + $log = [ + 'appname' => $module, + ]; + foreach ($msg as $index => $value) { + [$name, $type] = $this->template[$index]; + switch ($type) { + case "string": + $log[$name] = trim($value); + break; + case "timespan": + $log[$name] = strtotime(explode('.', $value)[0]); + break; + case "int": + $log[$name] = (int)$value; + break; + default: + $log[$name] = trim($value); + } + } + $this->client->send([ + [ + 'topic' => $this->topic, + 'value' => json_encode($log), + 'key' => '' + ] + ]); + } + } + } +} \ No newline at end of file diff --git a/src/Targets/SeasStashTarget.php b/src/Targets/SeasStashTarget.php new file mode 100644 index 0000000..eff94ea --- /dev/null +++ b/src/Targets/SeasStashTarget.php @@ -0,0 +1,62 @@ +clientPool = $clientPool; + $this->levelList = $levelList; + } + + /** + * @param array $messages + * @throws Exception + */ + public function export(array $messages): void + { + $connection = $this->clientPool->getConnection(); + foreach ($messages as $module => $message) { + foreach ($message as $msg) { + if (is_string($msg)) { + switch (ini_get('seaslog.appender')) { + case '2': + case '3': + $msg = trim(substr($msg, $this->str_n_pos($msg, ' ', 6))); + break; + case '1': + default: + $fileName = basename($module); + $module = substr($fileName, 0, strpos($fileName, '_', -1)); + } + $msg = explode($this->split, trim($msg)); + } + if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { + continue; + } + ArrayHelper::remove($msg, '%c'); + $msg = $module . '@' . str_replace(PHP_EOL, '', implode($this->split, $msg)) . PHP_EOL; + $connection->send($msg); + } + } + $this->clientPool->release($connection); + } +} \ No newline at end of file diff --git a/src/Targets/StyleTarget.php b/src/Targets/StyleTarget.php new file mode 100644 index 0000000..c187c12 --- /dev/null +++ b/src/Targets/StyleTarget.php @@ -0,0 +1,130 @@ +color = $color; + $this->levelList = $levelList; + } + + /** + * @param array $messages + */ + public function export(array $messages): void + { + foreach ($messages as $message) { + foreach ($message as $msg) { + if (is_string($msg)) { + switch (ini_get('seaslog.appender')) { + case '2': + case '3': + $msg = trim(substr($msg, $this->str_n_pos($msg, ' ', 6))); + break; + } + $msg = explode($this->split, trim($msg)); + $ranColor = $this->default; + } else { + $ranColor = ArrayHelper::remove($msg, '%c'); + } + if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { + continue; + } + if (empty($ranColor)) { + $ranColor = $this->default; + } elseif (is_array($ranColor) && count($ranColor) === 2) { + $ranColor = $ranColor[0]; + } else { + $ranColor = $this->default; + } + $context = []; + foreach ($msg as $index => $m) { + if (isset($this->colorTemplate[$index])) { + $color = $this->colorTemplate[$index]; + $m = is_string($m) ? trim($m) : (string)$m; + $level = trim($msg[$this->levelIndex]); + switch ($color) { + case self::COLOR_LEVEL: + $context[] = $this->color->apply($this->getLevelColor($level), $m); + break; + case self::COLOR_DEFAULT: + $context[] = $this->color->apply($this->default, $m); + break; + case self::COLOR_RANDOM: + $context[] = $this->color->apply($ranColor, $m); + break; + default: + $context[] = $this->color->apply($color, $m); + } + } else { + $context[] = $this->color->apply($this->default, $m); + } + } + if (isset($context)) { + echo implode(' ' . $this->color->apply($this->splitColor, '|') . ' ', $context) . PHP_EOL; + } + } + } + } + + /** + * @param string $level + * @return string + */ + private function getLevelColor(string $level): string + { + switch (strtolower($level)) { + case LogLevel::INFO: + return "green"; + case LogLevel::DEBUG: + return 'dark_gray'; + case LogLevel::ERROR: + return "red"; + case LogLevel::WARNING: + return 'yellow'; + default: + return 'light_red'; + } + } + +} \ No newline at end of file diff --git a/src/Targets/WebsocketTarget.php b/src/Targets/WebsocketTarget.php new file mode 100644 index 0000000..41b1832 --- /dev/null +++ b/src/Targets/WebsocketTarget.php @@ -0,0 +1,127 @@ +connections as $fd) { + if ($swooleServer->isEstablished($fd)) { + foreach ($messages as $message) { + foreach ($message as $msg) { + if (is_string($msg)) { + switch (ini_get('seaslog.appender')) { + case '2': + case '3': + $msg = trim(substr($msg, $this->str_n_pos($msg, ' ', 6))); + break; + } + $msg = explode($this->split, trim($msg)); + $ranColor = $this->default; + } else { + $ranColor = ArrayHelper::remove($msg, '%c'); + } + if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), + $this->levelList)) { + continue; + } + if (empty($ranColor)) { + $ranColor = $this->default; + } elseif (is_array($ranColor) && count($ranColor) === 2) { + $ranColor = $ranColor[1]; + } else { + $ranColor = $this->default; + } + foreach ($msg as $index => $m) { + $msg[$index] = trim($m); + if (isset($this->colorTemplate[$index])) { + $color = $this->colorTemplate[$index]; + $level = trim($msg[$this->levelIndex]); + switch ($color) { + case self::COLOR_LEVEL: + $colors[] = HtmlColor::getColor($this->getLevelColor($level)); + break; + case self::COLOR_RANDOM: + $colors[] = HtmlColor::getColor($ranColor); + break; + case self::COLOR_DEFAULT: + $colors[] = $this->default; + break; + default: + $colors[] = HtmlColor::getColor($color); + } + } else { + $colors[] = $this->default; + } + } + $msg = json_encode([$msg, $colors], JSON_UNESCAPED_UNICODE); + rgo(function () use ($swooleServer, $fd, $msg) { + $swooleServer->push($fd, $msg); + }); + } + } + } + } + } + + /** + * @param string $level + * @return string + */ + private function getLevelColor(string $level): string + { + switch (strtolower($level)) { + case LogLevel::INFO: + return "green"; + case LogLevel::DEBUG: + return 'dark_gray'; + case LogLevel::ERROR: + return "red"; + case LogLevel::WARNING: + return 'yellow'; + default: + return 'light_red'; + } + } + +} \ No newline at end of file diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 0000000..82ba0e9 --- /dev/null +++ b/src/functions.php @@ -0,0 +1,25 @@ +getTraceAsString()); + return 0; + } + }); + } +} \ No newline at end of file diff --git a/tests/ArrayHelperTest.php b/tests/ArrayHelperTest.php new file mode 100644 index 0000000..ee18951 --- /dev/null +++ b/tests/ArrayHelperTest.php @@ -0,0 +1,35 @@ +assertEquals('test', ArrayHelper::getValue([ + 'key' => 'test' + ], 'key')); + } + + public function testGetValueDefault() + { + $this->assertEquals('test', ArrayHelper::getValue([ + ], 'key', 'test')); + } + + public function testRemove() + { + $value = [ + 'key' => 'test' + ]; + $this->assertEquals('test', ArrayHelper::remove($value, 'key')); + $this->assertArrayNotHasKey('test', $value); + } +} \ No newline at end of file diff --git a/tests/ConsoleColorTest.php b/tests/ConsoleColorTest.php new file mode 100644 index 0000000..0a5a836 --- /dev/null +++ b/tests/ConsoleColorTest.php @@ -0,0 +1,28 @@ +assertEquals('[none test]', $color->apply('none', '[none test]')); + $this->assertEquals('[green test]', $color->apply('green', '[green test]')); + } + + public function testSetForceStyle(){ + $color = new ConsoleColor(); + $color->setForceStyle(true); + $this->assertTrue($color->isStyleForced()); + $color->setForceStyle(false); + $this->assertFalse($color->isStyleForced()); + } +} \ No newline at end of file diff --git a/tests/ContestTest.php b/tests/ContestTest.php new file mode 100644 index 0000000..95a993f --- /dev/null +++ b/tests/ContestTest.php @@ -0,0 +1,43 @@ +assertEquals('value', Context::get('key')); + }); + } + + public function testHas() + { + go(function () { + Context::set('key', 'value'); + $this->assertTrue(Context::has('key')); + }); + } + + public function testNotHas() + { + go(function () { + $this->assertFalse(Context::has('key')); + }); + } + + public function testDelete() + { + go(function () { + Context::set('key', 'value'); + $this->assertEquals('value', Context::get('key')); + Context::delete('key'); + $this->assertFalse(Context::has('key')); + }); + } +} \ No newline at end of file diff --git a/tests/StyleTargetTest.php b/tests/StyleTargetTest.php new file mode 100644 index 0000000..fee31d8 --- /dev/null +++ b/tests/StyleTargetTest.php @@ -0,0 +1,86 @@ +assertInstanceOf(StyleTarget::class, $target); + $target->export([ + 'logger' => [ + implode(' | ', [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + 'UNKNOW', + '127.0.0.1', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[SeasLog String]' + ]) + ] + ]); + } + + public function testExport() + { + $target = new StyleTarget(); + $this->assertInstanceOf(StyleTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + 'UNKNOW', + '127.0.0.1', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithLevel() + { + $target = new StyleTarget([ + 'info' + ]); + $this->assertInstanceOf(StyleTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + 'UNKNOW', + '127.0.0.1', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[Test WARNING]' + ], + [ + '2019-08-30 09:58:01.937', + 'INFO', + 'vendor/phpunit/phpunit/phpunit', + 'UNKNOW', + '127.0.0.1', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[Test INFO]' + ] + ] + ]); + } +} \ No newline at end of file diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php new file mode 100644 index 0000000..e1e75a3 --- /dev/null +++ b/tests/SwooleLoggerTest.php @@ -0,0 +1,291 @@ + new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); + return $logger; + } + + public function testGetBasePath() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger->getBasePath(); + + $logger = $this->init(1); + $logger->setBasePath('/tmp/seaslogger'); + $basePath = $logger->getBasePath(); + $this->assertEquals('/tmp/seaslogger', $basePath); + } + + public function testSetRequestID() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::setRequestID(1024); + + $logger = $this->init(1); + $result = $logger::setRequestID(1024); + $this->assertTrue($result); + } + + public function testGetRequestID() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::getRequestID(); + + $logger = $this->init(1); + $result = $logger::setRequestID(1024); + $this->assertTrue($result); + $requestID = $logger::getRequestID(); + $this->assertEquals(1024, $requestID); + } + + public function testEmergency() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->emergency('[SeasLog Test]', ['level' => 'emergency']); + } + + public function testAlert() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->alert('[SeasLog Test]', ['level' => 'alert']); + } + + public function testCritical() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->critical('[SeasLog Test]', ['level' => 'critical']); + } + + public function testError() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->error('[SeasLog Test]', ['level' => 'error']); + } + + public function testWarning() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->warning('[SeasLog Test]', ['level' => 'warning']); + } + + public function testNotice() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->notice('[SeasLog Test]', ['level' => 'notice']); + } + + public function testInfo() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[SeasLog Test]', ['level' => 'info']); + } + + public function testDebug() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->debug('[SeasLog Test]', ['level' => 'debug']); + } + + public function testLog() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'log']); + } + + public function testRequestLevel() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->setRequestLevel(Logger::ALL); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); + + $logger->log(0, '[SeasLog Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + } + + public function testSetLogger() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::setLogger('seas'); + + $logger = $this->init(1); + $result = $logger::setLogger('seas'); + $this->assertTrue($result); + } + + public function testGetLastLogger() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::getLastLogger(); + + $logger = $this->init(1); + $result = $logger::setLogger('seas'); + $this->assertTrue($result); + $model = $logger::getLastLogger(); + $this->assertEquals('seas', $model); + } + + public function testSetDatetimeFormat() + { + $logger = $this->init(); + $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + } + + public function testGetDatetimeFormat() + { + $logger = $this->init(); + $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + $format = $logger::getDatetimeFormat(); + $this->assertEquals('Y-m-d H:i:s', $format); + } + + public function testAnalyzerCount() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::analyzerCount(); + + $logger = $this->init(1); + $result = $logger::analyzerCount(); + $this->assertNotNull($result); + } + + public function testAnalyzerDetail() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::analyzerDetail(); + + $logger = $this->init(1); + $result = $logger::analyzerDetail(); + $this->assertNotNull($result); + } + + public function testGetBuffer() + { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[SeasLog Test]', ['level' => 'info']); + $buffer = $logger::getBuffer(); + $this->assertNotNull($buffer); + + $logger = $this->init(1); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[SeasLog Test]', ['level' => 'info']); + $buffer = $logger::getBuffer(); + $this->assertNotNull($buffer); + } + + public function testFlushBuffer() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::flushBuffer(); + + $logger = $this->init(1); + $this->expectException(NotSupportedException::class); + $logger::flushBuffer(); + } + + + public function testInvoke() + { + $logger = $this->init(); + $seasLogger = $logger(['path' => '/tmp/logger']); + $this->assertInstanceOf(Logger::class, $seasLogger); + + $logger = $this->init(1); + $seasLogger = $logger(['path' => '/tmp/logger']); + $this->assertInstanceOf(Logger::class, $seasLogger); + } + + public function testCloseLoggerStreamAll() + { + $logger = $this->init(1); + $logger->setBasePath('/tmp/allLogger'); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); + + $logger->log(0, '[SeasLog Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + + $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); + } + + public function testCloseLoggerStream() + { + $logger = $this->init(); + $this->expectException(NotSupportedException::class); + $logger::closeLoggerStream(); + + $logger = $this->init(1); + $logger->setBasePath('/tmp/PandaLogger'); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); + + $logger->log(0, '[SeasLog Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + + $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ASSIGN, '/tmp/PandaLogger')); + } +} \ No newline at end of file From 17b04ff6a4849aa3e4fb6acb42ac1103b26a7bab Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Fri, 30 Aug 2019 13:37:16 +0800 Subject: [PATCH 02/27] add seaslog require --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1b9d6c5..d1b2058 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ "require": { "php": "^7.1", "psr/log": "^1.0.2", + "ext-seaslog": "^2.0", "lcobucci/clock": "^1.0", "friendsofphp/php-cs-fixer": "^2.11" }, From fc6098035ec36408f1ab23cb121eb8abc6201b5c Mon Sep 17 00:00:00 2001 From: Panda Date: Fri, 30 Aug 2019 19:08:24 +0800 Subject: [PATCH 03/27] Update .travis.yml install swoole --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 7a7a62f..8608830 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ php: before_script: - pecl install SeasLog + - pecl install swoole - composer self-update - composer install --prefer-source --no-interaction --dev From f6eb4041327f5650ebad5383c713840d69b23df7 Mon Sep 17 00:00:00 2001 From: Panda Date: Fri, 30 Aug 2019 19:11:48 +0800 Subject: [PATCH 04/27] install swoole by bin install swoole by bin --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8608830..ce37c1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,8 @@ php: before_script: - pecl install SeasLog - - pecl install swoole + - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - + - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - composer self-update - composer install --prefer-source --no-interaction --dev From b6cc5eacb24acc1c390ad6c709596e3f785bd463 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Sat, 31 Aug 2019 10:38:31 +0800 Subject: [PATCH 05/27] fix type when in strict_types env; commented code for testcase when swoole old version have no getBufferCount function --- src/Logger.php | 4 +- tests/SwooleLoggerTest.php | 140 ++++++++++++++++++------------------- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/Logger.php b/src/Logger.php index 61dc878..a07dc01 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -203,7 +203,7 @@ public static function analyzerCount($level = 'all', $log_path = '*', $key_word if (static::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support analyzerCount"); } - return SeasLog::analyzerCount($level, $log_path, $key_word); + return SeasLog::analyzerCount($level, $log_path, (string)$key_word); } /** @@ -232,7 +232,7 @@ public static function analyzerDetail( return SeasLog::analyzerDetail( $level, $log_path, - $key_word, + (string)$key_word, $start, $limit, $order diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index e1e75a3..9f0766f 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -36,10 +36,10 @@ public function testGetBasePath() $this->expectException(NotSupportedException::class); $logger->getBasePath(); - $logger = $this->init(1); - $logger->setBasePath('/tmp/seaslogger'); - $basePath = $logger->getBasePath(); - $this->assertEquals('/tmp/seaslogger', $basePath); +// $logger = $this->init(1); +// $logger->setBasePath('/tmp/seaslogger'); +// $basePath = $logger->getBasePath(); +// $this->assertEquals('/tmp/seaslogger', $basePath); } public function testSetRequestID() @@ -48,9 +48,9 @@ public function testSetRequestID() $this->expectException(NotSupportedException::class); $logger::setRequestID(1024); - $logger = $this->init(1); - $result = $logger::setRequestID(1024); - $this->assertTrue($result); +// $logger = $this->init(1); +// $result = $logger::setRequestID(1024); +// $this->assertTrue($result); } public function testGetRequestID() @@ -59,11 +59,11 @@ public function testGetRequestID() $this->expectException(NotSupportedException::class); $logger::getRequestID(); - $logger = $this->init(1); - $result = $logger::setRequestID(1024); - $this->assertTrue($result); - $requestID = $logger::getRequestID(); - $this->assertEquals(1024, $requestID); +// $logger = $this->init(1); +// $result = $logger::setRequestID(1024); +// $this->assertTrue($result); +// $requestID = $logger::getRequestID(); +// $this->assertEquals(1024, $requestID); } public function testEmergency() @@ -153,9 +153,9 @@ public function testSetLogger() $this->expectException(NotSupportedException::class); $logger::setLogger('seas'); - $logger = $this->init(1); - $result = $logger::setLogger('seas'); - $this->assertTrue($result); +// $logger = $this->init(1); +// $result = $logger::setLogger('seas'); +// $this->assertTrue($result); } public function testGetLastLogger() @@ -164,11 +164,11 @@ public function testGetLastLogger() $this->expectException(NotSupportedException::class); $logger::getLastLogger(); - $logger = $this->init(1); - $result = $logger::setLogger('seas'); - $this->assertTrue($result); - $model = $logger::getLastLogger(); - $this->assertEquals('seas', $model); +// $logger = $this->init(1); +// $result = $logger::setLogger('seas'); +// $this->assertTrue($result); +// $model = $logger::getLastLogger(); +// $this->assertEquals('seas', $model); } public function testSetDatetimeFormat() @@ -193,9 +193,9 @@ public function testAnalyzerCount() $this->expectException(NotSupportedException::class); $logger::analyzerCount(); - $logger = $this->init(1); - $result = $logger::analyzerCount(); - $this->assertNotNull($result); +// $logger = $this->init(1); +// $result = $logger::analyzerCount(); +// $this->assertNotNull($result); } public function testAnalyzerDetail() @@ -204,9 +204,9 @@ public function testAnalyzerDetail() $this->expectException(NotSupportedException::class); $logger::analyzerDetail(); - $logger = $this->init(1); - $result = $logger::analyzerDetail(); - $this->assertNotNull($result); +// $logger = $this->init(1); +// $result = $logger::analyzerDetail(); +// $this->assertNotNull($result); } public function testGetBuffer() @@ -217,11 +217,11 @@ public function testGetBuffer() $buffer = $logger::getBuffer(); $this->assertNotNull($buffer); - $logger = $this->init(1); - $this->assertInstanceOf(Logger::class, $logger); - $logger->info('[SeasLog Test]', ['level' => 'info']); - $buffer = $logger::getBuffer(); - $this->assertNotNull($buffer); +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->info('[SeasLog Test]', ['level' => 'info']); +// $buffer = $logger::getBuffer(); +// $this->assertNotNull($buffer); } public function testFlushBuffer() @@ -230,9 +230,9 @@ public function testFlushBuffer() $this->expectException(NotSupportedException::class); $logger::flushBuffer(); - $logger = $this->init(1); - $this->expectException(NotSupportedException::class); - $logger::flushBuffer(); +// $logger = $this->init(1); +// $this->expectException(NotSupportedException::class); +// $logger::flushBuffer(); } @@ -242,29 +242,29 @@ public function testInvoke() $seasLogger = $logger(['path' => '/tmp/logger']); $this->assertInstanceOf(Logger::class, $seasLogger); - $logger = $this->init(1); - $seasLogger = $logger(['path' => '/tmp/logger']); - $this->assertInstanceOf(Logger::class, $seasLogger); +// $logger = $this->init(1); +// $seasLogger = $logger(['path' => '/tmp/logger']); +// $this->assertInstanceOf(Logger::class, $seasLogger); } - public function testCloseLoggerStreamAll() - { - $logger = $this->init(1); - $logger->setBasePath('/tmp/allLogger'); - $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); - $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); - $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); - $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); - $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); - $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); - $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); - $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); - - $logger->log(0, '[SeasLog Test]', ['level' => 'default']); - $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); - - $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); - } +// public function testCloseLoggerStreamAll() +// { +// $logger = $this->init(1); +// $logger->setBasePath('/tmp/allLogger'); +// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); +// $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); +// $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); +// $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); +// $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); +// $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); +// $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); +// $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); +// +// $logger->log(0, '[SeasLog Test]', ['level' => 'default']); +// $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); +// +// $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); +// } public function testCloseLoggerStream() { @@ -272,20 +272,20 @@ public function testCloseLoggerStream() $this->expectException(NotSupportedException::class); $logger::closeLoggerStream(); - $logger = $this->init(1); - $logger->setBasePath('/tmp/PandaLogger'); - $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); - $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); - $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); - $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); - $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); - $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); - $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); - $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); - - $logger->log(0, '[SeasLog Test]', ['level' => 'default']); - $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); - - $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ASSIGN, '/tmp/PandaLogger')); +// $logger = $this->init(1); +// $logger->setBasePath('/tmp/PandaLogger'); +// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); +// $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); +// $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); +// $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); +// $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); +// $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); +// $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); +// $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); +// +// $logger->log(0, '[SeasLog Test]', ['level' => 'default']); +// $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); +// +// $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ASSIGN, '/tmp/PandaLogger')); } } \ No newline at end of file From 3e661e78eebb5af4093a3c54d3b3bae08f5842fe Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Sat, 31 Aug 2019 15:46:18 +0800 Subject: [PATCH 06/27] keep template output like seaslog --- src/LoggerConfig.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/LoggerConfig.php b/src/LoggerConfig.php index b514912..353aa49 100644 --- a/src/LoggerConfig.php +++ b/src/LoggerConfig.php @@ -139,8 +139,7 @@ public function log(string $level, string $message, array $context = []): void $msg[] = ArrayHelper::getValue($template, $tmp, uniqid()); break; case '%H': - $msg[] = ArrayHelper::getValue($template, $tmp, - isset($_SERVER['HOSTNAME']) ? $_SERVER['HOSTNAME'] : 'local'); + $msg[] = ArrayHelper::getValue($template, $tmp, $_SERVER['HOSTNAME']); break; case '%P': $msg[] = ArrayHelper::getValue($template, $tmp, getmypid()); @@ -149,14 +148,14 @@ public function log(string $level, string $message, array $context = []): void $msg[] = ArrayHelper::getValue($template, $tmp, 'cli'); break; case '%R': - $msg[] = ArrayHelper::getValue($template, $tmp, - isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : '/'); + $msg[] = ArrayHelper::getValue($template, $tmp, $_SERVER['SCRIPT_NAME']); break; case '%m': - $msg[] = strtoupper(ArrayHelper::getValue($template, $tmp, 'unknow')); + $method = ArrayHelper::getValue($template, $tmp); + $msg[] = $method ? strtoupper($method) : $_SERVER['SHELL']; break; case '%I': - $msg[] = ArrayHelper::getValue($template, $tmp, '127.0.0.1'); + $msg[] = ArrayHelper::getValue($template, $tmp, 'local'); break; case '%F': case '%C': From 7cdb72a131279a77a52ce21e794cca034155ea8d Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 3 Sep 2019 15:27:41 +0800 Subject: [PATCH 07/27] fix docker image and sql --- docker/docker-compose.yaml | 7 ++++--- sql/seaslog.sql | 40 +++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 317c08d..002768c 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -17,6 +17,7 @@ services: - 5555:5555 environment: KAFKA_BROKER_ID: 1 + KAFKA_ADVERTISED_HOST_NAME: kafka KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT @@ -31,15 +32,15 @@ services: image: zenko/kafka-manager restart: unless-stopped ports: - - 9000:9000 - environment: + - 9091:9000 + environment: ZK_HOSTS: "zookeeper:2181" depends_on: - zookeeper - kafka clickhouse: - image: yandex/clickhouse-server + image: yandex/clickhouse-server:19.13.3.26 restart: unless-stopped ports: - 8123:8123 diff --git a/sql/seaslog.sql b/sql/seaslog.sql index e9b0946..ef91b8b 100644 --- a/sql/seaslog.sql +++ b/sql/seaslog.sql @@ -1,5 +1,25 @@ create database if not exists logs; +CREATE TABLE if not exists logs.kafka_seaslog +( + `appname` String, + `datetime` DateTime, + `level` String, + `request_uri` String, + `request_method` String, + `clientip` String, + `requestid` String, + `filename` String, + `memoryusage` UInt64, + `message` String +) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka:29092', + kafka_topic_list = 'seaslog', + kafka_group_name = 'clickhouse', + kafka_format = 'JSONEachRow', + kafka_skip_broken_messages = 1, + kafka_num_consumers = 1; +--消费者数量根据情况自定义 + CREATE TABLE if not exists logs.seaslog ( `appname` String, @@ -30,23 +50,3 @@ SELECT appname, message FROM logs.kafka_seaslog; -CREATE TABLE if not exists logs.kafka_seaslog -( - `appname` String, - `datetime` DateTime, - `level` String, - `request_uri` String, - `request_method` String, - `clientip` String, - `requestid` String, - `filename` String, - `memoryusage` UInt64, - `message` String -) ENGINE = Kafka SETTINGS kafka_broker_list = 'kafka:29092', - kafka_topic_list = 'seaslog', - kafka_group_name = 'clickhouse', - kafka_format = 'JSONEachRow', - kafka_skip_broken_messages = 1, - kafka_num_consumers = 1; ---消费者数量根据情况自定义 - From 8347b817628a6cc967732dc5146c587a71ffa69e Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 3 Sep 2019 20:48:30 +0800 Subject: [PATCH 08/27] fix filename --- src/Exceptions/{Exception.php => NotSupportedException.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Exceptions/{Exception.php => NotSupportedException.php} (100%) diff --git a/src/Exceptions/Exception.php b/src/Exceptions/NotSupportedException.php similarity index 100% rename from src/Exceptions/Exception.php rename to src/Exceptions/NotSupportedException.php From 4f42e4b0c752ba40a543abe6826c7bbedfed1428 Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 11:49:48 +0800 Subject: [PATCH 09/27] fix ci --- .travis.yml | 45 +++++++++++++++++++++++++------------- .travis/ci.ini | 8 +++++++ .travis/seaslog.install.sh | 10 +++++++++ .travis/swoole.install.sh | 10 +++++++++ 4 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 .travis/ci.ini create mode 100644 .travis/seaslog.install.sh create mode 100644 .travis/swoole.install.sh diff --git a/.travis.yml b/.travis.yml index ce37c1c..cbc4589 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,37 @@ language: php -php: - - 7.1 - - 7.2 +sudo: required -before_script: - - pecl install SeasLog - - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - - - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - - composer self-update - - composer install --prefer-source --no-interaction --dev +matrix: + include: + - php: 7.2 + env: SW_VERSION="4.4.5";SL_VERSION="SeasLog-2.0.2" + - php: 7.3 + env: SW_VERSION="4.4.5";SL_VERSION="SeasLog-2.0.2" + - php: master + env: SW_VERSION="4.4.5";SL_VERSION="SeasLog-2.0.2" -after_script: - - wget https://scrutinizer-ci.com/ocular.phar - - php ocular.phar code-coverage:upload --format=php-clover coverage.clover + allow_failures: + - php: master -script: ./vendor/bin/phpunit --coverage-clover=coverage.clover +services: + - mysql -matrix: - fast_finish: true +before_install: + - export PHP_MAJOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 1)" + - export PHP_MINOR="$(`phpenv which php` -r 'echo phpversion();' | cut -d '.' -f 2)" + - echo $PHP_MAJOR + - echo $PHP_MINOR + +install: + - cd $TRAVIS_BUILD_DIR + - bash .travis/swoole.install.sh + - phpenv config-rm xdebug.ini || echo "xdebug not available" + - phpenv config-add .travis/ci.ini + +before_script: + - cd $TRAVIS_BUILD_DIR + - composer install +script: + - composer test \ No newline at end of file diff --git a/.travis/ci.ini b/.travis/ci.ini new file mode 100644 index 0000000..04b0c20 --- /dev/null +++ b/.travis/ci.ini @@ -0,0 +1,8 @@ +[opcache] +opcache.enable_cli=1 + +[seaslog] +extension = "seaslog.so" + +[swoole] +extension = "swoole.so" \ No newline at end of file diff --git a/.travis/seaslog.install.sh b/.travis/seaslog.install.sh new file mode 100644 index 0000000..fb7dedd --- /dev/null +++ b/.travis/seaslog.install.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +wget https://github.com/SeasX/SeasLog/archive/${SL_VERSION}-O SeasLog.tar.gz +mkdir -p SeasLog +tar -xf SeasLog.tar.gz -C SeasLog --strip-components=1 +rm SeasLog.tar.gz +cd SeasLog +phpize +./configure +make -j$(nproc) +make install \ No newline at end of file diff --git a/.travis/swoole.install.sh b/.travis/swoole.install.sh new file mode 100644 index 0000000..1c546f5 --- /dev/null +++ b/.travis/swoole.install.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +wget https://github.com/swoole/swoole-src/archive/v${SW_VERSION}.tar.gz -O swoole.tar.gz +mkdir -p swoole +tar -xf swoole.tar.gz -C swoole --strip-components=1 +rm swoole.tar.gz +cd swoole +phpize +./configure --enable-openssl --enable-mysqlnd +make -j$(nproc) +make install \ No newline at end of file From bb8f98b2b9cd748bd19e0c3723e1d0a1d324e8e3 Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 11:54:30 +0800 Subject: [PATCH 10/27] install seaslog --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cbc4589..b50bb79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_install: install: - cd $TRAVIS_BUILD_DIR - bash .travis/swoole.install.sh + - bash .travis/seaslog.install.sh - phpenv config-rm xdebug.ini || echo "xdebug not available" - phpenv config-add .travis/ci.ini From cd33cc972d5d7fdc5c976866fd6342027cfeed4c Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 14:36:41 +0800 Subject: [PATCH 11/27] fix download url --- .travis/seaslog.install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/seaslog.install.sh b/.travis/seaslog.install.sh index fb7dedd..b1779fa 100644 --- a/.travis/seaslog.install.sh +++ b/.travis/seaslog.install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -wget https://github.com/SeasX/SeasLog/archive/${SL_VERSION}-O SeasLog.tar.gz +wget https://github.com/SeasX/SeasLog/archive/${SL_VERSION}.tar.gz mkdir -p SeasLog tar -xf SeasLog.tar.gz -C SeasLog --strip-components=1 rm SeasLog.tar.gz From cb7cc629e10d76059b4aad2f59fbed6907169bdc Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 14:42:17 +0800 Subject: [PATCH 12/27] rename Seaslog file name --- .travis/seaslog.install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/seaslog.install.sh b/.travis/seaslog.install.sh index b1779fa..0664632 100644 --- a/.travis/seaslog.install.sh +++ b/.travis/seaslog.install.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -wget https://github.com/SeasX/SeasLog/archive/${SL_VERSION}.tar.gz +wget https://github.com/SeasX/SeasLog/archive/${SL_VERSION}.tar.gz -O SeasLog.tar.gz mkdir -p SeasLog tar -xf SeasLog.tar.gz -C SeasLog --strip-components=1 rm SeasLog.tar.gz From c1be8a058e8795458d8f89356a4c4b96cd84ddae Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 15:28:36 +0800 Subject: [PATCH 13/27] add code-coverage --- .travis.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b50bb79..24b57af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ sudo: required matrix: include: + - php: 7.1 + env: SW_VERSION="4.4.5";SL_VERSION="SeasLog-2.0.2" - php: 7.2 env: SW_VERSION="4.4.5";SL_VERSION="SeasLog-2.0.2" - php: 7.3 @@ -32,7 +34,11 @@ install: before_script: - cd $TRAVIS_BUILD_DIR - - composer install + - composer self-update + - composer install --prefer-source --no-interaction --dev + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover -script: - - composer test \ No newline at end of file +script: ./vendor/bin/phpunit --coverage-clover=coverage.clover \ No newline at end of file From 7f620be47bbd7e891e590ea998b8ee714ee5a2cf Mon Sep 17 00:00:00 2001 From: wujunze Date: Mon, 9 Sep 2019 15:39:50 +0800 Subject: [PATCH 14/27] fix test --- tests/SwooleLoggerTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 9f0766f..2c1740e 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -30,10 +30,12 @@ public function init(int $type = 0) return $logger; } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testGetBasePath() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger->getBasePath(); // $logger = $this->init(1); From 9e130e97821bc0dedef1e85eea4088fbacf65de0 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 10 Sep 2019 16:38:22 +0800 Subject: [PATCH 15/27] add customer template support and add more unit test --- README.md | 20 +- docker-seaslog.ini | 115 ++++++++++ example/logger.php | 22 +- src/AbstractConfig.php | 32 ++- src/ConsoleColor.php | 18 +- src/HtmlColor.php | 8 + src/LoggerConfig.php | 24 +- src/SeaslogConfig.php | 14 +- src/Targets/AbstractTarget.php | 45 +++- src/Targets/EchoTarget.php | 3 + src/Targets/KafkaTarget.php | 73 ++++-- src/Targets/SeasStashTarget.php | 3 + src/Targets/StyleTarget.php | 20 +- src/Targets/WebsocketTarget.php | 42 ++-- tests/ArrayHelperTest.php | 2 +- tests/ConsoleColorTest.php | 5 +- tests/{ContestTest.php => ContextTest.php} | 12 +- tests/EchoTargetTest.php | 131 +++++++++++ tests/HtmlColorTest.php | 23 ++ tests/LoggerTest.php | 18 +- tests/NotSupportedExceptionTest.php | 29 +++ tests/ProcessManager.php | 143 ++++++++++++ tests/SeasStashTargetTest.php | 132 +++++++++++ tests/StyleTargetTest.php | 79 ++++++- tests/SwooleLoggerTest.php | 251 ++++++++++++++++----- tests/TestCase.php | 2 +- tests/WebSocketTargetTest.php | 149 ++++++++++++ 27 files changed, 1248 insertions(+), 167 deletions(-) create mode 100644 docker-seaslog.ini rename tests/{ContestTest.php => ContextTest.php} (80%) create mode 100644 tests/EchoTargetTest.php create mode 100644 tests/HtmlColorTest.php create mode 100644 tests/NotSupportedExceptionTest.php create mode 100644 tests/ProcessManager.php create mode 100644 tests/SeasStashTargetTest.php create mode 100644 tests/WebSocketTargetTest.php diff --git a/README.md b/README.md index a80f1cc..a2cea8d 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,11 @@ $ composer require seasx/seas-logger * 日志收集建议使用`docker`目录下的`docker-compose.yaml`启动套件,用`sql`目录下的`seaslog.sql`在`Clickhouse`中创建数据库和表,`LoggerConfig`中添加`KafkaTarget`,即可在`Clickhouse`中看到日志 * 默认带的BI套件为`Superset`,可以做一些分析 * 生成环境建议`StyleTarget`或者`WebsocketTarget`仅输出`Warning`和`Error`日志或仅保留`KafkaTarget`,使用`SeaslogConfig`(等待最新版本发布)配置,`KafkaTarget`可以输出所有日志 -* `WebsocketTarget`由于各个框架的`Server`获取方式不一致,需要自己注册一个`getServer():?\Swoole\Server`的`function`返回`\Swoole\Server`即可,默认会往所有`Websocket`连接发送日志,如果需要过滤`fd`可以自定义`Target`。 +* `WebsocketTarget`由于各个框架的`Server`获取方式不一致,需要调用`setGetServer`注册获取`Server`的回调函数返回`\Swoole\Server`,默认会往所有`Websocket`连接发送日志,如果需要过滤`fd`可以自定义`Target`。 +* 支持自定义模板,符号为`%A`,默认在`%M`之前,可以在`registerTemplate`里面设置统一的值,同时会被日志记录方法的`Context`参数中设置`template`键值覆盖。 * 撸码建议采用`DI`依赖注入的方式注册`Logger` + ### 非Swoole用法 ```php @@ -88,8 +90,12 @@ use Seasx\SeasLogger\Targets\StyleTarget; new Broker([ 'brokerVersion' => '1.0.0', ]), new Pool([ - 'uri' => 'kafka:9092' - ])), [], 'seaslog') + 'uri' => '192.168.5.134:9092' + ])), + [], + 'seaslog_test', + [['task_id', 'string'], ['worker_id', 'string']]//自定义模板添加的处理字段,顺序需要按照日志记录中的template数组一致 + ) ], [ 'appName' => 'Seaslog',//应用名:远程发送日志的时候用于区分是哪个应用发送来的 'bufferSize' => 1000,//定量:buffer>=时会输出,默认为1 @@ -128,12 +134,16 @@ use Seasx\SeasLogger\Targets\StyleTarget; ] ]); } + $requestVar['%A'] = ['123', '456'];//%A为自定义字段,会被log里面的template覆盖 Context::set(Logger::CONTEXT_KEY, $requestVar); } return $requestVar; }); - //这里区别于标准PSR-3,Context占用一个固定key(module),作用与Seaslog的Logger配置一样,默认值为System - $logger->info("test logger", ['module' => 'logger']); + /* + * 这里区别于标准PSR-3,Context占用两个固定key(module),作用与Seaslog的Logger参数一样,默认值为System + * template为用户自定义模板对应的填充值,默认为[],不填充 + */ + $logger->info("test logger $i", ['module' => 'logger', 'template' => ['abc', 'def']]); }); ``` diff --git a/docker-seaslog.ini b/docker-seaslog.ini new file mode 100644 index 0000000..1b39a41 --- /dev/null +++ b/docker-seaslog.ini @@ -0,0 +1,115 @@ +[SeasLog] +;configuration for php SeasLog module +extension = seaslog.so + +;默认log根目录 +seaslog.default_basepath = "/data/runtime/logs" + +;默认logger目录 +seaslog.default_logger = "Rabbit" + +;日期格式配置 默认"Y-m-d H:i:s" +seaslog.default_datetime_format = "Y-m-d H:i:s" + +;日志格式模板 默认"%T | %L | %P | %Q | %t | %M" +seaslog.default_template = "%T | %L | %R | %m | %I | %Q | %F | %U | %M" + +;是否以目录区分Logger 1是(默认) 0否 +seaslog.disting_folder = 0 + +;是否以type分文件 1是 0否(默认) +seaslog.disting_type = 1 + +;是否每小时划分一个文件 1是 0否(默认) +seaslog.disting_by_hour = 1 + +;是否启用buffer 1是 0否(默认) +seaslog.use_buffer = 1 + +;buffer中缓冲数量 默认0(不使用buffer_size) +seaslog.buffer_size = 300000 + +;cli运行时关闭buffer +;1是 0否(默认) +seaslog.buffer_disabled_in_cli = 0 + +;记录日志级别,数字越大,根据级别记的日志越多。 +;0-EMERGENCY 1-ALERT 2-CRITICAL 3-ERROR 4-WARNING 5-NOTICE 6-INFO 7-DEBUG 8-ALL +;默认8(所有日志) +; +; 注意, 该配置项自1.7.0版本开始有变动。 +; 在1.7.0版本之前, 该值数字越小,根据级别记的日志越多: +; 0-all 1-debug 2-info 3-notice 4-warning 5-error 6-critical 7-alert 8-emergency +; 1.7.0 之前的版本,该值默认为0(所有日志); +seaslog.level = 8 + +;日志函数调用回溯层级 +;影响预定义变量 %F 中的行数 +;默认0 +seaslog.recall_depth = 3 + +;自动记录notice 默认0(关闭) +seaslog.trace_notice = 0 + +;自动记录warning 默认0(关闭) +seaslog.trace_warning = 0 + +;自动记录错误 默认1(开启) +seaslog.trace_error = 1 + +;自动记录异常信息 默认0(关闭) +seaslog.trace_exception = 0 + +;日志存储介质 1File 2TCP 3UDP (默认为1) +seaslog.appender = 1 + +;写入重试次数 +;默认0(不重试) +seaslog.appender_retry = 3 + +;接收ip 默认127.0.0.1 (当使用TCP或UDP时必填) +seaslog.remote_host = "seasstash.log" + +;接收端口 默认514 (当使用TCP或UDP时必填) +seaslog.remote_port = 514 + +;接收端口的超时时间 默认1秒 +seaslog.remote_timeout = 1 + +;过滤日志中的回车和换行符 (默认为0) +seaslog.trim_wrap = 1 + +;是否开启抛出SeasLog自身异常 1开启(默认) 0否 +seaslog.throw_exception = 1 + +;是否开启忽略SeasLog自身warning 1开启(默认) 0否 +seaslog.ignore_warning = 1 + +;是否开启性能追踪 1开启 0关闭(默认) +seaslog.trace_performance = 0 + +;性能追踪时的千分比采样率 +;默认10,即百分之一 +seaslog.trace_performance_sample_rate = 100 + +;性能追踪时的开始层级 默认从第1层开始 +seaslog.trace_performance_start_depth = 1 + +;性能追踪时深度层级 默认5层 +seaslog.trace_performance_max_depth = 5 + +;性能追踪时每层的函数最大数 按wall_time降序排列top 默认top5 +seaslog.trace_performance_max_functions_per_depth = 5 + +;性能追踪时当前请求执行时间的记录阈值 只有当请求执行时间大于该值时,才记录性能日志 默认1000ms +seaslog.trace_performance_min_wall_time = 1000 + +;性能追踪时每个方法执行时间的记录阈值 只有当方法执行时间大于该值时,才参与计算 默认10ms +seaslog.trace_performance_min_function_wall_time = 10 + +[fork] +;是否开启多线程 1开启 0关闭 +fork_open = 1 + +;线程个数 +fork_count = 3 \ No newline at end of file diff --git a/example/logger.php b/example/logger.php index a37cca9..2bb5fd7 100644 --- a/example/logger.php +++ b/example/logger.php @@ -30,7 +30,11 @@ 'brokerVersion' => '1.0.0', ]), new Pool([ 'uri' => '192.168.5.134:9092' - ])), [], 'seaslog_test') + ])), + [], + 'seaslog_test', + [['task_id', 'string'], ['worker_id', 'string']]//自定义模板添加的处理字段,顺序需要按照日志记录中的template数组一致 + ) ], [ 'appName' => 'Seaslog',//应用名:远程发送日志的时候用于区分是哪个应用发送来的 'bufferSize' => 1,//定量:buffer>=时会输出,默认为1,每次记录都会输出 @@ -58,23 +62,27 @@ '%c' => [ $possibleStyles[rand(0, count($possibleStyles) - 1)], $htmlColors[rand(0, count($htmlColors) - 1)] - ] + ], ]); } else { $requestVar = array_filter([ '%Q' => uniqid(), '%c' => [ - $possibleStyles[rand(0, count($possibleStyles) - 1)], - $htmlColors[rand(0, count($htmlColors) - 1)] + 'console' => $possibleStyles[rand(0, count($possibleStyles) - 1)], + 'websocket' => $htmlColors[rand(0, count($htmlColors) - 1)] ] ]); } + $requestVar['%A'] = ['123', '456']; Context::set(Logger::CONTEXT_KEY, $requestVar); } return $requestVar; }); - //这里区别于标准PSR-3,Context占用一个固定key(module),作用与Seaslog的Logger参数一样,默认值为System - for ($i = 0; $i < 100; $i++) { - $logger->info("test logger $i", ['module' => 'logger']); + /* + * 这里区别于标准PSR-3,Context占用两个固定key(module),作用与Seaslog的Logger参数一样,默认值为System + * template为用户自定义模板对应的填充值,默认为[],不填充 + */ + for ($i = 0; $i < 1; $i++) { + $logger->info("test logger $i", ['module' => 'logger', 'template' => ['abc', 'def']]); } }); \ No newline at end of file diff --git a/src/AbstractConfig.php b/src/AbstractConfig.php index fd1b63f..eda3d41 100644 --- a/src/AbstractConfig.php +++ b/src/AbstractConfig.php @@ -13,6 +13,15 @@ */ abstract class AbstractConfig { + const TYPE_JSON = 'json'; + const TYPE_FIELD = 'field'; + /** @var array */ + protected static $supportField = [ + self::TYPE_JSON, + self::TYPE_FIELD + ]; + /** @var string */ + protected $split = ' | '; /** @var int */ protected $bufferSize = 1; /** @var AbstractTarget[] */ @@ -25,6 +34,10 @@ abstract class AbstractConfig protected $userTemplate; /** @var string */ protected $appName = 'Seaslog'; + /** @var array */ + protected $template; + /** @var string */ + protected $customerType = self::TYPE_FIELD; /** * AbstractConfig constructor. @@ -33,16 +46,19 @@ abstract class AbstractConfig */ public function __construct(array $target, array $configs = []) { + foreach ($configs as $name => $value) { + if (property_exists($this, $name) && $name !== 'targetList') { + $this->$name = $value; + } + } $this->targetList = $target; if (empty($this->targetList)) { $this->targetList = [ 'echo' => new StyleTarget() ]; } - foreach ($configs as $name => $value) { - if (property_exists($this, $name) && $name !== 'targetList') { - $this->$name = $value; - } + foreach ($this->targetList as $target) { + $target->setTemplate($this->template)->setCustomerFieldType($this->customerType)->setSplit($this->split); } register_shutdown_function(function () { $this->flush(true); @@ -55,6 +71,14 @@ public function __construct(array $target, array $configs = []) */ abstract public function flush(bool $flush = false): void; + /** + * @return array + */ + public static function getSupportFieldType(): array + { + return static::$supportField; + } + /** * @param callable $userTemplate */ diff --git a/src/ConsoleColor.php b/src/ConsoleColor.php index 4493881..a89c799 100644 --- a/src/ConsoleColor.php +++ b/src/ConsoleColor.php @@ -124,6 +124,15 @@ public function isStyleForced(): bool return $this->forceStyle; } + /** + * @param string $style + * @return bool + */ + private function isValidStyle(string $style): bool + { + return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); + } + /** * @param string $style * @return string @@ -154,15 +163,6 @@ public function are256ColorsSupported(): bool } } - /** - * @param string $style - * @return bool - */ - private function isValidStyle(string $style): bool - { - return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); - } - /** * @param string|int $value * @return string diff --git a/src/HtmlColor.php b/src/HtmlColor.php index e691291..1077925 100644 --- a/src/HtmlColor.php +++ b/src/HtmlColor.php @@ -167,4 +167,12 @@ public static function getPossibleColors(): array { return array_keys(self::$colors); } + + /** + * @return array + */ + public static function getPossibleColorsRGB(): array + { + return array_values(self::$colors); + } } \ No newline at end of file diff --git a/src/LoggerConfig.php b/src/LoggerConfig.php index 353aa49..9a74495 100644 --- a/src/LoggerConfig.php +++ b/src/LoggerConfig.php @@ -34,12 +34,9 @@ class LoggerConfig extends AbstractConfig '%F', '%U', '%u', - '%C' + '%C', + '%A' ]; - /** @var array */ - protected $template; - /** @var string */ - protected $split = ' | '; /** @var int */ protected $isMicroTime = 3; /** @var bool */ @@ -54,15 +51,15 @@ class LoggerConfig extends AbstractConfig public function __construct( array $target, array $configs = [], - array $template = ['%T', '%L', '%R', '%m', '%I', '%Q', '%F', '%U', '%M'] + array $template = ['%T', '%L', '%R', '%m', '%I', '%Q', '%F', '%U', '%A', '%M'] ) { - parent::__construct($target, $configs); foreach ($template as $tmp) { if (!in_array($tmp, self::$supportTemplate)) { throw new InvalidArgumentException("$tmp not supported!"); } } $this->template = $template; + parent::__construct($target, $configs); } /** @@ -175,6 +172,19 @@ public function log(string $level, string $message, array $context = []): void case '%u': $msg[] = memory_get_peak_usage(); break; + case '%A': + $customerTemplate = ArrayHelper::getValue($context, 'template', + []) ?? ArrayHelper::getValue($template, $tmp, + []); + switch ($this->customerType) { + case AbstractConfig::TYPE_JSON: + $msg[] = json_encode($customerTemplate, JSON_UNESCAPED_UNICODE); + break; + case AbstractConfig::TYPE_FIELD: + default: + $msg[] = implode($this->split, $customerTemplate); + } + break; } } $color = ArrayHelper::getValue($template, '%c'); diff --git a/src/SeaslogConfig.php b/src/SeaslogConfig.php index 71c9cf3..15da51c 100644 --- a/src/SeaslogConfig.php +++ b/src/SeaslogConfig.php @@ -19,8 +19,9 @@ class SeaslogConfig extends AbstractConfig */ public function __construct(array $target, array $configs = []) { - parent::__construct($target, $configs); + $this->template = explode($this->split, ini_get('seaslog.default_template')); ini_set('seaslog.recall_depth', (string)$this->recall_depth); + parent::__construct($target, $configs); } /** @@ -45,6 +46,17 @@ public function log(string $level, string $message, array $context = []): void ]) as $key => $value) { Seaslog::setRequestVariable($key, $value); } + if (!empty($template = ArrayHelper::remove($context, 'template', []) ?? ArrayHelper::remove($template, '%A', + []))) { + switch ($this->customerType) { + case AbstractConfig::TYPE_JSON: + $template = json_encode($template, JSON_UNESCAPED_UNICODE); + break; + case AbstractConfig::TYPE_FIELD: + $template = implode($this->split, $template); + } + $message = $template . $this->split . $message; + } Seaslog::$level($message); $this->flush(); } diff --git a/src/Targets/AbstractTarget.php b/src/Targets/AbstractTarget.php index 7d9880d..7fee071 100644 --- a/src/Targets/AbstractTarget.php +++ b/src/Targets/AbstractTarget.php @@ -3,6 +3,9 @@ namespace Seasx\SeasLogger\Targets; +use Seasx\SeasLogger\AbstractConfig; +use Seasx\SeasLogger\Exceptions\NotSupportedException; + /** * Class AbstractTarget * @package Seasx\SeasLogger\Targets @@ -15,16 +18,54 @@ abstract class AbstractTarget protected $levelList = []; /** @var int */ protected $levelIndex = 1; + /** @var string */ + protected $customerType; + /** @var array */ + protected $template = []; /** * AbstractTarget constructor. * @param array $levelList + */ + public function __construct(array $levelList = []) + { + $this->levelList = $levelList; + } + + /** * @param string $split + * @return AbstractTarget */ - public function __construct(array $levelList = [], string $split = ' | ') + public function setSplit(string $split): self { $this->split = $split; - $this->levelList = $levelList; + return $this; + } + + /** + * @param array $template + * @return AbstractTarget + */ + public function setTemplate(array $template): self + { + $this->template = $template; + return $this; + } + + /** + * @param string $type + * @return AbstractTarget + */ + public function setCustomerFieldType(string $type): self + { + if (!in_array($type, AbstractConfig::getSupportFieldType())) { + throw new NotSupportedException("The field type not support $type"); + } + if ($this->customerType === null) { + $this->customerType = $type; + } + + return $this; } /** diff --git a/src/Targets/EchoTarget.php b/src/Targets/EchoTarget.php index 0e0c479..8ef38e2 100644 --- a/src/Targets/EchoTarget.php +++ b/src/Targets/EchoTarget.php @@ -3,6 +3,8 @@ namespace Seasx\SeasLogger\Targets; +use Seasx\SeasLogger\ArrayHelper; + /** * Class EchoTarget * @package Seasx\SeasLogger\Targets @@ -22,6 +24,7 @@ public function export(array $messages): void if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { continue; } + ArrayHelper::remove($msg, '%c'); echo implode($this->split, $msg) . PHP_EOL; } } diff --git a/src/Targets/KafkaTarget.php b/src/Targets/KafkaTarget.php index 28ad512..d5f53ea 100644 --- a/src/Targets/KafkaTarget.php +++ b/src/Targets/KafkaTarget.php @@ -5,6 +5,7 @@ use Exception; +use Seasx\SeasLogger\AbstractConfig; use Seasx\SeasLogger\ArrayHelper; use Seasx\SeasLogger\Kafka\Producter; @@ -16,23 +17,27 @@ class KafkaTarget extends AbstractTarget { /** @var Producter */ private $client; - /** @var array */ - private $template = []; /** @var string */ private $topic; + /** @var array */ + private $customerTmp = []; + /** @var array */ + private $fieldTemplate = []; /** * KafkaTarget constructor. * @param Producter $client - * @param string $topic * @param array $levelList - * @param array $template + * @param string $topic + * @param array $customerTmp + * @param array $fieldTemplate */ public function __construct( Producter $client, array $levelList = [], string $topic = 'seaslog', - array $template = [ + array $customerTmp = [], + array $fieldTemplate = [ ['datetime', 'timespan'], ['level', 'string'], ['request_uri', 'string'], @@ -46,7 +51,8 @@ public function __construct( ) { $this->client = $client; $this->topic = $topic; - $this->template = $template; + $this->fieldTemplate = $fieldTemplate; + $this->customerTmp = $customerTmp; $this->levelList = $levelList; } @@ -79,20 +85,24 @@ public function export(array $messages): void $log = [ 'appname' => $module, ]; - foreach ($msg as $index => $value) { - [$name, $type] = $this->template[$index]; - switch ($type) { - case "string": - $log[$name] = trim($value); - break; - case "timespan": - $log[$name] = strtotime(explode('.', $value)[0]); - break; - case "int": - $log[$name] = (int)$value; - break; - default: - $log[$name] = trim($value); + $i = 0; + foreach ($msg as $index => $msgValue) { + if ($this->template[$index] === '%A') { + switch ($this->customerType) { + case AbstractConfig::TYPE_JSON: + $msgValue = json_decode($msgValue, true); + break; + case AbstractConfig::TYPE_FIELD: + default: + $msgValue = explode($this->split, $msgValue); + } + foreach ($this->customerTmp as $tmpIndex => [$name, $type]) { + $this->makeLog($log, $name, $type, isset($msgValue[$tmpIndex]) ? $msgValue[$tmpIndex] : ''); + } + } else { + [$name, $type] = $this->fieldTemplate[$i]; + $this->makeLog($log, $name, $type, $msgValue); + $i++; } } $this->client->send([ @@ -105,4 +115,27 @@ public function export(array $messages): void } } } + + /** + * @param array $log + * @param string $name + * @param string $type + * @param $value + */ + private function makeLog(array &$log, string $name, string $type, $value): void + { + switch ($type) { + case "timespan": + $log[$name] = $value ? strtotime(explode('.', $value)[0]) : 0; + break; + case "int": + $log[$name] = $value ? (int)$value : 0; + break; + case "string": + $log[$name] = $value ? trim($value) : ''; + break; + default: + $log[$type][$name] = $value; + } + } } \ No newline at end of file diff --git a/src/Targets/SeasStashTarget.php b/src/Targets/SeasStashTarget.php index eff94ea..495e719 100644 --- a/src/Targets/SeasStashTarget.php +++ b/src/Targets/SeasStashTarget.php @@ -48,6 +48,9 @@ public function export(array $messages): void $module = substr($fileName, 0, strpos($fileName, '_', -1)); } $msg = explode($this->split, trim($msg)); + if (($diff = count($msg) - count($this->template)) > 0) { + array_splice($msg, -($diff + 1), $diff, [array_slice($msg, -($diff + 1), $diff)]); + } } if (!empty($this->levelList) && !in_array(strtolower($msg[$this->levelIndex]), $this->levelList)) { continue; diff --git a/src/Targets/StyleTarget.php b/src/Targets/StyleTarget.php index c187c12..a7ef6f1 100644 --- a/src/Targets/StyleTarget.php +++ b/src/Targets/StyleTarget.php @@ -72,32 +72,32 @@ public function export(array $messages): void } if (empty($ranColor)) { $ranColor = $this->default; - } elseif (is_array($ranColor) && count($ranColor) === 2) { - $ranColor = $ranColor[0]; + } elseif (is_array($ranColor) && isset($ranColor['console'])) { + $ranColor = $ranColor['console']; } else { $ranColor = $this->default; } $context = []; - foreach ($msg as $index => $m) { + foreach ($msg as $index => $msgValue) { + $level = $this->getLevelColor(trim($msg[$this->levelIndex])); if (isset($this->colorTemplate[$index])) { $color = $this->colorTemplate[$index]; - $m = is_string($m) ? trim($m) : (string)$m; - $level = trim($msg[$this->levelIndex]); + $msgValue = is_string($msgValue) ? trim($msgValue) : (string)$msgValue; switch ($color) { case self::COLOR_LEVEL: - $context[] = $this->color->apply($this->getLevelColor($level), $m); + $context[] = $this->color->apply($level, $msgValue); break; case self::COLOR_DEFAULT: - $context[] = $this->color->apply($this->default, $m); + $context[] = $this->color->apply($this->default, $msgValue); break; case self::COLOR_RANDOM: - $context[] = $this->color->apply($ranColor, $m); + $context[] = $this->color->apply($ranColor, $msgValue); break; default: - $context[] = $this->color->apply($color, $m); + $context[] = $this->color->apply($color, $msgValue); } } else { - $context[] = $this->color->apply($this->default, $m); + $context[] = $this->color->apply($level, $msgValue); } } if (isset($context)) { diff --git a/src/Targets/WebsocketTarget.php b/src/Targets/WebsocketTarget.php index 41b1832..7546653 100644 --- a/src/Targets/WebsocketTarget.php +++ b/src/Targets/WebsocketTarget.php @@ -29,10 +29,21 @@ class WebsocketTarget extends AbstractTarget self::COLOR_RANDOM, self::COLOR_LEVEL, 'DarkGray', + self::COLOR_LEVEL, self::COLOR_LEVEL ]; /** @var string */ private $default = 'LightGray'; + /** @var callable */ + private $getServer; + + /** + * @param callable $function + */ + public function setGetServer(callable $function): void + { + $this->getServer = $function; + } /** * @param array $messages @@ -40,9 +51,12 @@ class WebsocketTarget extends AbstractTarget */ public function export(array $messages): void { + if (!is_callable($this->getServer)) { + return; + } /** @var Server $server */ - $swooleServer = getServer(); - if (!$swooleServer) { + $swooleServer = call_user_func($this->getServer); + if (!$swooleServer || !$swooleServer instanceof Server) { return; } foreach ($swooleServer->connections as $fd) { @@ -67,19 +81,19 @@ public function export(array $messages): void } if (empty($ranColor)) { $ranColor = $this->default; - } elseif (is_array($ranColor) && count($ranColor) === 2) { - $ranColor = $ranColor[1]; + } elseif (is_array($ranColor) && isset($ranColor['websocket'])) { + $ranColor = $ranColor['websocket']; } else { $ranColor = $this->default; } - foreach ($msg as $index => $m) { - $msg[$index] = trim($m); + foreach ($msg as $index => $msgValue) { + $msg[$index] = is_string($msgValue) ? trim($msgValue) : (string)$msgValue; + $level = $this->getLevelColor(trim($msg[$this->levelIndex])); if (isset($this->colorTemplate[$index])) { $color = $this->colorTemplate[$index]; - $level = trim($msg[$this->levelIndex]); switch ($color) { case self::COLOR_LEVEL: - $colors[] = HtmlColor::getColor($this->getLevelColor($level)); + $colors[] = HtmlColor::getColor($level); break; case self::COLOR_RANDOM: $colors[] = HtmlColor::getColor($ranColor); @@ -91,7 +105,7 @@ public function export(array $messages): void $colors[] = HtmlColor::getColor($color); } } else { - $colors[] = $this->default; + $colors[] = $level; } } $msg = json_encode([$msg, $colors], JSON_UNESCAPED_UNICODE); @@ -112,15 +126,15 @@ private function getLevelColor(string $level): string { switch (strtolower($level)) { case LogLevel::INFO: - return "green"; + return "Green"; case LogLevel::DEBUG: - return 'dark_gray'; + return 'DarkGray'; case LogLevel::ERROR: - return "red"; + return "Red"; case LogLevel::WARNING: - return 'yellow'; + return 'Yellow'; default: - return 'light_red'; + return 'DarkRed'; } } diff --git a/tests/ArrayHelperTest.php b/tests/ArrayHelperTest.php index ee18951..2517324 100644 --- a/tests/ArrayHelperTest.php +++ b/tests/ArrayHelperTest.php @@ -1,5 +1,5 @@ assertEquals('[green test]', $color->apply('green', '[green test]')); } - public function testSetForceStyle(){ + public function testSetForceStyle() + { $color = new ConsoleColor(); $color->setForceStyle(true); $this->assertTrue($color->isStyleForced()); diff --git a/tests/ContestTest.php b/tests/ContextTest.php similarity index 80% rename from tests/ContestTest.php rename to tests/ContextTest.php index 95a993f..fb2ca6d 100644 --- a/tests/ContestTest.php +++ b/tests/ContextTest.php @@ -1,16 +1,16 @@ assertEquals('value', Context::get('key')); }); @@ -18,7 +18,7 @@ public function testGet() public function testHas() { - go(function () { + \Co\Run(function () { Context::set('key', 'value'); $this->assertTrue(Context::has('key')); }); @@ -26,14 +26,14 @@ public function testHas() public function testNotHas() { - go(function () { + \Co\Run(function () { $this->assertFalse(Context::has('key')); }); } public function testDelete() { - go(function () { + \Co\Run(function () { Context::set('key', 'value'); $this->assertEquals('value', Context::get('key')); Context::delete('key'); diff --git a/tests/EchoTargetTest.php b/tests/EchoTargetTest.php new file mode 100644 index 0000000..0aed724 --- /dev/null +++ b/tests/EchoTargetTest.php @@ -0,0 +1,131 @@ +assertInstanceOf(EchoTarget::class, $target); + $target->export([ + 'logger' => [ + implode(' | ', [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[SeasLog String]' + ]) + ] + ]); + } + + public function testExport() + { + $target = new EchoTarget(); + $this->assertInstanceOf(EchoTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithCustomerTemplate() + { + $target = new EchoTarget(); + $this->assertInstanceOf(EchoTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + implode(' | ', ['123']), + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithCustomerTypeJson() + { + $target = new EchoTarget(); + $target->setCustomerFieldType(AbstractConfig::TYPE_JSON); + $this->assertInstanceOf(EchoTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + json_encode(['task_id' => '123']), + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithLevel() + { + $target = new EchoTarget([ + 'info' + ]); + $this->assertInstanceOf(EchoTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[Test WARNING]' + ], + [ + '2019-08-30 09:58:01.937', + 'INFO', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[Test INFO]' + ] + ] + ]); + } +} \ No newline at end of file diff --git a/tests/HtmlColorTest.php b/tests/HtmlColorTest.php new file mode 100644 index 0000000..55d1d97 --- /dev/null +++ b/tests/HtmlColorTest.php @@ -0,0 +1,23 @@ +assertEquals(count(HtmlColor::getPossibleColors()), count(HtmlColor::getPossibleColorsRGB())); + } + + public function testGetColor() + { + $keys = HtmlColor::getPossibleColors(); + $values = HtmlColor::getPossibleColorsRGB(); + $index = rand(0, count($keys)); + $this->assertEquals(HtmlColor::getColor($keys[$index]), $values[$index]); + } +} \ No newline at end of file diff --git a/tests/LoggerTest.php b/tests/LoggerTest.php index ab2e7f4..e9c758b 100644 --- a/tests/LoggerTest.php +++ b/tests/LoggerTest.php @@ -1,5 +1,5 @@ init(); + + $basePath = $logger->getBasePath(); + $this->assertEquals('/tmp/seaslogger', $basePath); + } + /** * @return Logger */ @@ -26,14 +34,6 @@ public function init() return $logger; } - public function testGetBasePath() - { - $logger = $this->init(); - - $basePath = $logger->getBasePath(); - $this->assertEquals('/tmp/seaslogger', $basePath); - } - public function testSetRequestID() { $logger = $this->init(); diff --git a/tests/NotSupportedExceptionTest.php b/tests/NotSupportedExceptionTest.php new file mode 100644 index 0000000..0ff5893 --- /dev/null +++ b/tests/NotSupportedExceptionTest.php @@ -0,0 +1,29 @@ + | + +----------------------------------------------------------------------+ + */ + +namespace Seasx\SeasLogger\Tests; + +use Swoole; +use swoole_atomic; + +class ProcessManager +{ + public $parentFunc; + public $childFunc; + /** + * @var swoole_atomic + */ + protected $atomic; + /** + * wait wakeup 1s default + */ + protected $waitTimeout = 1.0; + protected $childPid; + protected $childStatus = 255; + protected $parentFirst = false; + /** + * @var Swoole\Process + */ + protected $childProcess; + + public function __construct() + { + $this->atomic = new Swoole\Atomic(0); + } + + //等待信息 + + public function wakeup() + { + return $this->atomic->wakeup(); + } + + //唤醒等待的进程 + + public function run($redirectStdout = false) + { + $this->childProcess = new Swoole\Process(function () { + if ($this->parentFirst) { + $this->wait(); + } + $this->runChildFunc(); + exit; + }, $redirectStdout, $redirectStdout); + if (!$this->childProcess || !$this->childProcess->start()) { + exit("ERROR: CAN NOT CREATE PROCESS\n"); + } + register_shutdown_function(function () { + $this->kill(); + }); + if (!$this->parentFirst) { + $this->wait(); + } + $this->runParentFunc($this->childPid = $this->childProcess->pid); + Swoole\Event::wait(); + $waitInfo = Swoole\Process::wait(true); + $this->childStatus = $waitInfo['code']; + return true; + } + + public function wait() + { + return $this->atomic->wait($this->waitTimeout); + } + + public function runChildFunc() + { + return call_user_func($this->childFunc); + } + + /** + * Kill Child Process + * @param bool $force + */ + public function kill(bool $force = false) + { + if (!defined('PCNTL_ESRCH')) { + define('PCNTL_ESRCH', 3); + } + if ($this->childPid) { + if ($force || (!@Swoole\Process::kill($this->childPid) && swoole_errno() !== PCNTL_ESRCH)) { + if (!@Swoole\Process::kill($this->childPid, SIGKILL) && swoole_errno() !== PCNTL_ESRCH) { + exit('KILL CHILD PROCESS ERROR'); + } + } + } + } + + public function runParentFunc($pid = 0) + { + if (!$this->parentFunc) { + return (function () { + $this->kill(); + })(); + } else { + return call_user_func($this->parentFunc, $pid); + } + } + + public function getChildOutput() + { + $this->childProcess->setBlocking(false); + while (1) { + $data = @$this->childProcess->read(); + if (!$data) { + sleep(1); + } else { + return $data; + } + } + } + + /** + * @param $data + */ + public function setChildOutput($data) + { + $this->childProcess->write($data); + } +} \ No newline at end of file diff --git a/tests/SeasStashTargetTest.php b/tests/SeasStashTargetTest.php new file mode 100644 index 0000000..dfb637a --- /dev/null +++ b/tests/SeasStashTargetTest.php @@ -0,0 +1,132 @@ + [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + implode(' | ', ['123', '456']), + '[SeasLog Array]' + ] + ] + ]; + $target = new SeasStashTarget(new Pool([ + 'uri' => '127.0.0.1:9501' + ])); + $this->assertInstanceOf(SeasStashTarget::class, $target); + + $pm->parentFunc = function () use ($log, $pm, $target) { + $str = $pm->getChildOutput(); + $src = []; + foreach (current($log['logger']) as $item) { + if (is_array($item)) { + $item = implode(' | ', $item); + } + $src[] = $item; + } + $this->assertEquals(trim($str), trim('logger@' . implode(' | ', $src))); + $pm->kill(); + }; + $pm->childFunc = function () use ($target, $pm, $log) { + $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_eof_check' => true, + 'package_eof' => PHP_EOL, + 'pid_file' => '/dev/shm/tcp.pid' + ]); + $server->on('workerstart', function (Server $server, int $worker_id) use ($target, $pm, $log) { + $pm->wakeup(); + $target->export($log); + }); + $server->on('receive', + function (Swoole\Server $server, int $fd, int $reactor_id, string $data) use ($pm) { + $pm->setChildOutput($data); + $server->shutdown(); + }); + $server->start(); + }; + $pm->run(true); + } + + public function testExportWithJsonType() + { + $pm = new ProcessManager(); + $log = [ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + json_encode(['task_id' => '123', 'id' => 'abc']), + '[SeasLog Array]' + ] + ] + ]; + $target = new SeasStashTarget(new Pool([ + 'uri' => '127.0.0.1:9501' + ])); + $target->setCustomerFieldType(AbstractConfig::TYPE_JSON); + $this->assertInstanceOf(SeasStashTarget::class, $target); + + $pm->parentFunc = function () use ($log, $pm, $target) { + $str = $pm->getChildOutput(); + $src = []; + foreach (current($log['logger']) as $item) { + if (is_array($item)) { + $item = json_encode($item); + } + $src[] = $item; + } + $this->assertEquals(trim($str), trim('logger@' . implode(' | ', $src))); + $pm->kill(); + }; + $pm->childFunc = function () use ($target, $pm, $log) { + $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'open_eof_check' => true, + 'package_eof' => PHP_EOL, + 'pid_file' => '/dev/shm/tcp.pid' + ]); + $server->on('workerstart', function (Server $server, int $worker_id) use ($target, $pm, $log) { + $pm->wakeup(); + $target->export($log); + }); + $server->on('receive', + function (Swoole\Server $server, int $fd, int $reactor_id, string $data) use ($pm) { + $pm->setChildOutput($data); + $server->shutdown(); + }); + $server->start(); + }; + $pm->run(true); + } +} \ No newline at end of file diff --git a/tests/StyleTargetTest.php b/tests/StyleTargetTest.php index fee31d8..e375e5d 100644 --- a/tests/StyleTargetTest.php +++ b/tests/StyleTargetTest.php @@ -1,13 +1,29 @@ assertInstanceOf(AbstractTarget::class, $target->setCustomerFieldType(AbstractConfig::TYPE_JSON)); + } + + public function testSetNotSupportCustomerFieldType() + { + $target = new StyleTarget(); + $this->expectException(NotSupportedException::class); + $target->setCustomerFieldType('test'); + } + public function testStringExport() { $target = new StyleTarget(); @@ -18,8 +34,8 @@ public function testStringExport() '2019-08-30 09:58:01.937', 'WARNING', 'vendor/phpunit/phpunit/phpunit', - 'UNKNOW', - '127.0.0.1', + '/bin/bash', + 'local', '5d6882a9e38ff', 'Logger.php:453', 1380624, @@ -39,11 +55,56 @@ public function testExport() '2019-08-30 09:58:01.937', 'WARNING', 'vendor/phpunit/phpunit/phpunit', - 'UNKNOW', - '127.0.0.1', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithCustomerTemplate() + { + $target = new StyleTarget(); + $this->assertInstanceOf(StyleTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + implode(' | ', ['123']), + '[SeasLog Array]' + ] + ] + ]); + } + + public function testExportWithCustomerTypeJson() + { + $target = new StyleTarget(); + $target->setCustomerFieldType(AbstractConfig::TYPE_JSON); + $this->assertInstanceOf(StyleTarget::class, $target); + $target->export([ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', '5d6882a9e38ff', 'Logger.php:453', 1380624, + json_encode(['task_id' => '123']), '[SeasLog Array]' ] ] @@ -62,8 +123,8 @@ public function testExportWithLevel() '2019-08-30 09:58:01.937', 'WARNING', 'vendor/phpunit/phpunit/phpunit', - 'UNKNOW', - '127.0.0.1', + '/bin/bash', + 'local', '5d6882a9e38ff', 'Logger.php:453', 1380624, @@ -73,8 +134,8 @@ public function testExportWithLevel() '2019-08-30 09:58:01.937', 'INFO', 'vendor/phpunit/phpunit/phpunit', - 'UNKNOW', - '127.0.0.1', + '/bin/bash', + 'local', '5d6882a9e38ff', 'Logger.php:453', 1380624, diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 2c1740e..5804292 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -3,6 +3,7 @@ namespace Seasx\SeasLogger\Tests; +use Seasx\SeasLogger\AbstractConfig; use Seasx\SeasLogger\Exceptions\NotSupportedException; use Seasx\SeasLogger\Logger; use Seasx\SeasLogger\LoggerConfig; @@ -11,6 +12,20 @@ class SwooleLoggerTest extends TestCase { + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ + public function testGetBasePath() + { + $logger = $this->init(); + $logger->getBasePath(); + +// $logger = $this->init(1); +// $logger->setBasePath('/tmp/seaslogger'); +// $basePath = $logger->getBasePath(); +// $this->assertEquals('/tmp/seaslogger', $basePath); + } + /** * @param int $type * @return Logger @@ -33,21 +48,9 @@ public function init(int $type = 0) /** * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException */ - public function testGetBasePath() - { - $logger = $this->init(); - $logger->getBasePath(); - -// $logger = $this->init(1); -// $logger->setBasePath('/tmp/seaslogger'); -// $basePath = $logger->getBasePath(); -// $this->assertEquals('/tmp/seaslogger', $basePath); - } - public function testSetRequestID() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::setRequestID(1024); // $logger = $this->init(1); @@ -55,10 +58,12 @@ public function testSetRequestID() // $this->assertTrue($result); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testGetRequestID() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::getRequestID(); // $logger = $this->init(1); @@ -70,89 +75,205 @@ public function testGetRequestID() public function testEmergency() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->emergency('[SeasLog Test]', ['level' => 'emergency']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->emergency('[SeasLog Test]', ['level' => 'emergency']); + }); } public function testAlert() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->alert('[SeasLog Test]', ['level' => 'alert']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->alert('[SeasLog Test]', ['level' => 'alert']); + }); } public function testCritical() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->critical('[SeasLog Test]', ['level' => 'critical']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->critical('[SeasLog Test]', ['level' => 'critical']); + }); } public function testError() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->error('[SeasLog Test]', ['level' => 'error']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->error('[LoggerConfig Test]', ['level' => 'error']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->error('[SeasLog Test]', ['level' => 'error']); + }); } public function testWarning() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->warning('[SeasLog Test]', ['level' => 'warning']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->warning('[SeasLog Test]', ['level' => 'warning']); + }); } public function testNotice() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->notice('[SeasLog Test]', ['level' => 'notice']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->notice('[SeasLog Test]', ['level' => 'notice']); + }); } public function testInfo() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->info('[SeasLog Test]', ['level' => 'info']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[LoggerConfig Test]', ['level' => 'info']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->info('[SeasLog Test]', ['level' => 'info']); + }); } public function testDebug() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->debug('[SeasLog Test]', ['level' => 'debug']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->debug('[SeasLog Test]', ['level' => 'debug']); + }); } public function testLog() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'log']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'log']); + }); + } + + public function testLogWithFieldTemplate() + { + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->log(Logger::INFO, '[SeasLog FieldTemplate]', ['level' => 'log', 'template' => ['test']]); + }); + } + + public function testLogWithJsonTemplate() + { + \Co\run(function () { + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'customerType' => AbstractConfig::TYPE_JSON, + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); + $this->assertInstanceOf(Logger::class, $logger); + $logger->log(Logger::INFO, '[LoggerConfig JsonTemplate]', + ['level' => 'log', 'template' => ['task_type' => 'test']]); + +// $logger = new Logger( +// new SeaslogConfig([ +// 'echo' => new StyleTarget() +// ], [ +// 'appName' => 'Seaslog', +// 'customerType' => AbstractConfig::TYPE_JSON, +// 'bufferSize' => 1, +// 'tick' => 0, +// 'recall_depth' => 2, +// ])); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->log(Logger::INFO, '[SeasLog JsonTemplate]', ['level' => 'log', 'template' => ['task_type' => 'test']]); + }); } public function testRequestLevel() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->setRequestLevel(Logger::ALL); - $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); - $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); - $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); - $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); - $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); - $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); - $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); - $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); - - $logger->log(0, '[SeasLog Test]', ['level' => 'default']); - $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + \Co\run(function () { + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $logger->setRequestLevel(Logger::ALL); + $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[LoggerConfig Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[LoggerConfig Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[LoggerConfig Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[LoggerConfig Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[LoggerConfig Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[LoggerConfig Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[LoggerConfig Test]', ['level' => 'ALERT']); + $logger->log(0, '[LoggerConfig Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[LoggerConfig Test]', ['level' => 'default']); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->setRequestLevel(Logger::ALL); +// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); +// $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); +// $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); +// $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); +// $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); +// $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); +// $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); +// $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); +// $logger->log(0, '[SeasLog Test]', ['level' => 'default']); +// $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + }); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testSetLogger() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::setLogger('seas'); // $logger = $this->init(1); @@ -160,10 +281,12 @@ public function testSetLogger() // $this->assertTrue($result); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testGetLastLogger() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::getLastLogger(); // $logger = $this->init(1); @@ -189,10 +312,12 @@ public function testGetDatetimeFormat() $this->assertEquals('Y-m-d H:i:s', $format); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testAnalyzerCount() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::analyzerCount(); // $logger = $this->init(1); @@ -200,10 +325,12 @@ public function testAnalyzerCount() // $this->assertNotNull($result); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testAnalyzerDetail() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::analyzerDetail(); // $logger = $this->init(1); @@ -226,10 +353,12 @@ public function testGetBuffer() // $this->assertNotNull($buffer); } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testFlushBuffer() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::flushBuffer(); // $logger = $this->init(1); @@ -268,10 +397,12 @@ public function testInvoke() // $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); // } + /** + * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException + */ public function testCloseLoggerStream() { $logger = $this->init(); - $this->expectException(NotSupportedException::class); $logger::closeLoggerStream(); // $logger = $this->init(1); diff --git a/tests/TestCase.php b/tests/TestCase.php index 2d75cc8..a185b95 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,7 +9,7 @@ * with this source code in the file LICENSE. */ -namespace SeasX\SeasLogger\Tests; +namespace Seasx\SeasLogger\Tests; use PHPUnit\Framework\TestCase as PHPUnitTestCase; diff --git a/tests/WebSocketTargetTest.php b/tests/WebSocketTargetTest.php new file mode 100644 index 0000000..9890390 --- /dev/null +++ b/tests/WebSocketTargetTest.php @@ -0,0 +1,149 @@ + [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + '123', + '[SeasLog Array]' + ] + ] + ]; + $target = new WebsocketTarget(); + $this->assertInstanceOf(WebsocketTarget::class, $target); + + $pm->parentFunc = function ($pid) use ($pm, $log) { + \Co\Run(function () use ($log) { + $cli = new Client('127.0.0.1', 9501); + $cli->set(['timeout' => -1]); + while (!@$cli->upgrade('/')) { + Co::sleep(0.1); + } + $frame = $cli->recv(); + $data = json_decode($frame->data, true); + [$msg, $colors] = $data; + $this->assertEquals(count($msg), count($colors)); + $this->assertEquals($msg[8], '123'); + $inColor = true; + foreach ($colors as $color) { + if (!in_array($color, HtmlColor::getPossibleColorsRGB())) { + $inColor = false; + break; + } + } + $this->assertTrue($inColor); + }); + $pm->kill(); + }; + $pm->childFunc = function () use ($pm, $target, $log) { + $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'pid_file' => '/dev/shm/ws.pid' + ]); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('message', function (Server $server, Frame $frame) { + }); + $server->on('open', + function (Swoole\WebSocket\Server $server, Swoole\Http\Request $request) use ($target, $log) { + $target->setGetServer(function () use ($server) { + return $server; + }); + $target->export($log); + $server->shutdown(); + }); + $server->start(); + }; + $pm->run(); + } + + public function testExportWithJsonType() + { + $pm = new ProcessManager(); + $log = [ + 'logger' => [ + [ + '2019-08-30 09:58:01.937', + 'WARNING', + 'vendor/phpunit/phpunit/phpunit', + '/bin/bash', + 'local', + '5d6882a9e38ff', + 'Logger.php:453', + 1380624, + json_encode(['task_id' => '123']), + '[SeasLog Array]' + ] + ] + ]; + $target = new WebsocketTarget(); + $this->assertInstanceOf(WebsocketTarget::class, $target); + + $pm->parentFunc = function ($pid) use ($pm, $log) { + \Co\Run(function () use ($log) { + $cli = new Client('127.0.0.1', 9501); + $cli->set(['timeout' => -1]); + while (!@$cli->upgrade('/')) { + Co::sleep(0.1); + } + $frame = $cli->recv(); + $data = json_decode($frame->data, true); + [$msg, $color] = $data; + $this->assertEquals(count($msg), count($color)); + $this->assertEquals($msg[8], json_encode(['task_id' => '123'])); + }); + $pm->kill(); + }; + $pm->childFunc = function () use ($pm, $target, $log) { + $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); + $server->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'pid_file' => '/dev/shm/ws.pid' + ]); + $server->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $server->on('message', function (Server $server, Frame $frame) { + }); + $server->on('open', + function (Swoole\WebSocket\Server $server, Swoole\Http\Request $request) use ($target, $log) { + $target->setGetServer(function () use ($server) { + return $server; + }); + $target->setCustomerFieldType(AbstractConfig::TYPE_JSON); + $target->export($log); + $server->shutdown(); + }); + $server->start(); + }; + $pm->run(); + } +} \ No newline at end of file From 624d34e9b297bdd25a7c2bb4777acb813bc0e009 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 10 Sep 2019 16:49:41 +0800 Subject: [PATCH 16/27] remove websockettarget test --- tests/WebSocketTargetTest.php | 149 ---------------------------------- 1 file changed, 149 deletions(-) delete mode 100644 tests/WebSocketTargetTest.php diff --git a/tests/WebSocketTargetTest.php b/tests/WebSocketTargetTest.php deleted file mode 100644 index 9890390..0000000 --- a/tests/WebSocketTargetTest.php +++ /dev/null @@ -1,149 +0,0 @@ - [ - [ - '2019-08-30 09:58:01.937', - 'WARNING', - 'vendor/phpunit/phpunit/phpunit', - '/bin/bash', - 'local', - '5d6882a9e38ff', - 'Logger.php:453', - 1380624, - '123', - '[SeasLog Array]' - ] - ] - ]; - $target = new WebsocketTarget(); - $this->assertInstanceOf(WebsocketTarget::class, $target); - - $pm->parentFunc = function ($pid) use ($pm, $log) { - \Co\Run(function () use ($log) { - $cli = new Client('127.0.0.1', 9501); - $cli->set(['timeout' => -1]); - while (!@$cli->upgrade('/')) { - Co::sleep(0.1); - } - $frame = $cli->recv(); - $data = json_decode($frame->data, true); - [$msg, $colors] = $data; - $this->assertEquals(count($msg), count($colors)); - $this->assertEquals($msg[8], '123'); - $inColor = true; - foreach ($colors as $color) { - if (!in_array($color, HtmlColor::getPossibleColorsRGB())) { - $inColor = false; - break; - } - } - $this->assertTrue($inColor); - }); - $pm->kill(); - }; - $pm->childFunc = function () use ($pm, $target, $log) { - $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); - $server->set([ - 'worker_num' => 1, - 'log_file' => '/dev/null', - 'pid_file' => '/dev/shm/ws.pid' - ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); - }); - $server->on('message', function (Server $server, Frame $frame) { - }); - $server->on('open', - function (Swoole\WebSocket\Server $server, Swoole\Http\Request $request) use ($target, $log) { - $target->setGetServer(function () use ($server) { - return $server; - }); - $target->export($log); - $server->shutdown(); - }); - $server->start(); - }; - $pm->run(); - } - - public function testExportWithJsonType() - { - $pm = new ProcessManager(); - $log = [ - 'logger' => [ - [ - '2019-08-30 09:58:01.937', - 'WARNING', - 'vendor/phpunit/phpunit/phpunit', - '/bin/bash', - 'local', - '5d6882a9e38ff', - 'Logger.php:453', - 1380624, - json_encode(['task_id' => '123']), - '[SeasLog Array]' - ] - ] - ]; - $target = new WebsocketTarget(); - $this->assertInstanceOf(WebsocketTarget::class, $target); - - $pm->parentFunc = function ($pid) use ($pm, $log) { - \Co\Run(function () use ($log) { - $cli = new Client('127.0.0.1', 9501); - $cli->set(['timeout' => -1]); - while (!@$cli->upgrade('/')) { - Co::sleep(0.1); - } - $frame = $cli->recv(); - $data = json_decode($frame->data, true); - [$msg, $color] = $data; - $this->assertEquals(count($msg), count($color)); - $this->assertEquals($msg[8], json_encode(['task_id' => '123'])); - }); - $pm->kill(); - }; - $pm->childFunc = function () use ($pm, $target, $log) { - $server = new Server('127.0.0.1', 9501, SWOOLE_BASE); - $server->set([ - 'worker_num' => 1, - 'log_file' => '/dev/null', - 'pid_file' => '/dev/shm/ws.pid' - ]); - $server->on('workerStart', function () use ($pm) { - $pm->wakeup(); - }); - $server->on('message', function (Server $server, Frame $frame) { - }); - $server->on('open', - function (Swoole\WebSocket\Server $server, Swoole\Http\Request $request) use ($target, $log) { - $target->setGetServer(function () use ($server) { - return $server; - }); - $target->setCustomerFieldType(AbstractConfig::TYPE_JSON); - $target->export($log); - $server->shutdown(); - }); - $server->start(); - }; - $pm->run(); - } -} \ No newline at end of file From fd255de013973daaf8038d9df39ba89157279dc9 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 10 Sep 2019 17:03:20 +0800 Subject: [PATCH 17/27] fix test --- docker-seaslog.ini | 115 ------------------------------------- tests/SwooleLoggerTest.php | 10 +++- 2 files changed, 9 insertions(+), 116 deletions(-) delete mode 100644 docker-seaslog.ini diff --git a/docker-seaslog.ini b/docker-seaslog.ini deleted file mode 100644 index 1b39a41..0000000 --- a/docker-seaslog.ini +++ /dev/null @@ -1,115 +0,0 @@ -[SeasLog] -;configuration for php SeasLog module -extension = seaslog.so - -;默认log根目录 -seaslog.default_basepath = "/data/runtime/logs" - -;默认logger目录 -seaslog.default_logger = "Rabbit" - -;日期格式配置 默认"Y-m-d H:i:s" -seaslog.default_datetime_format = "Y-m-d H:i:s" - -;日志格式模板 默认"%T | %L | %P | %Q | %t | %M" -seaslog.default_template = "%T | %L | %R | %m | %I | %Q | %F | %U | %M" - -;是否以目录区分Logger 1是(默认) 0否 -seaslog.disting_folder = 0 - -;是否以type分文件 1是 0否(默认) -seaslog.disting_type = 1 - -;是否每小时划分一个文件 1是 0否(默认) -seaslog.disting_by_hour = 1 - -;是否启用buffer 1是 0否(默认) -seaslog.use_buffer = 1 - -;buffer中缓冲数量 默认0(不使用buffer_size) -seaslog.buffer_size = 300000 - -;cli运行时关闭buffer -;1是 0否(默认) -seaslog.buffer_disabled_in_cli = 0 - -;记录日志级别,数字越大,根据级别记的日志越多。 -;0-EMERGENCY 1-ALERT 2-CRITICAL 3-ERROR 4-WARNING 5-NOTICE 6-INFO 7-DEBUG 8-ALL -;默认8(所有日志) -; -; 注意, 该配置项自1.7.0版本开始有变动。 -; 在1.7.0版本之前, 该值数字越小,根据级别记的日志越多: -; 0-all 1-debug 2-info 3-notice 4-warning 5-error 6-critical 7-alert 8-emergency -; 1.7.0 之前的版本,该值默认为0(所有日志); -seaslog.level = 8 - -;日志函数调用回溯层级 -;影响预定义变量 %F 中的行数 -;默认0 -seaslog.recall_depth = 3 - -;自动记录notice 默认0(关闭) -seaslog.trace_notice = 0 - -;自动记录warning 默认0(关闭) -seaslog.trace_warning = 0 - -;自动记录错误 默认1(开启) -seaslog.trace_error = 1 - -;自动记录异常信息 默认0(关闭) -seaslog.trace_exception = 0 - -;日志存储介质 1File 2TCP 3UDP (默认为1) -seaslog.appender = 1 - -;写入重试次数 -;默认0(不重试) -seaslog.appender_retry = 3 - -;接收ip 默认127.0.0.1 (当使用TCP或UDP时必填) -seaslog.remote_host = "seasstash.log" - -;接收端口 默认514 (当使用TCP或UDP时必填) -seaslog.remote_port = 514 - -;接收端口的超时时间 默认1秒 -seaslog.remote_timeout = 1 - -;过滤日志中的回车和换行符 (默认为0) -seaslog.trim_wrap = 1 - -;是否开启抛出SeasLog自身异常 1开启(默认) 0否 -seaslog.throw_exception = 1 - -;是否开启忽略SeasLog自身warning 1开启(默认) 0否 -seaslog.ignore_warning = 1 - -;是否开启性能追踪 1开启 0关闭(默认) -seaslog.trace_performance = 0 - -;性能追踪时的千分比采样率 -;默认10,即百分之一 -seaslog.trace_performance_sample_rate = 100 - -;性能追踪时的开始层级 默认从第1层开始 -seaslog.trace_performance_start_depth = 1 - -;性能追踪时深度层级 默认5层 -seaslog.trace_performance_max_depth = 5 - -;性能追踪时每层的函数最大数 按wall_time降序排列top 默认top5 -seaslog.trace_performance_max_functions_per_depth = 5 - -;性能追踪时当前请求执行时间的记录阈值 只有当请求执行时间大于该值时,才记录性能日志 默认1000ms -seaslog.trace_performance_min_wall_time = 1000 - -;性能追踪时每个方法执行时间的记录阈值 只有当方法执行时间大于该值时,才参与计算 默认10ms -seaslog.trace_performance_min_function_wall_time = 10 - -[fork] -;是否开启多线程 1开启 0关闭 -fork_open = 1 - -;线程个数 -fork_count = 3 \ No newline at end of file diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 5804292..44773ea 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -193,7 +193,15 @@ public function testLog() public function testLogWithFieldTemplate() { \Co\run(function () { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); From 95b4d6add8353e024b428ae4c66965417ff0064e Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 10 Sep 2019 18:30:31 +0800 Subject: [PATCH 18/27] changed property config from private to protected --- src/Logger.php | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Logger.php b/src/Logger.php index a07dc01..03982f6 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -91,7 +91,7 @@ class Logger implements LoggerInterface self::EMERGENCY => 'EMERGENCY', ]; /** @var AbstractConfig */ - private static $config; + protected static $config; /** * Logger constructor. @@ -102,7 +102,7 @@ public function __construct(?AbstractConfig $config = null) if ($config !== null && !extension_loaded('swoole')) { throw new NotSupportedException("This usage must have swoole version>=4"); } - static::$config = $config; + self::$config = $config; } /** @@ -114,7 +114,7 @@ public function __construct(?AbstractConfig $config = null) */ public static function setRequestID($request_id) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support setRequestID"); } return SeasLog::setRequestID($request_id); @@ -127,7 +127,7 @@ public static function setRequestID($request_id) */ public static function getRequestID() { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support getRequestID"); } return SeasLog::getRequestID(); @@ -142,7 +142,7 @@ public static function getRequestID() */ public static function setLogger($module) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support setLogger"); } return SeasLog::setLogger($module); @@ -155,7 +155,7 @@ public static function setLogger($module) */ public static function getLastLogger() { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support getLastLogger"); } return SeasLog::getLastLogger(); @@ -170,7 +170,7 @@ public static function getLastLogger() */ public static function setDatetimeFormat($format) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { LoggerConfig::setDatetimeFormat($format); } return SeasLog::setDatetimeFormat($format); @@ -183,7 +183,7 @@ public static function setDatetimeFormat($format) */ public static function getDatetimeFormat() { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { return LoggerConfig::getDatetimeFormat(); } return SeasLog::getDatetimeFormat(); @@ -200,7 +200,7 @@ public static function getDatetimeFormat() */ public static function analyzerCount($level = 'all', $log_path = '*', $key_word = null) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support analyzerCount"); } return SeasLog::analyzerCount($level, $log_path, (string)$key_word); @@ -226,7 +226,7 @@ public static function analyzerDetail( $limit = 20, $order = SEASLOG_DETAIL_ORDER_ASC ) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support analyzerDetail"); } return SeasLog::analyzerDetail( @@ -246,7 +246,7 @@ public static function analyzerDetail( */ public static function getBuffer() { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { return LoggerConfig::getBuffer(); } return SeasLog::getBuffer(); @@ -259,7 +259,7 @@ public static function getBuffer() */ public static function flushBuffer() { - if (static::$config) { + if (self::$config) { throw new NotSupportedException("This ENV not support flushBuffer"); } return SeasLog::flushBuffer(); @@ -274,7 +274,7 @@ public static function flushBuffer() */ public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL, $name = '') { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException("LoggerConfig not support closeLoggerStream"); } if (empty($name)) { @@ -290,7 +290,7 @@ public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD */ public function getConfig(): ?AbstractConfig { - return static::$config; + return self::$config; } /** @@ -313,7 +313,7 @@ public function setRequestLevel($level = self::ALL) */ public function emergency($message, array $context = array()) { - empty(static::$config) ? SeasLog::emergency($message, $context) : $this->log(self::EMERGENCY, $message, + empty(self::$config) ? SeasLog::emergency($message, $context) : $this->log(self::EMERGENCY, $message, $context); } @@ -336,15 +336,15 @@ public function log($level, $message, array $context = array()) return; } - if (empty(static::$config)) { + if (empty(self::$config)) { $levelFunction = strtolower(self::$levels[$level]); SeasLog::$levelFunction($message, $context); } else { if (is_string($message)) { - static::$config->log(self::$levels[$level], $message, $context); + self::$config->log(self::$levels[$level], $message, $context); } elseif (is_array($message)) { foreach ($message as $m) { - static::$config->log(self::$levels[$level], $m, $context); + self::$config->log(self::$levels[$level], $m, $context); } } } @@ -363,7 +363,7 @@ public function log($level, $message, array $context = array()) */ public function alert($message, array $context = array()) { - empty(static::$config) ? SeasLog::alert($message, $context) : $this->log(self::ALERT, $message, $context); + empty(self::$config) ? SeasLog::alert($message, $context) : $this->log(self::ALERT, $message, $context); } /** @@ -378,7 +378,7 @@ public function alert($message, array $context = array()) */ public function critical($message, array $context = array()) { - empty(static::$config) ? SeasLog::critical($message, $context) : $this->log(self::CRITICAL, $message, + empty(self::$config) ? SeasLog::critical($message, $context) : $this->log(self::CRITICAL, $message, $context); } @@ -393,7 +393,7 @@ public function critical($message, array $context = array()) */ public function error($message, array $context = array()) { - empty(static::$config) ? SeasLog::error($message, $context) : $this->log(self::ERROR, $message, $context); + empty(self::$config) ? SeasLog::error($message, $context) : $this->log(self::ERROR, $message, $context); } /** @@ -409,7 +409,7 @@ public function error($message, array $context = array()) */ public function warning($message, array $context = array()) { - empty(static::$config) ? SeasLog::warning($message, $context) : $this->log(self::WARNING, $message, $context); + empty(self::$config) ? SeasLog::warning($message, $context) : $this->log(self::WARNING, $message, $context); } /** @@ -422,7 +422,7 @@ public function warning($message, array $context = array()) */ public function notice($message, array $context = array()) { - empty(static::$config) ? SeasLog::notice($message, $context) : $this->log(self::NOTICE, $message, $context); + empty(self::$config) ? SeasLog::notice($message, $context) : $this->log(self::NOTICE, $message, $context); } /** @@ -437,7 +437,7 @@ public function notice($message, array $context = array()) */ public function info($message, array $context = array()) { - empty(static::$config) ? SeasLog::info($message, $context) : $this->log(self::INFO, $message, $context); + empty(self::$config) ? SeasLog::info($message, $context) : $this->log(self::INFO, $message, $context); } /** @@ -450,7 +450,7 @@ public function info($message, array $context = array()) */ public function debug($message, array $context = array()) { - empty(static::$config) ? SeasLog::debug($message, $context) : $this->log(self::DEBUG, $message, $context); + empty(self::$config) ? SeasLog::debug($message, $context) : $this->log(self::DEBUG, $message, $context); } /** @@ -458,7 +458,7 @@ public function debug($message, array $context = array()) */ public function getBasePath() { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); } return SeasLog::getBasePath(); @@ -488,7 +488,7 @@ public function __invoke(array $config) */ public function setBasePath(string $basePath) { - if (static::$config instanceof LoggerConfig) { + if (self::$config instanceof LoggerConfig) { throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); } return SeasLog::setBasePath($basePath); From 15d8ea9d6d5f13b7133e02169e2b1bf3d2359307 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Tue, 10 Sep 2019 19:44:56 +0800 Subject: [PATCH 19/27] add more check --- src/Logger.php | 26 +++++--- src/SeaslogConfig.php | 23 ++++---- tests/SwooleLoggerTest.php | 118 ++++++++++++++++++++++++------------- 3 files changed, 109 insertions(+), 58 deletions(-) diff --git a/src/Logger.php b/src/Logger.php index 03982f6..8493ef9 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -105,6 +105,24 @@ public function __construct(?AbstractConfig $config = null) self::$config = $config; } + /** + * @return AbstractConfig|null + */ + public function getConfig(): ?AbstractConfig + { + return self::$config; + } + + /** + * @param AbstractConfig $config + * @return Logger + */ + public function setConfig(AbstractConfig $config): self + { + self::$config = $config; + return $this; + } + /** * 设置本次请求标识. * @@ -285,14 +303,6 @@ public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD } - /** - * @return AbstractConfig - */ - public function getConfig(): ?AbstractConfig - { - return self::$config; - } - /** * set request level for seaslog. * diff --git a/src/SeaslogConfig.php b/src/SeaslogConfig.php index 15da51c..786a834 100644 --- a/src/SeaslogConfig.php +++ b/src/SeaslogConfig.php @@ -33,7 +33,7 @@ public function __construct(array $target, array $configs = []) public function log(string $level, string $message, array $context = []): void { $template = $this->getTemplate(); - $module = ArrayHelper::remove($context, 'module', 'System'); + $module = ArrayHelper::remove($context, 'module'); if ($module !== null) { Seaslog::setLogger($this->appName . '_' . $module); } @@ -67,16 +67,19 @@ public function log(string $level, string $message, array $context = []): void */ public function flush(bool $flush = false): void { - $total = Seaslog::getBufferCount(); - if ($flush || $total >= $this->bufferSize) { - $buffer = Seaslog::getBuffer(); - Seaslog::flushBuffer(0); - foreach ($this->targetList as $index => $target) { - rgo(function () use ($target, $buffer, $flush) { - $target->export($buffer); - }); + if (method_exists('Seaslog', 'getBufferCount')) { + $total = Seaslog::getBufferCount(); + if (($flush || $total >= $this->bufferSize) && ($buffer = Seaslog::getBuffer()) !== false) { + Seaslog::flushBuffer(0); + foreach ($this->targetList as $index => $target) { + rgo(function () use ($target, $buffer, $flush) { + $target->export($buffer); + }); + } + unset($buffer); } - unset($buffer); + } else { + Seaslog::flushBuffer(); } } } \ No newline at end of file diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 44773ea..06627cd 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -4,7 +4,6 @@ namespace Seasx\SeasLogger\Tests; use Seasx\SeasLogger\AbstractConfig; -use Seasx\SeasLogger\Exceptions\NotSupportedException; use Seasx\SeasLogger\Logger; use Seasx\SeasLogger\LoggerConfig; use Seasx\SeasLogger\SeaslogConfig; @@ -12,6 +11,18 @@ class SwooleLoggerTest extends TestCase { + public function testGetLogger() + { + $logger = new Logger(); + $this->assertEmpty($logger->getConfig()); + + $logger = $this->init(); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + + $logger = $this->init(1); + $this->assertInstanceOf(SeaslogConfig::class, $logger->getConfig()); + } + /** * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException */ @@ -20,10 +31,10 @@ public function testGetBasePath() $logger = $this->init(); $logger->getBasePath(); -// $logger = $this->init(1); -// $logger->setBasePath('/tmp/seaslogger'); -// $basePath = $logger->getBasePath(); -// $this->assertEquals('/tmp/seaslogger', $basePath); + $logger = $this->init(1); + $logger->setBasePath('/tmp/seaslogger'); + $basePath = $logger->getBasePath(); + $this->assertEquals('/tmp/seaslogger', $basePath); } /** @@ -53,9 +64,9 @@ public function testSetRequestID() $logger = $this->init(); $logger::setRequestID(1024); -// $logger = $this->init(1); -// $result = $logger::setRequestID(1024); -// $this->assertTrue($result); + $logger = $this->init(1); + $result = $logger::setRequestID(1024); + $this->assertTrue($result); } /** @@ -66,11 +77,11 @@ public function testGetRequestID() $logger = $this->init(); $logger::getRequestID(); -// $logger = $this->init(1); -// $result = $logger::setRequestID(1024); -// $this->assertTrue($result); -// $requestID = $logger::getRequestID(); -// $this->assertEquals(1024, $requestID); + $logger = $this->init(1); + $result = $logger::setRequestID(1024); + $this->assertTrue($result); + $requestID = $logger::getRequestID(); + $this->assertEquals(1024, $requestID); } public function testEmergency() @@ -78,6 +89,7 @@ public function testEmergency() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); // $logger = $this->init(1); @@ -91,6 +103,7 @@ public function testAlert() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); // $logger = $this->init(1); @@ -104,6 +117,7 @@ public function testCritical() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); // $logger = $this->init(1); @@ -117,6 +131,7 @@ public function testError() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->error('[LoggerConfig Test]', ['level' => 'error']); // $logger = $this->init(1); @@ -130,6 +145,7 @@ public function testWarning() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); // $logger = $this->init(1); @@ -143,6 +159,7 @@ public function testNotice() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); // $logger = $this->init(1); @@ -156,6 +173,7 @@ public function testInfo() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->info('[LoggerConfig Test]', ['level' => 'info']); // $logger = $this->init(1); @@ -169,6 +187,7 @@ public function testDebug() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); // $logger = $this->init(1); @@ -182,6 +201,7 @@ public function testLog() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); // $logger = $this->init(1); @@ -203,6 +223,7 @@ public function testLogWithFieldTemplate() 'recall_depth' => 2, ])); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); // $logger = $this->init(1); @@ -225,6 +246,7 @@ public function testLogWithJsonTemplate() 'recall_depth' => 2, ])); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::INFO, '[LoggerConfig JsonTemplate]', ['level' => 'log', 'template' => ['task_type' => 'test']]); @@ -248,6 +270,7 @@ public function testRequestLevel() \Co\run(function () { $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->setRequestLevel(Logger::ALL); $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'DEBUG']); $logger->log(Logger::WARNING, '[LoggerConfig Test]', ['level' => 'WARNING']); @@ -284,9 +307,9 @@ public function testSetLogger() $logger = $this->init(); $logger::setLogger('seas'); -// $logger = $this->init(1); -// $result = $logger::setLogger('seas'); -// $this->assertTrue($result); + $logger = $this->init(1); + $result = $logger::setLogger('seas'); + $this->assertTrue($result); } /** @@ -297,11 +320,11 @@ public function testGetLastLogger() $logger = $this->init(); $logger::getLastLogger(); -// $logger = $this->init(1); -// $result = $logger::setLogger('seas'); -// $this->assertTrue($result); -// $model = $logger::getLastLogger(); -// $this->assertEquals('seas', $model); + $logger = $this->init(1); + $result = $logger::setLogger('seas'); + $this->assertTrue($result); + $model = $logger::getLastLogger(); + $this->assertEquals('seas', $model); } public function testSetDatetimeFormat() @@ -309,6 +332,10 @@ public function testSetDatetimeFormat() $logger = $this->init(); $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); $this->assertTrue($result); + + $logger = $this->init(1); + $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); } public function testGetDatetimeFormat() @@ -318,6 +345,12 @@ public function testGetDatetimeFormat() $this->assertTrue($result); $format = $logger::getDatetimeFormat(); $this->assertEquals('Y-m-d H:i:s', $format); + + $logger = $this->init(1); + $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + $format = $logger::getDatetimeFormat(); + $this->assertEquals('Y-m-d H:i:s', $format); } /** @@ -328,9 +361,9 @@ public function testAnalyzerCount() $logger = $this->init(); $logger::analyzerCount(); -// $logger = $this->init(1); -// $result = $logger::analyzerCount(); -// $this->assertNotNull($result); + $logger = $this->init(1); + $result = $logger::analyzerCount(); + $this->assertNotNull($result); } /** @@ -341,24 +374,29 @@ public function testAnalyzerDetail() $logger = $this->init(); $logger::analyzerDetail(); -// $logger = $this->init(1); -// $result = $logger::analyzerDetail(); -// $this->assertNotNull($result); + $logger = $this->init(1); + $result = $logger::analyzerDetail(); + $this->assertNotNull($result); } public function testGetBuffer() { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $logger->info('[SeasLog Test]', ['level' => 'info']); - $buffer = $logger::getBuffer(); - $this->assertNotNull($buffer); + \Co\run(function (){ + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->info('[LoggerConfig Test]', ['level' => 'info']); + $buffer = $logger::getBuffer(); + $this->assertNotNull($buffer); + +// $logger = $this->init(1); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->info('[SeasLog Test]', ['level' => 'info']); +// $buffer = $logger::getBuffer(); +// $this->assertNotNull($buffer); + }); + -// $logger = $this->init(1); -// $this->assertInstanceOf(Logger::class, $logger); -// $logger->info('[SeasLog Test]', ['level' => 'info']); -// $buffer = $logger::getBuffer(); -// $this->assertNotNull($buffer); } /** @@ -381,9 +419,9 @@ public function testInvoke() $seasLogger = $logger(['path' => '/tmp/logger']); $this->assertInstanceOf(Logger::class, $seasLogger); -// $logger = $this->init(1); -// $seasLogger = $logger(['path' => '/tmp/logger']); -// $this->assertInstanceOf(Logger::class, $seasLogger); + $logger = $this->init(1); + $seasLogger = $logger(['path' => '/tmp/logger']); + $this->assertInstanceOf(Logger::class, $seasLogger); } // public function testCloseLoggerStreamAll() From 889bb8c4addd8249af0aa97bc6306ef561b8c6d7 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Wed, 11 Sep 2019 11:41:35 +0800 Subject: [PATCH 20/27] changed logger's config from static to normal and add some config function. add more unit test --- src/AbstractConfig.php | 31 ++++-- src/Logger.php | 99 +++++++++--------- src/LoggerConfig.php | 34 +++--- src/SeaslogConfig.php | 27 ++++- tests/LoggerConfigTest.php | 60 +++++++++++ tests/SeaslogConfigTest.php | 57 ++++++++++ tests/SwooleLoggerTest.php | 203 ++++++++++++++++-------------------- 7 files changed, 318 insertions(+), 193 deletions(-) create mode 100644 tests/LoggerConfigTest.php create mode 100644 tests/SeaslogConfigTest.php diff --git a/src/AbstractConfig.php b/src/AbstractConfig.php index eda3d41..9147eee 100644 --- a/src/AbstractConfig.php +++ b/src/AbstractConfig.php @@ -4,7 +4,6 @@ namespace Seasx\SeasLogger; use Seasx\SeasLogger\targets\AbstractTarget; -use Seasx\SeasLogger\Targets\StyleTarget; use Swoole\Timer; /** @@ -52,11 +51,6 @@ public function __construct(array $target, array $configs = []) } } $this->targetList = $target; - if (empty($this->targetList)) { - $this->targetList = [ - 'echo' => new StyleTarget() - ]; - } foreach ($this->targetList as $target) { $target->setTemplate($this->template)->setCustomerFieldType($this->customerType)->setSplit($this->split); } @@ -66,11 +60,34 @@ public function __construct(array $target, array $configs = []) $this->tick > 0 && Timer::tick($this->tick * 1000, [$this, 'flush'], [true]); } + /** + * @return array + */ + public function getTemplate(): array + { + return $this->template; + } + /** * @param bool $flush */ abstract public function flush(bool $flush = false): void; + /** + * @return array + */ + abstract public function getBuffer(): array; + + /** + * @return string + */ + abstract public function getDatetimeFormat(): string; + + /** + * @param string $format + * @return bool + */ + abstract public function setDatetimeFormat(string $format): bool; /** * @return array */ @@ -97,7 +114,7 @@ abstract public function log(string $level, string $message, array $context = [] /** * @return array */ - protected function getTemplate(): array + protected function getUserTemplate(): array { if ($this->userTemplate) { $template = call_user_func($this->userTemplate); diff --git a/src/Logger.php b/src/Logger.php index 8493ef9..32bb6a8 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -91,7 +91,7 @@ class Logger implements LoggerInterface self::EMERGENCY => 'EMERGENCY', ]; /** @var AbstractConfig */ - protected static $config; + protected $config; /** * Logger constructor. @@ -102,7 +102,7 @@ public function __construct(?AbstractConfig $config = null) if ($config !== null && !extension_loaded('swoole')) { throw new NotSupportedException("This usage must have swoole version>=4"); } - self::$config = $config; + $this->config = $config; } /** @@ -110,16 +110,16 @@ public function __construct(?AbstractConfig $config = null) */ public function getConfig(): ?AbstractConfig { - return self::$config; + return $this->config; } /** * @param AbstractConfig $config * @return Logger */ - public function setConfig(AbstractConfig $config): self + public function setConfig(?AbstractConfig $config): self { - self::$config = $config; + $this->config = $config; return $this; } @@ -132,9 +132,6 @@ public function setConfig(AbstractConfig $config): self */ public static function setRequestID($request_id) { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support setRequestID"); - } return SeasLog::setRequestID($request_id); } @@ -145,9 +142,6 @@ public static function setRequestID($request_id) */ public static function getRequestID() { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support getRequestID"); - } return SeasLog::getRequestID(); } @@ -160,9 +154,6 @@ public static function getRequestID() */ public static function setLogger($module) { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support setLogger"); - } return SeasLog::setLogger($module); } @@ -173,9 +164,6 @@ public static function setLogger($module) */ public static function getLastLogger() { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support getLastLogger"); - } return SeasLog::getLastLogger(); } @@ -188,12 +176,26 @@ public static function getLastLogger() */ public static function setDatetimeFormat($format) { - if (self::$config instanceof LoggerConfig) { - LoggerConfig::setDatetimeFormat($format); - } return SeasLog::setDatetimeFormat($format); } + /** + * @param string $format + * @return bool + */ + public function setConfigDatetimeFormat(string $format): bool + { + return $this->config->setDatetimeFormat($format); + } + + /** + * @return string + */ + public function getConfigDatetimeFormat(): string + { + return $this->config->getDatetimeFormat(); + } + /** * 返回当前DatetimeFormat配置格式. * @@ -201,9 +203,6 @@ public static function setDatetimeFormat($format) */ public static function getDatetimeFormat() { - if (self::$config instanceof LoggerConfig) { - return LoggerConfig::getDatetimeFormat(); - } return SeasLog::getDatetimeFormat(); } @@ -218,9 +217,6 @@ public static function getDatetimeFormat() */ public static function analyzerCount($level = 'all', $log_path = '*', $key_word = null) { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support analyzerCount"); - } return SeasLog::analyzerCount($level, $log_path, (string)$key_word); } @@ -244,9 +240,6 @@ public static function analyzerDetail( $limit = 20, $order = SEASLOG_DETAIL_ORDER_ASC ) { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support analyzerDetail"); - } return SeasLog::analyzerDetail( $level, $log_path, @@ -264,12 +257,17 @@ public static function analyzerDetail( */ public static function getBuffer() { - if (self::$config instanceof LoggerConfig) { - return LoggerConfig::getBuffer(); - } return SeasLog::getBuffer(); } + /** + * @return array + */ + public function getConfigBuffer(): array + { + return $this->config->getBuffer(); + } + /** * 将buffer中的日志立刻刷到硬盘. * @@ -277,12 +275,14 @@ public static function getBuffer() */ public static function flushBuffer() { - if (self::$config) { - throw new NotSupportedException("This ENV not support flushBuffer"); - } return SeasLog::flushBuffer(); } + public function flushConfigBuffer(): void + { + $this->config->flush(true); + } + /** * Manually release stream flow from logger * @@ -292,9 +292,6 @@ public static function flushBuffer() */ public static function closeLoggerStream($type = SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL, $name = '') { - if (self::$config instanceof LoggerConfig) { - throw new NotSupportedException("LoggerConfig not support closeLoggerStream"); - } if (empty($name)) { return SeasLog::closeLoggerStream($type); } @@ -323,7 +320,7 @@ public function setRequestLevel($level = self::ALL) */ public function emergency($message, array $context = array()) { - empty(self::$config) ? SeasLog::emergency($message, $context) : $this->log(self::EMERGENCY, $message, + empty($this->config) ? SeasLog::emergency($message, $context) : $this->log(self::EMERGENCY, $message, $context); } @@ -346,15 +343,15 @@ public function log($level, $message, array $context = array()) return; } - if (empty(self::$config)) { + if (empty($this->config)) { $levelFunction = strtolower(self::$levels[$level]); SeasLog::$levelFunction($message, $context); } else { if (is_string($message)) { - self::$config->log(self::$levels[$level], $message, $context); + $this->config->log(self::$levels[$level], $message, $context); } elseif (is_array($message)) { foreach ($message as $m) { - self::$config->log(self::$levels[$level], $m, $context); + $this->config->log(self::$levels[$level], $m, $context); } } } @@ -373,7 +370,7 @@ public function log($level, $message, array $context = array()) */ public function alert($message, array $context = array()) { - empty(self::$config) ? SeasLog::alert($message, $context) : $this->log(self::ALERT, $message, $context); + empty($this->config) ? SeasLog::alert($message, $context) : $this->log(self::ALERT, $message, $context); } /** @@ -388,7 +385,7 @@ public function alert($message, array $context = array()) */ public function critical($message, array $context = array()) { - empty(self::$config) ? SeasLog::critical($message, $context) : $this->log(self::CRITICAL, $message, + empty($this->config) ? SeasLog::critical($message, $context) : $this->log(self::CRITICAL, $message, $context); } @@ -403,7 +400,7 @@ public function critical($message, array $context = array()) */ public function error($message, array $context = array()) { - empty(self::$config) ? SeasLog::error($message, $context) : $this->log(self::ERROR, $message, $context); + empty($this->config) ? SeasLog::error($message, $context) : $this->log(self::ERROR, $message, $context); } /** @@ -419,7 +416,7 @@ public function error($message, array $context = array()) */ public function warning($message, array $context = array()) { - empty(self::$config) ? SeasLog::warning($message, $context) : $this->log(self::WARNING, $message, $context); + empty($this->config) ? SeasLog::warning($message, $context) : $this->log(self::WARNING, $message, $context); } /** @@ -432,7 +429,7 @@ public function warning($message, array $context = array()) */ public function notice($message, array $context = array()) { - empty(self::$config) ? SeasLog::notice($message, $context) : $this->log(self::NOTICE, $message, $context); + empty($this->config) ? SeasLog::notice($message, $context) : $this->log(self::NOTICE, $message, $context); } /** @@ -447,7 +444,7 @@ public function notice($message, array $context = array()) */ public function info($message, array $context = array()) { - empty(self::$config) ? SeasLog::info($message, $context) : $this->log(self::INFO, $message, $context); + empty($this->config) ? SeasLog::info($message, $context) : $this->log(self::INFO, $message, $context); } /** @@ -460,7 +457,7 @@ public function info($message, array $context = array()) */ public function debug($message, array $context = array()) { - empty(self::$config) ? SeasLog::debug($message, $context) : $this->log(self::DEBUG, $message, $context); + empty($this->config) ? SeasLog::debug($message, $context) : $this->log(self::DEBUG, $message, $context); } /** @@ -468,7 +465,7 @@ public function debug($message, array $context = array()) */ public function getBasePath() { - if (self::$config instanceof LoggerConfig) { + if ($this->config instanceof LoggerConfig) { throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); } return SeasLog::getBasePath(); @@ -498,7 +495,7 @@ public function __invoke(array $config) */ public function setBasePath(string $basePath) { - if (self::$config instanceof LoggerConfig) { + if ($this->config instanceof LoggerConfig) { throw new NotSupportedException(sprintf("LoggerConfig not support %s", __METHOD__)); } return SeasLog::setBasePath($basePath); diff --git a/src/LoggerConfig.php b/src/LoggerConfig.php index 9a74495..6bd1d9f 100644 --- a/src/LoggerConfig.php +++ b/src/LoggerConfig.php @@ -14,9 +14,9 @@ class LoggerConfig extends AbstractConfig { /** @var array */ - protected static $buffer = []; + protected $buffer = []; /** @var string */ - protected static $datetime_format = "Y-m-d H:i:s"; + protected $datetime_format = "Y-m-d H:i:s"; /** @var array */ private static $supportTemplate = [ '%W', @@ -65,19 +65,19 @@ public function __construct( /** * @return string */ - public static function getDatetimeFormat(): string + public function getDatetimeFormat(): string { - return static::$datetime_format; + return $this->datetime_format; } /** * @param string $format * @return bool */ - public static function setDatetimeFormat(string $format): bool + public function setDatetimeFormat(string $format): bool { if (date($format, time()) !== false) { - static::$datetime_format = $format; + $this->datetime_format = $format; return true; } return false; @@ -88,9 +88,9 @@ public static function setDatetimeFormat(string $format): bool * * @return array */ - public static function getBuffer(): array + public function getBuffer(): array { - return static::$buffer; + return $this->buffer; } /** @@ -101,9 +101,9 @@ public static function getBuffer(): array */ public function log(string $level, string $message, array $context = []): void { - $template = $this->getTemplate(); + $template = $this->getUserTemplate(); $msg = []; - $module = ArrayHelper::getValue($context, 'module', 'System'); + $module = ArrayHelper::getValue($context, 'module'); foreach ($this->template as $tmp) { switch ($tmp) { case '%W': @@ -127,9 +127,9 @@ public function log(string $level, string $message, array $context = []): void $milliseconds = 0; } if ($tmp === '%T') { - $msg[] = date(static::$datetime_format, (int)$timestamp) . '.' . (int)$milliseconds; + $msg[] = date($this->datetime_format, (int)$timestamp) . '.' . (int)$milliseconds; } else { - $msg[] = date(static::$datetime_format, (int)$timestamp); + $msg[] = date($this->datetime_format, (int)$timestamp); } break; case '%Q': @@ -189,8 +189,8 @@ public function log(string $level, string $message, array $context = []): void } $color = ArrayHelper::getValue($template, '%c'); $color && $msg['%c'] = $color; - $key = $this->appName . '_' . $module; - static::$buffer[$key][] = $msg; + $key = $this->appName . ($module ? '_' . $module : ''); + $this->buffer[$key][] = $msg; $this->flush(); } @@ -200,13 +200,13 @@ public function log(string $level, string $message, array $context = []): void */ public function flush(bool $flush = false): void { - if (!empty(static::$buffer) && $flush || ($this->bufferSize !== 0 && $this->bufferSize <= count(static::$buffer))) { + if (!empty($this->buffer) && $flush || ($this->bufferSize !== 0 && $this->bufferSize <= count($this->buffer))) { foreach ($this->targetList as $index => $target) { rgo(function () use ($target, $flush) { - $target->export(static::$buffer); + $target->export($this->buffer); }); } - array_splice(static::$buffer, 0); + array_splice($this->buffer, 0); } } } \ No newline at end of file diff --git a/src/SeaslogConfig.php b/src/SeaslogConfig.php index 786a834..984e7b8 100644 --- a/src/SeaslogConfig.php +++ b/src/SeaslogConfig.php @@ -24,6 +24,26 @@ public function __construct(array $target, array $configs = []) parent::__construct($target, $configs); } + /** + * @return array + */ + public function getBuffer(): array + { + $buffer = Seaslog::getBuffer(); + return $buffer !== false ? $buffer : []; + } + + public function getDatetimeFormat(): string + { + return SeasLog::getDatetimeFormat(); + } + + public function setDatetimeFormat(string $format): bool + { + return SeasLog::setDatetimeFormat($format); + } + + /** * @param string $level * @param string $message @@ -32,7 +52,7 @@ public function __construct(array $target, array $configs = []) */ public function log(string $level, string $message, array $context = []): void { - $template = $this->getTemplate(); + $template = $this->getUserTemplate(); $module = ArrayHelper::remove($context, 'module'); if ($module !== null) { Seaslog::setLogger($this->appName . '_' . $module); @@ -68,8 +88,7 @@ public function log(string $level, string $message, array $context = []): void public function flush(bool $flush = false): void { if (method_exists('Seaslog', 'getBufferCount')) { - $total = Seaslog::getBufferCount(); - if (($flush || $total >= $this->bufferSize) && ($buffer = Seaslog::getBuffer()) !== false) { + if (($flush || ($total = Seaslog::getBufferCount()) >= $this->bufferSize) && ($buffer = Seaslog::getBuffer()) !== false) { Seaslog::flushBuffer(0); foreach ($this->targetList as $index => $target) { rgo(function () use ($target, $buffer, $flush) { @@ -78,7 +97,7 @@ public function flush(bool $flush = false): void } unset($buffer); } - } else { + } elseif ($flush) { Seaslog::flushBuffer(); } } diff --git a/tests/LoggerConfigTest.php b/tests/LoggerConfigTest.php new file mode 100644 index 0000000..5128aa1 --- /dev/null +++ b/tests/LoggerConfigTest.php @@ -0,0 +1,60 @@ + 'Seaslog', + 'bufferSize' => $bufferSize, + 'tick' => 0, + 'recall_depth' => 1, + ]); + return $config; + } + + public function testSetDatetimeFormat() + { + $config = $this->init(); + $result = $config->setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + } + + public function testGetDatetimeFormat() + { + $config = $this->init(); + $result = $config->setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + $format = $config->getDatetimeFormat(); + $this->assertEquals('Y-m-d H:i:s', $format); + } + + public function testGetBuffer() + { + $config = $this->init(100); + $config->log(LogLevel::INFO, 'LoggerConfig Test'); + $buffer = $config->getBuffer(); + $this->assertNotEmpty($buffer); + $this->assertEquals(count($buffer['Seaslog']), 1); + $this->assertEquals(count(current($buffer['Seaslog'])), count($config->getTemplate())); + } + + public function testFlush() + { + \Co\Run(function () { + $config = $this->init(100); + $config->log(LogLevel::INFO, 'LoggerConfig Test'); + $config->flush(true); + $this->assertEmpty($config->getBuffer()); + }); + } +} \ No newline at end of file diff --git a/tests/SeaslogConfigTest.php b/tests/SeaslogConfigTest.php new file mode 100644 index 0000000..72e5c4d --- /dev/null +++ b/tests/SeaslogConfigTest.php @@ -0,0 +1,57 @@ + 'Seaslog', + 'bufferSize' => $bufferSize, + 'tick' => 0, + 'recall_depth' => 1, + ]); + return $config; + } + + public function testSetDatetimeFormat() + { + $config = $this->init(); + $result = $config->setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + } + + public function testGetDatetimeFormat() + { + $config = $this->init(); + $result = $config->setDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + $format = $config->getDatetimeFormat(); + $this->assertEquals('Y-m-d H:i:s', $format); + } + + public function testGetBuffer() + { + $config = $this->init(100); + $config->log(LogLevel::INFO, 'LoggerConfig Test'); + $buffer = $config->getBuffer(); + $this->assertNotNull($buffer); + } + + public function testFlush() + { + \Co\Run(function () { + $config = $this->init(100); + $config->log(LogLevel::INFO, 'LoggerConfig Test'); + $config->flush(true); + $this->assertEmpty($config->getBuffer()); + }); + } +} \ No newline at end of file diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 06627cd..b92d0e0 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -11,7 +11,7 @@ class SwooleLoggerTest extends TestCase { - public function testGetLogger() + public function testGetConfig() { $logger = new Logger(); $this->assertEmpty($logger->getConfig()); @@ -23,14 +23,8 @@ public function testGetLogger() $this->assertInstanceOf(SeaslogConfig::class, $logger->getConfig()); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testGetBasePath() { - $logger = $this->init(); - $logger->getBasePath(); - $logger = $this->init(1); $logger->setBasePath('/tmp/seaslogger'); $basePath = $logger->getBasePath(); @@ -39,9 +33,10 @@ public function testGetBasePath() /** * @param int $type + * @param int $bufferSize * @return Logger */ - public function init(int $type = 0) + public function init(int $type = 0, int $bufferSize = 1) { $class = $type === 0 ? LoggerConfig::class : SeaslogConfig::class; $logger = new Logger( @@ -49,34 +44,22 @@ public function init(int $type = 0) 'echo' => new StyleTarget() ], [ 'appName' => 'Seaslog', - 'bufferSize' => 1, + 'bufferSize' => $bufferSize, 'tick' => 0, 'recall_depth' => 2, ])); return $logger; } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testSetRequestID() { - $logger = $this->init(); - $logger::setRequestID(1024); - $logger = $this->init(1); $result = $logger::setRequestID(1024); $this->assertTrue($result); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testGetRequestID() { - $logger = $this->init(); - $logger::getRequestID(); - $logger = $this->init(1); $result = $logger::setRequestID(1024); $this->assertTrue($result); @@ -299,27 +282,15 @@ public function testRequestLevel() }); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testSetLogger() { - $logger = $this->init(); - $logger::setLogger('seas'); - $logger = $this->init(1); $result = $logger::setLogger('seas'); $this->assertTrue($result); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testGetLastLogger() { - $logger = $this->init(); - $logger::getLastLogger(); - $logger = $this->init(1); $result = $logger::setLogger('seas'); $this->assertTrue($result); @@ -329,51 +300,55 @@ public function testGetLastLogger() public function testSetDatetimeFormat() { - $logger = $this->init(); + $logger = $this->init(1); $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); $this->assertTrue($result); + } + + public function testSetConfigDatetimeFormat() + { + $logger = $this->init(); + $result = $logger->setConfigDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); $logger = $this->init(1); - $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $result = $logger->setConfigDatetimeFormat('Y-m-d H:i:s'); $this->assertTrue($result); } public function testGetDatetimeFormat() { - $logger = $this->init(); + $logger = $this->init(1); $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); $this->assertTrue($result); $format = $logger::getDatetimeFormat(); $this->assertEquals('Y-m-d H:i:s', $format); + } + + public function testGetConfigDatetimeFormat() + { + $logger = $this->init(); + $result = $logger->setConfigDatetimeFormat('Y-m-d H:i:s'); + $this->assertTrue($result); + $format = $logger->getConfigDatetimeFormat(); + $this->assertEquals('Y-m-d H:i:s', $format); $logger = $this->init(1); - $result = $logger::setDatetimeFormat('Y-m-d H:i:s'); + $result = $logger->setConfigDatetimeFormat('Y-m-d H:i:s'); $this->assertTrue($result); - $format = $logger::getDatetimeFormat(); + $format = $logger->getConfigDatetimeFormat(); $this->assertEquals('Y-m-d H:i:s', $format); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testAnalyzerCount() { - $logger = $this->init(); - $logger::analyzerCount(); - $logger = $this->init(1); $result = $logger::analyzerCount(); $this->assertNotNull($result); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testAnalyzerDetail() { - $logger = $this->init(); - $logger::analyzerDetail(); - $logger = $this->init(1); $result = $logger::analyzerDetail(); $this->assertNotNull($result); @@ -381,38 +356,44 @@ public function testAnalyzerDetail() public function testGetBuffer() { - \Co\run(function (){ - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->info('[LoggerConfig Test]', ['level' => 'info']); - $buffer = $logger::getBuffer(); - $this->assertNotNull($buffer); - -// $logger = $this->init(1); -// $this->assertInstanceOf(Logger::class, $logger); -// $logger->info('[SeasLog Test]', ['level' => 'info']); -// $buffer = $logger::getBuffer(); -// $this->assertNotNull($buffer); - }); - + $logger = $this->init(1); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[SeasLog Get Buffer]', ['level' => 'info']); + $buffer = $logger::getBuffer(); + $this->assertNotNull($buffer); + } + public function testGetConfigBuffer() + { + $logger = $this->init(0, 100); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[LoggerConfig Get Buffer]', ['level' => 'info']); + $buffer = $logger->getConfigBuffer(); + $logger->flushConfigBuffer(); + $this->assertNotNull($buffer); + +// $logger = $this->init(1, 100); +// $this->assertInstanceOf(Logger::class, $logger); +// $logger->info('[SeaslogConfig Get Buffer]', ['level' => 'info']); +// $buffer = $logger->getConfigBuffer(); +// $this->assertNotNull($buffer); } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ - public function testFlushBuffer() + public function testFlushConfigBuffer() { - $logger = $this->init(); - $logger::flushBuffer(); + $logger = $this->init(0, 100); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[LoggerConfig Flush]', ['level' => 'info']); + $logger->flushConfigBuffer(); + $this->assertEmpty($logger->getConfigBuffer()); -// $logger = $this->init(1); -// $this->expectException(NotSupportedException::class); -// $logger::flushBuffer(); + $logger = $this->init(1); + $this->assertInstanceOf(Logger::class, $logger); + $logger->info('[SeaslogConfig Flush]', ['level' => 'info']); + $logger->flushConfigBuffer(); + $this->assertEmpty($logger->getConfigBuffer()); } - public function testInvoke() { $logger = $this->init(); @@ -424,47 +405,41 @@ public function testInvoke() $this->assertInstanceOf(Logger::class, $seasLogger); } -// public function testCloseLoggerStreamAll() -// { -// $logger = $this->init(1); -// $logger->setBasePath('/tmp/allLogger'); -// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); -// $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); -// $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); -// $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); -// $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); -// $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); -// $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); -// $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); -// -// $logger->log(0, '[SeasLog Test]', ['level' => 'default']); -// $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); -// -// $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); -// } + public function testCloseLoggerStreamAll() + { + $logger = $this->init(1); + $logger->setBasePath('/tmp/allLogger'); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); + + $logger->log(0, '[SeasLog Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + + $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ALL)); + } - /** - * @expectedException \Seasx\SeasLogger\Exceptions\NotSupportedException - */ public function testCloseLoggerStream() { - $logger = $this->init(); - $logger::closeLoggerStream(); - -// $logger = $this->init(1); -// $logger->setBasePath('/tmp/PandaLogger'); -// $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); -// $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); -// $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); -// $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); -// $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); -// $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); -// $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); -// $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); -// -// $logger->log(0, '[SeasLog Test]', ['level' => 'default']); -// $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); -// -// $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ASSIGN, '/tmp/PandaLogger')); + $logger = $this->init(1); + $logger->setBasePath('/tmp/PandaLogger'); + $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[SeasLog Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[SeasLog Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[SeasLog Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[SeasLog Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[SeasLog Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[SeasLog Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); + + $logger->log(0, '[SeasLog Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); + + $this->assertTrue($logger::closeLoggerStream(SEASLOG_CLOSE_LOGGER_STREAM_MOD_ASSIGN, '/tmp/PandaLogger')); } } \ No newline at end of file From 5a919469b1f1e1fd6e74dcc91b966ee94fac9dfc Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Wed, 11 Sep 2019 11:50:21 +0800 Subject: [PATCH 21/27] test log not in Co\run --- tests/SwooleLoggerTest.php | 176 ++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 100 deletions(-) diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index b92d0e0..160f603 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -69,169 +69,148 @@ public function testGetRequestID() public function testEmergency() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->emergency('[SeasLog Test]', ['level' => 'emergency']); - }); } public function testAlert() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->alert('[SeasLog Test]', ['level' => 'alert']); - }); } public function testCritical() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->critical('[SeasLog Test]', ['level' => 'critical']); - }); } public function testError() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->error('[LoggerConfig Test]', ['level' => 'error']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->error('[LoggerConfig Test]', ['level' => 'error']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->error('[SeasLog Test]', ['level' => 'error']); - }); } public function testWarning() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->warning('[SeasLog Test]', ['level' => 'warning']); - }); } public function testNotice() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->notice('[SeasLog Test]', ['level' => 'notice']); - }); } public function testInfo() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->info('[LoggerConfig Test]', ['level' => 'info']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->info('[LoggerConfig Test]', ['level' => 'info']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->info('[SeasLog Test]', ['level' => 'info']); - }); } public function testDebug() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->debug('[SeasLog Test]', ['level' => 'debug']); - }); } public function testLog() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->log(Logger::DEBUG, '[SeasLog Test]', ['level' => 'log']); - }); } public function testLogWithFieldTemplate() { - \Co\run(function () { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); // $logger->log(Logger::INFO, '[SeasLog FieldTemplate]', ['level' => 'log', 'template' => ['test']]); - }); } public function testLogWithJsonTemplate() { - \Co\run(function () { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'customerType' => AbstractConfig::TYPE_JSON, - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->log(Logger::INFO, '[LoggerConfig JsonTemplate]', - ['level' => 'log', 'template' => ['task_type' => 'test']]); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'customerType' => AbstractConfig::TYPE_JSON, + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->log(Logger::INFO, '[LoggerConfig JsonTemplate]', + ['level' => 'log', 'template' => ['task_type' => 'test']]); // $logger = new Logger( // new SeaslogConfig([ @@ -245,26 +224,24 @@ public function testLogWithJsonTemplate() // ])); // $this->assertInstanceOf(Logger::class, $logger); // $logger->log(Logger::INFO, '[SeasLog JsonTemplate]', ['level' => 'log', 'template' => ['task_type' => 'test']]); - }); } public function testRequestLevel() { - \Co\run(function () { - $logger = $this->init(); - $this->assertInstanceOf(Logger::class, $logger); - $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); - $logger->setRequestLevel(Logger::ALL); - $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'DEBUG']); - $logger->log(Logger::WARNING, '[LoggerConfig Test]', ['level' => 'WARNING']); - $logger->log(Logger::ERROR, '[LoggerConfig Test]', ['level' => 'ERROR']); - $logger->log(Logger::INFO, '[LoggerConfig Test]', ['level' => 'INFO']); - $logger->log(Logger::CRITICAL, '[LoggerConfig Test]', ['level' => 'CRITICAL']); - $logger->log(Logger::EMERGENCY, '[LoggerConfig Test]', ['level' => 'EMERGENCY']); - $logger->log(Logger::NOTICE, '[LoggerConfig Test]', ['level' => 'NOTICE']); - $logger->log(Logger::ALERT, '[LoggerConfig Test]', ['level' => 'ALERT']); - $logger->log(0, '[LoggerConfig Test]', ['level' => 'default']); - $logger->log(Logger::ALL - 1, '[LoggerConfig Test]', ['level' => 'default']); + $logger = $this->init(); + $this->assertInstanceOf(Logger::class, $logger); + $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); + $logger->setRequestLevel(Logger::ALL); + $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'DEBUG']); + $logger->log(Logger::WARNING, '[LoggerConfig Test]', ['level' => 'WARNING']); + $logger->log(Logger::ERROR, '[LoggerConfig Test]', ['level' => 'ERROR']); + $logger->log(Logger::INFO, '[LoggerConfig Test]', ['level' => 'INFO']); + $logger->log(Logger::CRITICAL, '[LoggerConfig Test]', ['level' => 'CRITICAL']); + $logger->log(Logger::EMERGENCY, '[LoggerConfig Test]', ['level' => 'EMERGENCY']); + $logger->log(Logger::NOTICE, '[LoggerConfig Test]', ['level' => 'NOTICE']); + $logger->log(Logger::ALERT, '[LoggerConfig Test]', ['level' => 'ALERT']); + $logger->log(0, '[LoggerConfig Test]', ['level' => 'default']); + $logger->log(Logger::ALL - 1, '[LoggerConfig Test]', ['level' => 'default']); // $logger = $this->init(1); // $this->assertInstanceOf(Logger::class, $logger); @@ -279,7 +256,6 @@ public function testRequestLevel() // $logger->log(Logger::ALERT, '[SeasLog Test]', ['level' => 'ALERT']); // $logger->log(0, '[SeasLog Test]', ['level' => 'default']); // $logger->log(Logger::ALL - 1, '[SeasLog Test]', ['level' => 'default']); - }); } public function testSetLogger() From a8da58f95bcb3f232adece10deb534ad68160974 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Wed, 11 Sep 2019 12:00:00 +0800 Subject: [PATCH 22/27] fix test --- tests/SwooleLoggerTest.php | 100 +++++++++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 10 deletions(-) diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 160f603..ce50d48 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -69,7 +69,15 @@ public function testGetRequestID() public function testEmergency() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); @@ -81,7 +89,15 @@ public function testEmergency() public function testAlert() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); @@ -93,7 +109,15 @@ public function testAlert() public function testCritical() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); @@ -105,7 +129,15 @@ public function testCritical() public function testError() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->error('[LoggerConfig Test]', ['level' => 'error']); @@ -117,7 +149,15 @@ public function testError() public function testWarning() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); @@ -129,7 +169,15 @@ public function testWarning() public function testNotice() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); @@ -141,7 +189,15 @@ public function testNotice() public function testInfo() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->info('[LoggerConfig Test]', ['level' => 'info']); @@ -153,7 +209,15 @@ public function testInfo() public function testDebug() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); @@ -165,7 +229,15 @@ public function testDebug() public function testLog() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); @@ -228,7 +300,15 @@ public function testLogWithJsonTemplate() public function testRequestLevel() { - $logger = $this->init(); + $logger = new Logger( + new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->setRequestLevel(Logger::ALL); From ee00290479fb6b5bf781272b048e953b2fd41bfb Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Wed, 11 Sep 2019 12:12:03 +0800 Subject: [PATCH 23/27] php 7.2 give me pass --- tests/SwooleLoggerTest.php | 118 ++++++------------------------------- 1 file changed, 19 insertions(+), 99 deletions(-) diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index ce50d48..8e87db2 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -39,15 +39,15 @@ public function testGetBasePath() public function init(int $type = 0, int $bufferSize = 1) { $class = $type === 0 ? LoggerConfig::class : SeaslogConfig::class; - $logger = new Logger( - new $class([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => $bufferSize, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = new Logger(); + $logger->setConfig(new $class([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'bufferSize' => $bufferSize, + 'tick' => 0, + 'recall_depth' => 2, + ])); return $logger; } @@ -69,15 +69,7 @@ public function testGetRequestID() public function testEmergency() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->emergency('[LoggerConfig Test]', ['level' => 'emergency']); @@ -89,15 +81,7 @@ public function testEmergency() public function testAlert() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->alert('[LoggerConfig Test]', ['level' => 'alert']); @@ -109,15 +93,7 @@ public function testAlert() public function testCritical() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->critical('[LoggerConfig Test]', ['level' => 'critical']); @@ -129,15 +105,7 @@ public function testCritical() public function testError() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->error('[LoggerConfig Test]', ['level' => 'error']); @@ -149,15 +117,7 @@ public function testError() public function testWarning() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->warning('[LoggerConfig Test]', ['level' => 'warning']); @@ -169,15 +129,7 @@ public function testWarning() public function testNotice() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->notice('[LoggerConfig Test]', ['level' => 'notice']); @@ -189,15 +141,7 @@ public function testNotice() public function testInfo() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->info('[LoggerConfig Test]', ['level' => 'info']); @@ -209,15 +153,7 @@ public function testInfo() public function testDebug() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->debug('[LoggerConfig Test]', ['level' => 'debug']); @@ -229,15 +165,7 @@ public function testDebug() public function testLog() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::DEBUG, '[LoggerConfig Test]', ['level' => 'log']); @@ -300,15 +228,7 @@ public function testLogWithJsonTemplate() public function testRequestLevel() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->setRequestLevel(Logger::ALL); From ec2d3a793378e35bb7f8042d41245306374ee216 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Wed, 11 Sep 2019 12:18:01 +0800 Subject: [PATCH 24/27] fix test php 7.2 give me pass --- src/Logger.php | 2 +- tests/SwooleLoggerTest.php | 30 +++++++++++------------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/Logger.php b/src/Logger.php index 32bb6a8..d49eb48 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -97,7 +97,7 @@ class Logger implements LoggerInterface * Logger constructor. * @param AbstractConfig|null $config */ - public function __construct(?AbstractConfig $config = null) + public function __construct(AbstractConfig $config = null) { if ($config !== null && !extension_loaded('swoole')) { throw new NotSupportedException("This usage must have swoole version>=4"); diff --git a/tests/SwooleLoggerTest.php b/tests/SwooleLoggerTest.php index 8e87db2..a37352f 100644 --- a/tests/SwooleLoggerTest.php +++ b/tests/SwooleLoggerTest.php @@ -177,15 +177,7 @@ public function testLog() public function testLogWithFieldTemplate() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = $this->init(); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::INFO, '[LoggerConfig FieldTemplate]', ['level' => 'log', 'template' => ['test']]); @@ -197,16 +189,16 @@ public function testLogWithFieldTemplate() public function testLogWithJsonTemplate() { - $logger = new Logger( - new LoggerConfig([ - 'echo' => new StyleTarget() - ], [ - 'appName' => 'Seaslog', - 'customerType' => AbstractConfig::TYPE_JSON, - 'bufferSize' => 1, - 'tick' => 0, - 'recall_depth' => 2, - ])); + $logger = new Logger(); + $logger->setConfig(new LoggerConfig([ + 'echo' => new StyleTarget() + ], [ + 'appName' => 'Seaslog', + 'customerType' => AbstractConfig::TYPE_JSON, + 'bufferSize' => 1, + 'tick' => 0, + 'recall_depth' => 2, + ])); $this->assertInstanceOf(Logger::class, $logger); $this->assertInstanceOf(LoggerConfig::class, $logger->getConfig()); $logger->log(Logger::INFO, '[LoggerConfig JsonTemplate]', From 67118617a096d5d85dec48c7256cd06a6e60cff8 Mon Sep 17 00:00:00 2001 From: albert <63851587@qq.com> Date: Thu, 12 Sep 2019 13:38:45 +0800 Subject: [PATCH 25/27] fix php static scan use wujunze/php-cli-color replace Console color output --- composer.json | 3 +- example/logger.php | 42 +++---- src/ArrayHelper.php | 4 +- src/ConsoleColor.php | 190 -------------------------------- src/Context.php | 16 ++- src/HtmlColor.php | 6 +- src/Kafka/Config.php | 13 +-- src/Kafka/Produce.php | 44 ++++---- src/Kafka/Producter.php | 8 +- src/Kafka/ProducterConfig.php | 7 ++ src/Kafka/Protocol.php | 13 ++- src/Kafka/RecordValidator.php | 4 +- src/Kafka/Socket/Pool.php | 7 +- src/Kafka/Socket/SocketIO.php | 2 +- src/Logger.php | 8 +- src/LoggerConfig.php | 4 +- src/SeaslogConfig.php | 4 +- src/Targets/StyleTarget.php | 25 +++-- src/Targets/WebsocketTarget.php | 3 +- src/functions.php | 7 +- tests/ConsoleColorTest.php | 29 ----- 21 files changed, 108 insertions(+), 331 deletions(-) delete mode 100644 src/ConsoleColor.php delete mode 100644 tests/ConsoleColorTest.php diff --git a/composer.json b/composer.json index 6d1dcaa..e18d8f6 100644 --- a/composer.json +++ b/composer.json @@ -17,12 +17,13 @@ "require": { "php": "^7.1", "psr/log": "^1.0.2", + "wujunze/php-cli-color": "^2.4", "ext-seaslog": "^2.0", "lcobucci/clock": "^1.0", "friendsofphp/php-cs-fixer": "^2.11" }, "require-dev": { - "swoft/swoole-ide-helper": "^4.3", + "swoole/ide-helper": "@dev", "phpunit/phpunit": "^6.5" }, "autoload": { diff --git a/example/logger.php b/example/logger.php index 2bb5fd7..58bb7f4 100644 --- a/example/logger.php +++ b/example/logger.php @@ -2,8 +2,6 @@ require_once dirname(__DIR__) . '/vendor/autoload.php'; -use Seasx\SeasLogger\ArrayHelper; -use Seasx\SeasLogger\ConsoleColor; use Seasx\SeasLogger\Context; use Seasx\SeasLogger\HtmlColor; use Seasx\SeasLogger\Kafka\Broker; @@ -14,8 +12,9 @@ use Seasx\SeasLogger\LoggerConfig; use Seasx\SeasLogger\Targets\KafkaTarget; use Seasx\SeasLogger\Targets\StyleTarget; +use Wujunze\Colors; -\Co\Run(function () { +rgo(function () { $logger = new Logger( new LoggerConfig([ 'echo' => new StyleTarget([ @@ -46,33 +45,16 @@ * 下面是示例代码,具体的设置根据自己需要 */ $logger->getConfig()->registerTemplate(function () { - $possibleStyles = (new ConsoleColor())->getPossibleStyles(); + $possibleStyles = (new Colors())->getForegroundColors(); $htmlColors = HtmlColor::getPossibleColors(); if (($requestVar = Context::get(Logger::CONTEXT_KEY)) === null) { - /** @var Request $serverRequest */ - if (($serverRequest = Context::get('request')) !== null) { - $uri = $serverRequest->getUri(); - $requestId = $serverRequest->getAttribute(AttributeEnum::REQUESTID_ATTRIBUTE); - !$requestId && $requestId = uniqid(); - $requestVar = array_filter([ - '%Q' => $requestId, - '%R' => $uri->getPath(), - '%m' => $serverRequest->getMethod(), - '%I' => ArrayHelper::getValue($serverRequest->getServerParams(), 'remote_addr'), - '%c' => [ - $possibleStyles[rand(0, count($possibleStyles) - 1)], - $htmlColors[rand(0, count($htmlColors) - 1)] - ], - ]); - } else { - $requestVar = array_filter([ - '%Q' => uniqid(), - '%c' => [ - 'console' => $possibleStyles[rand(0, count($possibleStyles) - 1)], - 'websocket' => $htmlColors[rand(0, count($htmlColors) - 1)] - ] - ]); - } + $requestVar = array_filter([ + '%Q' => uniqid(), + '%c' => [ + 'console' => $possibleStyles[rand(0, count($possibleStyles) - 1)], + 'websocket' => $htmlColors[rand(0, count($htmlColors) - 1)] + ] + ]); $requestVar['%A'] = ['123', '456']; Context::set(Logger::CONTEXT_KEY, $requestVar); } @@ -85,4 +67,6 @@ for ($i = 0; $i < 1; $i++) { $logger->info("test logger $i", ['module' => 'logger', 'template' => ['abc', 'def']]); } -}); \ No newline at end of file +}); + +swoole_event_wait(); \ No newline at end of file diff --git a/src/ArrayHelper.php b/src/ArrayHelper.php index 8bbadce..5c07d84 100644 --- a/src/ArrayHelper.php +++ b/src/ArrayHelper.php @@ -14,7 +14,7 @@ class ArrayHelper /** * @param $array * @param $key - * @param null $default + * @param mixed|null $default * @return mixed|null */ public static function getValue($array, $key, $default = null) @@ -54,7 +54,7 @@ public static function getValue($array, $key, $default = null) /** * @param $array * @param $key - * @param null $default + * @param mixed|null $default * @return mixed|null */ public static function remove(&$array, $key, $default = null) diff --git a/src/ConsoleColor.php b/src/ConsoleColor.php deleted file mode 100644 index a89c799..0000000 --- a/src/ConsoleColor.php +++ /dev/null @@ -1,190 +0,0 @@ - null, - 'bold' => '1', - 'dark' => '2', - 'italic' => '3', - 'underline' => '4', - 'blink' => '5', - 'reverse' => '7', - 'concealed' => '8', - 'default' => '39', - 'black' => '30', - 'red' => '31', - 'green' => '32', - 'yellow' => '33', - 'blue' => '34', - 'magenta' => '35', - 'cyan' => '36', - 'light_gray' => '37', - 'dark_gray' => '90', - 'light_red' => '91', - 'light_green' => '92', - 'light_yellow' => '93', - 'light_blue' => '94', - 'light_magenta' => '95', - 'light_cyan' => '96', - 'white' => '97', - 'bg_default' => '49', - 'bg_black' => '40', - 'bg_red' => '41', - 'bg_green' => '42', - 'bg_yellow' => '43', - 'bg_blue' => '44', - 'bg_magenta' => '45', - 'bg_cyan' => '46', - 'bg_light_gray' => '47', - 'bg_dark_gray' => '100', - 'bg_light_red' => '101', - 'bg_light_green' => '102', - 'bg_light_yellow' => '103', - 'bg_light_blue' => '104', - 'bg_light_magenta' => '105', - 'bg_light_cyan' => '106', - 'bg_white' => '107', - ); - - /** - * ConsoleColor constructor. - * @param bool $forceStyle - */ - public function __construct(bool $forceStyle = true) - { - $this->forceStyle = $forceStyle; - $this->isSupported = $this->isSupported(); - } - - /** - * @return bool - */ - public function isSupported(): bool - { - if (DIRECTORY_SEPARATOR === '\\') { - if (function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT)) { - return true; - } elseif (getenv('ANSICON') !== false || getenv('ConEmuANSI') === 'ON') { - return true; - } - return false; - } else { - return function_exists('posix_isatty') && @posix_isatty(STDOUT); - } - } - - /** - * @param string $style - * @param string $text - * @return string - */ - public function apply(string $style, string $text): string - { - if (empty($style)) { - return $text; - } - if (!$this->isStyleForced() && !$this->isSupported()) { - return $text; - } - if ($this->isValidStyle($style)) { - $sequences = $this->styleSequence($style); - } else { - throw new InvalidArgumentException($style); - } - if (empty($sequences)) { - return $text; - } - return $this->escSequence($sequences) . $text . $this->escSequence((string)self::RESET_STYLE); - } - - /** - * @return bool - */ - public function isStyleForced(): bool - { - return $this->forceStyle; - } - - /** - * @param string $style - * @return bool - */ - private function isValidStyle(string $style): bool - { - return array_key_exists($style, $this->styles) || preg_match(self::COLOR256_REGEXP, $style); - } - - /** - * @param string $style - * @return string - */ - private function styleSequence(string $style): ?string - { - if (array_key_exists($style, $this->styles)) { - return $this->styles[$style]; - } - if (!$this->are256ColorsSupported()) { - return null; - } - preg_match(self::COLOR256_REGEXP, $style, $matches); - $type = $matches[1] === 'bg_' ? self::BACKGROUND : self::FOREGROUND; - $value = $matches[2]; - return "$type;5;$value"; - } - - /** - * @return bool - */ - public function are256ColorsSupported(): bool - { - if (DIRECTORY_SEPARATOR === '\\') { - return function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(STDOUT); - } else { - return strpos(getenv('TERM'), '256color') !== false; - } - } - - /** - * @param string|int $value - * @return string - */ - private function escSequence(string $value): string - { - return "\033[{$value}m"; - } - - /** - * @param bool $forceStyle - */ - public function setForceStyle(bool $forceStyle) - { - $this->forceStyle = (bool)$forceStyle; - } - - /** - * @return array - */ - public function getPossibleStyles(): array - { - return array_keys($this->styles); - } -} \ No newline at end of file diff --git a/src/Context.php b/src/Context.php index 408e12b..6b113ac 100644 --- a/src/Context.php +++ b/src/Context.php @@ -18,7 +18,9 @@ class Context */ public static function set(string $name, $value): void { - Co::getContext()[$name] = $value; + /** @var \ArrayObject $context */ + $context = Co::getContext(); + $context[$name] = $value; } /** @@ -27,7 +29,9 @@ public static function set(string $name, $value): void */ public static function get(string $name) { - return isset(Co::getContext()[$name]) ? Co::getContext()[$name] : null; + /** @var \ArrayObject $context */ + $context = Co::getContext(); + return isset($context[$name]) ? $context[$name] : null; } /** @@ -36,7 +40,9 @@ public static function get(string $name) */ public static function has(string $name): bool { - return isset(Co::getContext()[$name]); + /** @var \ArrayObject $context */ + $context = Co::getContext(); + return isset($context[$name]); } /** @@ -44,6 +50,8 @@ public static function has(string $name): bool */ public static function delete(string $name): void { - unset(Co::getContext()[$name]); + /** @var \ArrayObject $context */ + $context = Co::getContext(); + unset($context[$name]); } } \ No newline at end of file diff --git a/src/HtmlColor.php b/src/HtmlColor.php index 1077925..db46f05 100644 --- a/src/HtmlColor.php +++ b/src/HtmlColor.php @@ -9,7 +9,7 @@ */ class HtmlColor { - private static $colors = array( + protected static $colors = array( 'AliceBlue ' => '#F0F8FF', 'AntiqueWhite' => '#FAEBD7', 'Aqua' => '#00FFFF', @@ -165,7 +165,7 @@ public static function getColor(string $color): string */ public static function getPossibleColors(): array { - return array_keys(self::$colors); + return array_keys(static::$colors); } /** @@ -173,6 +173,6 @@ public static function getPossibleColors(): array */ public static function getPossibleColorsRGB(): array { - return array_values(self::$colors); + return array_values(static::$colors); } } \ No newline at end of file diff --git a/src/Kafka/Config.php b/src/Kafka/Config.php index 35ee954..8c1787e 100644 --- a/src/Kafka/Config.php +++ b/src/Kafka/Config.php @@ -43,14 +43,7 @@ abstract class Config /** * @var mixed[] */ - private static $defaults = [ - 'clientId' => 'seaslog-kafka', - 'brokerVersion' => '0.10.1.0', - 'metadataBrokerList' => '', - 'messageMaxBytes' => 1000000, - 'metadataRequestTimeoutMs' => 60000, - 'metadataRefreshIntervalMs' => 300000, - 'metadataMaxAgeMs' => -1 + protected static $defaults = [ ]; /** * @var mixed[] @@ -91,10 +84,6 @@ public function __call(string $name, array $args) return $this->options[$option]; } - if (isset(self::$defaults[$option])) { - return self::$defaults[$option]; - } - if (isset(static::$defaults[$option])) { return static::$defaults[$option]; } diff --git a/src/Kafka/Produce.php b/src/Kafka/Produce.php index 21194a9..a4e32b9 100644 --- a/src/Kafka/Produce.php +++ b/src/Kafka/Produce.php @@ -40,10 +40,8 @@ public function __construct(string $version = self::DEFAULT_BROKER_VERION, ?Cloc } /** - * @param mixed[] $payloads - * - * @throws NotSupported - * @throws ProtocolException + * @param array $payloads + * @return string */ public function encode(array $payloads = []): string { @@ -64,9 +62,9 @@ public function encode(array $payloads = []): string } /** - * @return mixed[] - * - * @throws ProtocolException + * @param string $data + * @return array + * @throws \Exception */ public function decode(string $data): array { @@ -88,17 +86,17 @@ public function decode(string $data): array * * @param mixed[] $values * - * @throws NotSupported - * @throws ProtocolException + * @param int $compression + * @return string */ protected function encodeProducePartition(array $values, int $compression): string { if (!isset($values['partition_id'])) { - throw new ProtocolException('given produce data invalid. `partition_id` is undefined.'); + throw new InvalidArgumentException('given produce data invalid. `partition_id` is undefined.'); } if (!isset($values['messages']) || empty($values['messages'])) { - throw new ProtocolException('given produce data invalid. `messages` is undefined.'); + throw new InvalidArgumentException('given produce data invalid. `messages` is undefined.'); } $data = self::pack(self::BIT_B32, (string)$values['partition_id']); @@ -117,7 +115,8 @@ protected function encodeProducePartition(array $values, int $compression): stri * * @param string[]|string[][] $messages * - * @throws NotSupported + * @param int $compression + * @return string */ protected function encodeMessageSet(array $messages, int $compression = self::COMPRESSION_NONE): string { @@ -144,7 +143,8 @@ protected function encodeMessageSet(array $messages, int $compression = self::CO /** * @param string[]|string $message * - * @throws NotSupported + * @param int $compression + * @return string */ protected function encodeMessage($message, int $compression = self::COMPRESSION_NONE): string { @@ -221,17 +221,17 @@ public function computeTimestampType(int $magic): int * * @param mixed[] $values * - * @throws NotSupported - * @throws ProtocolException + * @param int $compression + * @return string */ protected function encodeProduceTopic(array $values, int $compression): string { if (!isset($values['topic_name'])) { - throw new ProtocolException('given produce data invalid. `topic_name` is undefined.'); + throw new InvalidArgumentException('given produce data invalid. `topic_name` is undefined.'); } if (!isset($values['partitions']) || empty($values['partitions'])) { - throw new ProtocolException('given produce data invalid. `partitions` is undefined.'); + throw new InvalidArgumentException('given produce data invalid. `partitions` is undefined.'); } $topic = self::encodeString($values['topic_name'], self::PACK_INT16); @@ -243,9 +243,11 @@ protected function encodeProduceTopic(array $values, int $compression): string /** * decode produce topic pair response * + * @param string $data + * @param int $version * @return mixed[] * - * @throws ProtocolException + * @throws \Exception */ protected function produceTopicPair(string $data, int $version): array { @@ -267,9 +269,11 @@ protected function produceTopicPair(string $data, int $version): array /** * decode produce partition pair response * + * @param string $data + * @param int $version * @return mixed[] * - * @throws ProtocolException + * @throws \Exception */ protected function producePartitionPair(string $data, int $version): array { @@ -278,7 +282,7 @@ protected function producePartitionPair(string $data, int $version): array $offset += 4; $errorCode = self::unpack(self::BIT_B16_SIGNED, substr($data, $offset, 2)); $offset += 2; - $partitionOffset = self::unpack(self::BIT_B64, substr($data, $offset, 8)); + self::unpack(self::BIT_B64, substr($data, $offset, 8)); $offset += 8; $timestamp = 0; diff --git a/src/Kafka/Producter.php b/src/Kafka/Producter.php index 820a11e..20f0d16 100644 --- a/src/Kafka/Producter.php +++ b/src/Kafka/Producter.php @@ -74,9 +74,9 @@ public function send(array $recordSet, ?callable $callback = null): void if ($requiredAck !== 0) { $connect->send($requestData); $dataLen = Protocol::unpack(Protocol::BIT_B32, $connect->recv(4)); - $recordSet = $connect->recv($dataLen); + $recordSet = $connect->recv((int)$dataLen); $this->pool->release($connect); - $correlationId = Protocol::unpack(Protocol::BIT_B32, substr($recordSet, 0, 4)); + Protocol::unpack(Protocol::BIT_B32, substr($recordSet, 0, 4)); $callback && $callback(ProtocolTool::decode(ProtocolTool::PRODUCE_REQUEST, substr($recordSet, 4))); } else { @@ -100,8 +100,8 @@ public function syncMeta(): void $requestData = ProtocolTool::encode(ProtocolTool::METADATA_REQUEST, $params); $socket->send($requestData); $dataLen = Protocol::unpack(Protocol::BIT_B32, $socket->recv(4)); - $data = $socket->recv($dataLen); - $correlationId = Protocol::unpack(Protocol::BIT_B32, substr($data, 0, 4)); + $data = $socket->recv((int)$dataLen); + Protocol::unpack(Protocol::BIT_B32, substr($data, 0, 4)); $result = ProtocolTool::decode(ProtocolTool::METADATA_REQUEST, substr($data, 4)); if (!isset($result['brokers'], $result['topics'])) { throw new Exception('Get metadata is fail, brokers or topics is null.'); diff --git a/src/Kafka/ProducterConfig.php b/src/Kafka/ProducterConfig.php index f178b0d..adeb0ca 100644 --- a/src/Kafka/ProducterConfig.php +++ b/src/Kafka/ProducterConfig.php @@ -27,6 +27,13 @@ class ProducterConfig extends Config * @var mixed[] */ protected static $defaults = [ + 'clientId' => 'seaslog-kafka', + 'brokerVersion' => '0.10.1.0', + 'metadataBrokerList' => '', + 'messageMaxBytes' => 1000000, + 'metadataRequestTimeoutMs' => 60000, + 'metadataRefreshIntervalMs' => 300000, + 'metadataMaxAgeMs' => -1, 'requiredAck' => 1, 'timeout' => 5000, 'requestTimeout' => 6000, diff --git a/src/Kafka/Protocol.php b/src/Kafka/Protocol.php index e76c92a..168e82d 100644 --- a/src/Kafka/Protocol.php +++ b/src/Kafka/Protocol.php @@ -272,7 +272,7 @@ public function decodeString(string $data, string $bytes, int $compression = sel return ['length' => $offset, 'data' => '']; } - $data = (string)substr($data, $offset, $packLen); + $data = (string)substr($data, $offset, (int)$packLen); $offset += $packLen; return ['length' => $offset, 'data' => self::decompress($data, $compression)]; @@ -301,7 +301,7 @@ public static function unpack(string $type, string $bytes) // But if our system is little endian if (self::isSystemLittleEndian()) { // We need to flip the endianess because coming from kafka it is big endian - $set = self::convertSignedShortFromLittleEndianToBigEndian($set); + $set = self::convertSignedShortFromLittleEndianToBigEndian(/** @scrutinizer ignore-type */ $set); } $result = $set; } else { @@ -352,9 +352,14 @@ public static function isSystemLittleEndian(): bool { // If we don't know if our system is big endian or not yet... if (self::$isLittleEndianSystem === null) { - [$endianTest] = array_values(unpack('L1L', pack('V', 1))); + $value = unpack('L1L', pack('V', 1)); + if ($value === false) { + self::$isLittleEndianSystem = false; + } else { + [$endianTest] = array_values($value); - self::$isLittleEndianSystem = (int)$endianTest === 1; + self::$isLittleEndianSystem = (int)$endianTest === 1; + } } return self::$isLittleEndianSystem; diff --git a/src/Kafka/RecordValidator.php b/src/Kafka/RecordValidator.php index 58f4f2c..f70719b 100644 --- a/src/Kafka/RecordValidator.php +++ b/src/Kafka/RecordValidator.php @@ -12,8 +12,8 @@ final class RecordValidator { /** - * @param string[] $record - * @param mixed[][] $topicList + * @param array $record + * @param array $topicList * @throws InvalidRecordInSet */ public function validate(array $record, array $topicList): void diff --git a/src/Kafka/Socket/Pool.php b/src/Kafka/Socket/Pool.php index 1c41bb9..2edaad0 100644 --- a/src/Kafka/Socket/Pool.php +++ b/src/Kafka/Socket/Pool.php @@ -77,11 +77,8 @@ public function getConnection(): SocketIO if ($this->maxWait > 0 && $this->waitStack->count() > $this->maxWait) { throw new Exception('Connection pool queue is full'); } - $this->waitStack->push(Co::getCid()); - if (Co::suspend() == false) { - $this->waitStack->pop(); - throw new Exception('Reach max connections! Can not pending fetch!'); - } + $this->waitStack->push((int)Co::getCid()); + Co::yield(); return $this->queue->shift(); } diff --git a/src/Kafka/Socket/SocketIO.php b/src/Kafka/Socket/SocketIO.php index 9a499b0..edc7638 100644 --- a/src/Kafka/Socket/SocketIO.php +++ b/src/Kafka/Socket/SocketIO.php @@ -31,7 +31,7 @@ public function send(string $data, float $timeout = -1): int $ln = strlen($data); while ($data && $ln > 0) { $result = $this->connection->sendAll($data, $timeout); - if ($result === false) { + if (!is_int($result)) { $this->reconnect(); } $data = substr($data, $result); diff --git a/src/Logger.php b/src/Logger.php index d49eb48..bd54a13 100644 --- a/src/Logger.php +++ b/src/Logger.php @@ -347,13 +347,7 @@ public function log($level, $message, array $context = array()) $levelFunction = strtolower(self::$levels[$level]); SeasLog::$levelFunction($message, $context); } else { - if (is_string($message)) { - $this->config->log(self::$levels[$level], $message, $context); - } elseif (is_array($message)) { - foreach ($message as $m) { - $this->config->log(self::$levels[$level], $m, $context); - } - } + $this->config->log(self::$levels[$level], $message, $context); } } diff --git a/src/LoggerConfig.php b/src/LoggerConfig.php index 6bd1d9f..c03b965 100644 --- a/src/LoggerConfig.php +++ b/src/LoggerConfig.php @@ -120,7 +120,7 @@ public function log(string $level, string $message, array $context = []): void if ($this->isMicroTime > 0) { $micsec = $this->isMicroTime > 3 ? 3 : $this->isMicroTime; $mtimestamp = sprintf("%.{$micsec}f", microtime(true)); // 带毫秒的时间戳 - $timestamp = floor($mtimestamp); // 时间戳 + $timestamp = floor(/** @scrutinizer ignore-type */ $mtimestamp); // 时间戳 $milliseconds = round(($mtimestamp - $timestamp) * 1000); // 毫秒 } else { $timestamp = time(); @@ -156,7 +156,7 @@ public function log(string $level, string $message, array $context = []): void break; case '%F': case '%C': - $trace = Co::getBackTrace(Co::getCid(), DEBUG_BACKTRACE_IGNORE_ARGS, + $trace = Co::getBackTrace(Co::getCid(), /** @scrutinizer ignore-type */ DEBUG_BACKTRACE_IGNORE_ARGS, $this->recall_depth + 2); if ($tmp === '%F') { $trace = $trace[$this->recall_depth]; diff --git a/src/SeaslogConfig.php b/src/SeaslogConfig.php index 984e7b8..02c1181 100644 --- a/src/SeaslogConfig.php +++ b/src/SeaslogConfig.php @@ -88,10 +88,10 @@ public function log(string $level, string $message, array $context = []): void public function flush(bool $flush = false): void { if (method_exists('Seaslog', 'getBufferCount')) { - if (($flush || ($total = Seaslog::getBufferCount()) >= $this->bufferSize) && ($buffer = Seaslog::getBuffer()) !== false) { + if (($flush || Seaslog::getBufferCount() >= $this->bufferSize) && ($buffer = Seaslog::getBuffer()) !== false) { Seaslog::flushBuffer(0); foreach ($this->targetList as $index => $target) { - rgo(function () use ($target, $buffer, $flush) { + rgo(function () use ($target, $buffer) { $target->export($buffer); }); } diff --git a/src/Targets/StyleTarget.php b/src/Targets/StyleTarget.php index a7ef6f1..7a06431 100644 --- a/src/Targets/StyleTarget.php +++ b/src/Targets/StyleTarget.php @@ -5,7 +5,7 @@ use Psr\Log\LogLevel; use Seasx\SeasLogger\ArrayHelper; -use Seasx\SeasLogger\ConsoleColor; +use Wujunze\Colors; /** * Class StyleTarget @@ -16,7 +16,7 @@ class StyleTarget extends AbstractTarget const COLOR_RANDOM = 'random'; const COLOR_DEFAULT = 'default'; const COLOR_LEVEL = 'level'; - /** @var ConsoleColor */ + /** @var Colors */ private $color; /** @var array */ private $colorTemplate = [ @@ -30,19 +30,19 @@ class StyleTarget extends AbstractTarget 'dark_gray', self::COLOR_LEVEL ]; - private $default = 'none'; + private $default = null; /** @var string */ private $splitColor = 'cyan'; /** * StyleTarget constructor. * @param array $levelList - * @param ConsoleColor|null $color + * @param Colors|null $color */ - public function __construct(array $levelList = [], ?ConsoleColor $color = null) + public function __construct(array $levelList = [], ?Colors $color = null) { if ($color === null) { - $color = new ConsoleColor(); + $color = new Colors(); } $this->color = $color; $this->levelList = $levelList; @@ -85,23 +85,24 @@ public function export(array $messages): void $msgValue = is_string($msgValue) ? trim($msgValue) : (string)$msgValue; switch ($color) { case self::COLOR_LEVEL: - $context[] = $this->color->apply($level, $msgValue); + $context[] = $this->color->getColoredString($msgValue, $level); break; case self::COLOR_DEFAULT: - $context[] = $this->color->apply($this->default, $msgValue); + $context[] = $this->color->getColoredString($msgValue, $this->default); break; case self::COLOR_RANDOM: - $context[] = $this->color->apply($ranColor, $msgValue); + $context[] = $this->color->getColoredString($msgValue, $ranColor); break; default: - $context[] = $this->color->apply($color, $msgValue); + $context[] = $this->color->getColoredString($msgValue, $color); } } else { - $context[] = $this->color->apply($level, $msgValue); + $context[] = $this->color->getColoredString($msgValue, $level); } } if (isset($context)) { - echo implode(' ' . $this->color->apply($this->splitColor, '|') . ' ', $context) . PHP_EOL; + echo implode(' ' . $this->color->getColoredString('|', $this->splitColor) . ' ', + $context) . PHP_EOL; } } } diff --git a/src/Targets/WebsocketTarget.php b/src/Targets/WebsocketTarget.php index 7546653..316bc7c 100644 --- a/src/Targets/WebsocketTarget.php +++ b/src/Targets/WebsocketTarget.php @@ -7,7 +7,7 @@ use Psr\Log\LogLevel; use Seasx\SeasLogger\ArrayHelper; use Seasx\SeasLogger\HtmlColor; -use Swoole\Server; +use Swoole\WebSocket\Server; /** * Class WebsocketTarget @@ -86,6 +86,7 @@ public function export(array $messages): void } else { $ranColor = $this->default; } + $colors = []; foreach ($msg as $index => $msgValue) { $msg[$index] = is_string($msgValue) ? trim($msgValue) : (string)$msgValue; $level = $this->getLevelColor(trim($msg[$this->levelIndex])); diff --git a/src/functions.php b/src/functions.php index 82ba0e9..4b6bc27 100644 --- a/src/functions.php +++ b/src/functions.php @@ -10,16 +10,21 @@ */ function rgo(Closure $function, ?Closure $defer = null): int { - return go(function () use ($function, $defer) { + $cid = go(function () use ($function, $defer): int { try { if (is_callable($defer)) { defer($defer); } $function(); + return 1; } catch (Throwable $throwable) { print_r($throwable->getTraceAsString()); return 0; } }); + if (is_int($cid)) { + return $cid; + } + return 0; } } \ No newline at end of file diff --git a/tests/ConsoleColorTest.php b/tests/ConsoleColorTest.php deleted file mode 100644 index 325758a..0000000 --- a/tests/ConsoleColorTest.php +++ /dev/null @@ -1,29 +0,0 @@ -assertEquals('[none test]', $color->apply('none', '[none test]')); - $this->assertEquals('[green test]', $color->apply('green', '[green test]')); - } - - public function testSetForceStyle() - { - $color = new ConsoleColor(); - $color->setForceStyle(true); - $this->assertTrue($color->isStyleForced()); - $color->setForceStyle(false); - $this->assertFalse($color->isStyleForced()); - } -} \ No newline at end of file From 68bd4a8d8c24af940e380116c1762a19c360c8c6 Mon Sep 17 00:00:00 2001 From: wujunze Date: Thu, 12 Sep 2019 18:29:06 +0800 Subject: [PATCH 26/27] fix .scrutinizer.yml --- .scrutinizer.yml | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 1e294e5..3795bf1 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,15 +1,22 @@ checks: - php: - code_rating: true - duplication: true + php: true filter: excluded_paths: - - tests/* + - 'tests/*' build: - tests: - override: - - - command: ./vendor/bin/phpunit --coverage-clover=clover.xml - coverage: - file: clover.xml - format: php-clover + environment: + php: + version: 5.6 + ini: + 'date.timezone': 'Asia/Shanghai' + dependencies: + before: + - pecl install swoole-4.4.5 + - composer install + nodes: + analysis: + project_setup: + override: true + tests: + override: + - php-scrutinizer-run --enable-security-analysis \ No newline at end of file From 85746bf39e118e9cd4937e4e446e8e5b4a8b3e7e Mon Sep 17 00:00:00 2001 From: wujunze Date: Thu, 12 Sep 2019 18:30:44 +0800 Subject: [PATCH 27/27] php version 7.2 --- .scrutinizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 3795bf1..388da64 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -6,7 +6,7 @@ filter: build: environment: php: - version: 5.6 + version: 7.2 ini: 'date.timezone': 'Asia/Shanghai' dependencies: