Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP 8.1 and Monolog V3 #109

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0']
php-versions: ['8.1']
name: PHP ${{ matrix.php-versions }}
steps:
- name: Checkout
Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Check syntax
run: |
php -l src/
vendor/bin/phpcs --standard=psr2 --ignore=Tests src/
vendor/bin/phpcs --standard=psr12 src/ tests/

- name: Run tests
run: vendor/bin/phpunit --coverage-clover build/logs/clover.xml
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Please press **★ Star** button if you find this library useful.
This library uses AWS API through AWS PHP SDK, which has limits on concurrent requests. It means that on high concurrent or high load applications it may not work on it's best way. Please consider using another solution such as logging to the stdout and redirecting logs with fluentd.

## Requirements
* PHP ^7.3
* PHP >=8.1
* AWS account with proper permissions (see list of permissions below)

## Features
Expand All @@ -42,6 +42,7 @@ $ composer require maxbanton/cwh:^2.0
use Aws\CloudWatchLogs\CloudWatchLogsClient;
use Maxbanton\Cwh\Handler\CloudWatch;
use Monolog\Logger;
use Monolog\Level;
use Monolog\Formatter\JsonFormatter;

$sdkParams = [
Expand All @@ -67,7 +68,7 @@ $streamName = 'ec2-instance-1';
$retentionDays = 30;

// Instantiate handler (tags are optional)
$handler = new CloudWatch($client, $groupName, $streamName, $retentionDays, 10000, ['my-awesome-tag' => 'tag-value']);
$handler = new CloudWatch($client, $groupName, $streamName, $retentionDays, 10000, ['my-awesome-tag' => 'tag-value'], Level::Info);

// Optionally set the JsonFormatter to be able to access your log messages in a structured way
$handler->setFormatter(new JsonFormatter());
Expand Down
14 changes: 7 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@
"license": "MIT",
"authors": [
{
"name": "Max Leonov",
"name": "Maksym Leonov",
"email": "[email protected]",
"homepage": "https://maxleonov.pw"
"homepage": "https://github.com/maxbanton"
}
],
"support": {
"issues": "https://github.com/maxbanton/cwh/issues",
"source": "https://github.com/maxbanton/cwh"
},
"require": {
"php": "^7.2 || ^8",
"monolog/monolog": "^2.0",
"aws/aws-sdk-php": "^3.18"
"php": ">=8.1",
"monolog/monolog": "^3.0",
"aws/aws-sdk-php": "^3.2"
},
"require-dev": {
"phpunit/phpunit": "^8.5 || ^9.4",
"squizlabs/php_codesniffer": "^3.5"
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.7"
},
"suggest": {
"maxbanton/dd": "Minimalistic dump-and-die function for easy debugging"
Expand Down
161 changes: 34 additions & 127 deletions src/Handler/CloudWatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,139 +7,70 @@
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\AbstractProcessingHandler;
use Monolog\Logger;
use Monolog\LogRecord;
use Monolog\Level;

class CloudWatch extends AbstractProcessingHandler
{
/**
* Requests per second limit (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html)
*/
const RPS_LIMIT = 5;
public const RPS_LIMIT = 5;

/**
* Event size limit (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html)
*
* @var int
*/
const EVENT_SIZE_LIMIT = 262118; // 262144 - reserved 26
public const EVENT_SIZE_LIMIT = 262118; // 262144 - reserved 26

/**
* The batch of log events in a single PutLogEvents request cannot span more than 24 hours.
*
* @var int
*/
const TIMESPAN_LIMIT = 86400000;

/**
* @var CloudWatchLogsClient
*/
private $client;

/**
* @var string
*/
private $group;

/**
* @var string
*/
private $stream;

/**
* @var integer
*/
private $retention;

/**
* @var bool
*/
private $initialized = false;

/**
* @var string
*/
private $sequenceToken;

/**
* @var int
*/
private $batchSize;

/**
* @var array
*/
private $buffer = [];

/**
* @var array
*/
private $tags = [];

/**
* @var bool
*/
private $createGroup;
public const TIMESPAN_LIMIT = 86400000;

private CloudWatchLogsClient $client;
private string $group;
private string $stream;
private int $retention;
private bool $initialized = false;
private string | null $sequenceToken;
private int $batchSize;
/** @var LogRecord[] $buffer */
private array $buffer = [];
private array $tags = [];
private bool $createGroup;

/**
* Data amount limit (http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html)
*
* @var int
*/
private $dataAmountLimit = 1048576;

/**
* @var int
*/
private $currentDataAmount = 0;

/**
* @var int
*/
private $remainingRequests = self::RPS_LIMIT;

/**
* @var \DateTime
*/
private $savedTime;

/**
* @var int|null
*/
private $earliestTimestamp = null;
private int $dataAmountLimit = 1048576;
private int $currentDataAmount = 0;
private int $remainingRequests = self::RPS_LIMIT;
private \DateTime $savedTime;
private int | null $earliestTimestamp = null;

/**
* CloudWatchLogs constructor.
* @param CloudWatchLogsClient $client
*
* Log group names must be unique within a region for an AWS account.
* Log group names can be between 1 and 512 characters long.
* Log group names consist of the following characters: a-z, A-Z, 0-9, '_' (underscore), '-' (hyphen),
* '/' (forward slash), and '.' (period).
* @param string $group
*
* Log stream names must be unique within the log group.
* Log stream names can be between 1 and 512 characters long.
* The ':' (colon) and '*' (asterisk) characters are not allowed.
* @param string $stream
*
* @param int $retention
* @param int $batchSize
* @param array $tags
* @param int $level
* @param bool $bubble
* @param bool $createGroup
*
* @throws \Exception
*/
public function __construct(
CloudWatchLogsClient $client,
$group,
$stream,
$retention = 14,
$batchSize = 10000,
string $group,
string $stream,
int $retention = 14,
Copy link

@noahred16 noahred16 Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought retention could be null for infinite. So that'd be ?int

Copy link

@markinjapan markinjapan Nov 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@noahred16 Apologies but many months ago I forked this repository and applied the above PR, with additional fixes and improvements. It's available at phpnexus/cwh.

I intend to maintain my fork as I use CloudWatch Logs with PHP 8.1 at my workplace, but may I ask if you plan to take over maintenance of this original repository?

int $batchSize = 10000,
array $tags = [],
$level = Logger::DEBUG,
$bubble = true,
$createGroup = true
int | string | Level $level = Level::Debug,
bool $bubble = true,
bool $createGroup = true
) {
if ($batchSize > 10000) {
throw new \InvalidArgumentException('Batch size can not be greater than 10000');
Expand All @@ -155,13 +86,10 @@ public function __construct(

parent::__construct($level, $bubble);

$this->savedTime = new \DateTime;
$this->savedTime = new \DateTime();
}

/**
* {@inheritdoc}
*/
protected function write(array $record): void
protected function write(LogRecord $record): void
{
$records = $this->formatRecords($record);

Expand All @@ -178,9 +106,6 @@ protected function write(array $record): void
}
}

/**
* @param array $record
*/
private function addToBuffer(array $record): void
{
$this->currentDataAmount += $this->getMessageSize($record);
Expand Down Expand Up @@ -240,21 +165,15 @@ private function checkThrottle(): void

/**
* http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
*
* @param array $record
* @return int
*/
private function getMessageSize($record): int
private function getMessageSize(array $record): int
{
return strlen($record['message']) + 26;
}

/**
* Determine whether the specified record's message size in addition to the
* size of the current queued messages will exceed AWS CloudWatch's limit.
*
* @param array $record
* @return bool
*/
protected function willMessageSizeExceedLimit(array $record): bool
{
Expand All @@ -264,9 +183,6 @@ protected function willMessageSizeExceedLimit(array $record): bool
/**
* Determine whether the specified record's timestamp exceeds the 24 hour timespan limit
* for all batched messages written in a single call to PutLogEvents.
*
* @param array $record
* @return bool
*/
protected function willMessageTimestampExceedLimit(array $record): bool
{
Expand All @@ -276,11 +192,8 @@ protected function willMessageTimestampExceedLimit(array $record): bool
/**
* Event size in the batch can not be bigger than 256 KB
* https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html
*
* @param array $entry
* @return array
*/
private function formatRecords(array $entry): array
private function formatRecords(LogRecord $entry): array
{
$entries = str_split($entry['formatted'], self::EVENT_SIZE_LIMIT);
$timestamp = $entry['datetime']->format('U.u') * 1000;
Expand All @@ -307,7 +220,7 @@ private function formatRecords(array $entry): array
* - The maximum number of log events in a batch is 10,000.
* - A batch of log events in a single request cannot span more than 24 hours. Otherwise, the operation fails.
*
* @param array $entries
* @param LogRecord[] $entries
*
* @throws \Aws\CloudWatchLogs\Exception\CloudWatchLogsException Thrown by putLogEvents for example in case of an
* invalid sequence token
Expand Down Expand Up @@ -435,17 +348,11 @@ function ($stream) {
$this->initialized = true;
}

/**
* {@inheritdoc}
*/
protected function getDefaultFormatter(): FormatterInterface
{
return new LineFormatter("%channel%: %level_name%: %message% %context% %extra%", null, false, true);
}

/**
* {@inheritdoc}
*/
public function close(): void
{
$this->flushBuffer();
Expand Down
Loading