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

Merge changes from 2.x #23

Merged
merged 7 commits into from
Jun 27, 2024
Merged
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
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
push:
branches:
- master
- 2.x

jobs:
build:
Expand Down
6 changes: 3 additions & 3 deletions example-project/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Now it’s time to create a `Logger` instance and push a `LogtailHandler` handle

```php
$logger = new Logger("logtail-source");
$logger->pushHandler(new LogtailHandler("<source-token>"));
$logger->pushHandler(LogtailHandlerBuilder::withSourceToken("<source-token>")->build());
```

Don’t forget to change `<source-token>` to your actual token which you can find in the *Basic information* section when clicking on *Edit* on your select source.
Expand All @@ -69,11 +69,11 @@ Creating multiple loggers for different channels is fairly easy:
```php
# Logger for shopping cart component
$cart_logger = new Logger("shoping-cart");
$cart_logger->pushHandler(new LogtailHandler("<source-token>"));
$cart_logger->pushHandler(LogtailHandlerBuilder::withSourceToken("<source-token>")->build());

# Logger for payment component
$payment_logger = new Logger("payment");
$payment_logger->pushHandler(new LogtailHandler("<source-token>"));
$payment_logger->pushHandler(LogtailHandlerBuilder::withSourceToken("<source-token>")->build());
```

Then you can filter your logs using the following search formula:
Expand Down
9 changes: 7 additions & 2 deletions example-project/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Setting logger
use Monolog\Logger;
use Logtail\Monolog\LogtailHandler;
use Logtail\Monolog\LogtailHandlerBuilder;

# Check for arguments
if($argc != 2){
Expand All @@ -17,7 +17,12 @@
}

$logger = new Logger("logtail-source");
$logger->pushHandler(new LogtailHandler($argv[1]));
$handler = LogtailHandlerBuilder::withSourceToken($argv[1])
->withBufferLimit(100)
->withFlushIntervalMilliseconds(500)
->withExceptionThrowing(true)
->build();
$logger->pushHandler($handler);

# Below you can see available methods that can be used to send logs to logtail.
# Each method corresponds to Monologs log level.
Expand Down
34 changes: 6 additions & 28 deletions src/Monolog/LogtailClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,10 @@ class LogtailClient
const DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS = 5000;
const DEFAULT_TIMEOUT_MILLISECONDS = 5000;

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

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

/**
* @var \CurlHandle $handle
*/
private $handle = NULL;

/**
* @var int $connectionTimeoutMs
*/
private string $sourceToken;
private string $endpoint;
private \CurlHandle $handle;
private int $connectionTimeoutMs;

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


Expand All @@ -63,9 +44,9 @@ public function __construct(
$this->timeoutMs = $timeoutMs;
}

public function send($data)
public function send($data): void
{
if (is_null($this->handle)) {
if (!isset($this->handle)) {
$this->initCurlHandle();
}

Expand All @@ -75,10 +56,7 @@ public function send($data)
\Monolog\Handler\Curl\Util::execute($this->handle, 5, false);
}

/**
* @return void
*/
private function initCurlHandle()
private function initCurlHandle(): void
{
$this->handle = \curl_init();

Expand Down
88 changes: 67 additions & 21 deletions src/Monolog/LogtailHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,84 @@

use Monolog\Handler\BufferHandler;
use Monolog\Level;
use Monolog\LogRecord;

/**
* Sends buffered logs to Logtail.
*/
class LogtailHandler extends BufferHandler
{
const DEFAULT_BUBBLE = true;
const DEFAULT_BUFFER_LIMIT = 1000;
const DEFAULT_FLUSH_ON_OVERFLOW = true;
const DEFAULT_FLUSH_INTERVAL_MILLISECONDS = 5000;

private ?int $flushIntervalMs;
private int|float|null $highResolutionTimeOfNextFlush;

/**
* @param string $sourceToken Logtail source token
* @param int|string $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $endpoint Logtail ingesting endpoint
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer.
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
* @param int $connectionTimeoutMs The maximum time in milliseconds that you allow the connection phase to the server to take
* @param int $timeoutMs The maximum time in milliseconds that you allow a transfer operation to take
* @param string $sourceToken Logtail source token
* @param int|string|Level $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
* @param string $endpoint Logtail ingesting endpoint
* @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer
* @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded
* @param int $connectionTimeoutMs The maximum time in milliseconds that you allow the connection phase to the server to take
* @param int $timeoutMs The maximum time in milliseconds that you allow a transfer operation to take
* @param int|null $flushIntervalMs The time in milliseconds after which next log record will trigger flushing all logs. Null to disable
* @param bool $throwExceptions Whether to throw exceptions when sending logs fails
*/
public function __construct(
$sourceToken,
$level = Level::Debug,
$bubble = true,
$endpoint = LogtailClient::URL,
$bufferLimit = 0,
bool $flushOnOverflow = false,
string $sourceToken,
int|string|Level $level = Level::Debug,
bool $bubble = self::DEFAULT_BUBBLE,
string $endpoint = LogtailClient::URL,
int $bufferLimit = self::DEFAULT_BUFFER_LIMIT,
bool $flushOnOverflow = self::DEFAULT_FLUSH_ON_OVERFLOW,
int $connectionTimeoutMs = LogtailClient::DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS,
int $timeoutMs = LogtailClient::DEFAULT_TIMEOUT_MILLISECONDS,
?int $flushIntervalMs = self::DEFAULT_FLUSH_INTERVAL_MILLISECONDS,
bool $throwExceptions = SynchronousLogtailHandler::DEFAULT_THROW_EXCEPTION
) {
parent::__construct(
new SynchronousLogtailHandler($sourceToken, $level, $bubble, $endpoint, $connectionTimeoutMs, $timeoutMs),
$bufferLimit,
$level,
$bubble,
$flushOnOverflow,
);
parent::__construct(new SynchronousLogtailHandler($sourceToken, $level, $bubble, $endpoint, $connectionTimeoutMs, $timeoutMs, $throwExceptions), $bufferLimit, $level, $bubble, $flushOnOverflow);
$this->flushIntervalMs = $flushIntervalMs;
$this->setHighResolutionTimeOfLastFlush();
}

/**
* @inheritDoc
*/
public function handle(LogRecord $record): bool
{
$return = parent::handle($record);

if ($this->highResolutionTimeOfNextFlush !== null && $this->highResolutionTimeOfNextFlush <= hrtime(true)) {
$this->flush();
$this->setHighResolutionTimeOfLastFlush();
}

return $return;
}

/**
* @inheritDoc
*/
public function flush(): void
{
parent::flush();
$this->setHighResolutionTimeOfLastFlush();
}

private function setHighResolutionTimeOfLastFlush(): void
{
$currentHighResolutionTime = hrtime(true);
if ($this->flushIntervalMs === null || $currentHighResolutionTime === false) {
$this->highResolutionTimeOfNextFlush = null;

return;
}

// hrtime(true) returns nanoseconds, converting flushIntervalMs from milliseconds to nanoseconds
$this->highResolutionTimeOfNextFlush = $currentHighResolutionTime + $this->flushIntervalMs * 1e+6;
}
}
180 changes: 180 additions & 0 deletions src/Monolog/LogtailHandlerBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php declare(strict_types=1);

/*
* This file is part of the logtail/monolog-logtail package.
*
* (c) Better Stack
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Logtail\Monolog;

use Monolog\Level;

final class LogtailHandlerBuilder
{
private string $sourceToken;
private Level $level = Level::Debug;
private bool $bubble = LogtailHandler::DEFAULT_BUBBLE;
private string $endpoint = LogtailClient::URL;
private int $bufferLimit = LogtailHandler::DEFAULT_BUFFER_LIMIT;
private bool $flushOnOverflow = LogtailHandler::DEFAULT_FLUSH_ON_OVERFLOW;
private int $connectionTimeoutMs = LogtailClient::DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS;
private int $timeoutMs = LogtailClient::DEFAULT_TIMEOUT_MILLISECONDS;
private ?int $flushIntervalMs = LogtailHandler::DEFAULT_FLUSH_INTERVAL_MILLISECONDS;
private bool $throwExceptions = SynchronousLogtailHandler::DEFAULT_THROW_EXCEPTION;

/**
* @internal use {@see self::withSourceToken()} instead
*/
private function __construct($sourceToken)
{
$this->sourceToken = $sourceToken;
}

/**
* Builder for comfortable creation of {@see LogtailHandler}.
*
* @var string $sourceToken Your Better Stack source token.
* @see https://logs.betterstack.com/team/0/sources
* @return self Always returns new immutable instance
*/
public static function withSourceToken(string $sourceToken): self
{
return new self($sourceToken);
}

/**
* Sets the minimum logging level at which this handler will be triggered.
*
* @param Level $level
* @return self Always returns new immutable instance
*/
public function withLevel(Level $level): self
{
$clone = clone $this;
$clone->level = $level;

return $clone;
}

/**
* Sets whether the messages that are handled can bubble up the stack or not.
*
* @param bool $bubble
* @return self Always returns new immutable instance
*/
public function withLogBubbling(bool $bubble): self
{
$clone = clone $this;
$clone->bubble = $bubble;

return $clone;
}

/**
* Sets how many entries should be buffered at most, beyond that the oldest items are flushed or removed from the buffer.
*
* @param int $bufferLimit
* @return self Always returns new immutable instance
*/
public function withBufferLimit(int $bufferLimit): self
{
$clone = clone $this;
$clone->bufferLimit = $bufferLimit;

return $clone;
}

/**
* Sets whether the buffer is flushed (true) or discarded (false) when the max size has been reached.
*
* @param bool $flushOnOverflow
* @return self Always returns new immutable instance
*/
public function withFlushOnOverflow(bool $flushOnOverflow): self
{
$clone = clone $this;
$clone->flushOnOverflow = $flushOnOverflow;

return $clone;
}

/**
* Sets the maximum time in milliseconds that you allow the connection phase to the server to take.
*
* @param int $connectionTimeoutMs
* @return self Always returns new immutable instance
*/
public function withConnectionTimeoutMilliseconds(int $connectionTimeoutMs): self
{
$clone = clone $this;
$clone->connectionTimeoutMs = $connectionTimeoutMs;

return $clone;
}

/**
* Sets the maximum time in milliseconds that you allow a transfer operation to take.
*
* @param int $timeoutMs
* @return self Always returns new immutable instance
*/
public function withTimeoutMilliseconds(int $timeoutMs): self
{
$clone = clone $this;
$clone->timeoutMs = $timeoutMs;

return $clone;
}

/**
* Set the time in milliseconds after which next log record will trigger flushing all logs. Null to disable.
*
* @param int|null $flushIntervalMs
* @return self Always returns new immutable instance
*/
public function withFlushIntervalMilliseconds(?int $flushIntervalMs): self
{
$clone = clone $this;
$clone->flushIntervalMs = $flushIntervalMs;

return $clone;
}

/**
* Sets whether to throw exceptions when sending logs fails.
*
* @param bool $throwExceptions
* @return self Always returns new immutable instance
*/
public function withExceptionThrowing(bool $throwExceptions): self
{
$clone = clone $this;
$clone->throwExceptions = $throwExceptions;

return $clone;
}

/**
* Builds the {@see LogtailHandler} instance based on the setting.
*
* @return LogtailHandler
*/
public function build(): LogtailHandler
{
return new LogtailHandler(
$this->sourceToken,
$this->level,
$this->bubble,
$this->endpoint,
$this->bufferLimit,
$this->flushOnOverflow,
$this->connectionTimeoutMs,
$this->timeoutMs,
$this->flushIntervalMs
);
}
}
Loading
Loading