Skip to content
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
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Yii Logging Library Change Log

## 2.0.1 under development
## 2.1.0 under development

- Bug #84: Change the type of the `$level` parameter in the `Message` constructor to `string` (@dood-)
- New #104: Add new static methods `Logger::assertLevelIsValid()`, `Logger::assertLevelIsString()` and
Expand All @@ -9,6 +9,9 @@
- Bug #98: Fix error on formatting trace, when it doesn't contain "file" and "line" (@vjik)
- New #108: Support of nested values in message templates' variables, e. g. `{foo.bar}` (@vjik)
- Bug #89: Fix error on parse messages, that contains variables that cannot cast to a string (@vjik)
- New #109: Add context provider (@vjik)
- Chg #109: Deprecate `Logger` methods `setTraceLevel()` and `setExcludedTracePaths()` in favor of context provider
usage (@vjik)

## 2.0.0 May 22, 2022

Expand Down
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ $logger->info('Info message', ['key' => 'value']);
$logger->debug('Debug message', ['key' => 'value']);
```

## Message Flushing and Exporting
### Message Flushing and Exporting

Log messages are collected and stored in memory. To limit memory consumption, the logger will flush
the recorded messages to the log targets each time a certain number of log messages accumulate.
Expand All @@ -76,7 +76,7 @@ $target->setExportInterval(100); // default is 1000

> Note: All message flushing and exporting also occurs when the application ends.

## Logging targets
### Logging targets

This package contains two targets:

Expand All @@ -90,6 +90,42 @@ Extra logging targets are implemented as separate packages:
- [File](https://github.com/yiisoft/log-target-file)
- [Syslog](https://github.com/yiisoft/log-target-syslog)

### Context providers

Context providers are used to provide additional context data for log messages. You can define your own context provider
in `Logger` constructor:

```php
$logger = new \Yiisoft\Log\Logger(contextProvider: $myContextProvider);
```

By default, the logger uses built-in `Yiisoft\Log\ContextProvider\ContextProvider` that added following data to context:

- `time` — current Unix timestamp with microseconds (float value);
- `trace` — array of call stack information;
- `memory` — memory usage in bytes.
- `category` — category of the log message (always "application").

`Yiisoft\Log\ContextProvider\ContextProvider` constructor parameters:

- `traceLevel` — how much call stack information (file name and line number) should be logged for each
log message. If it is greater than 0, at most that number of call stacks will be logged. Note that only
application call stacks are counted.
- `excludedTracePaths` — array of paths to exclude from tracing when tracing is enabled with `traceLevel`.

Example of custom parameters' usage:

```php
$logger = new \Yiisoft\Log\Logger(
contextProvider: new Yiisoft\Log\ContextProvider\ContextProvider(
traceLevel: 3,
excludedTracePaths: [
'/vendor/yiisoft/di',
],
)
);
```

## Documentation

- [Yii guide to logging](https://github.com/yiisoft/docs/blob/master/guide/en/runtime/logging.md)
Expand Down
31 changes: 31 additions & 0 deletions src/ContextProvider/CompositeContextProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Log\ContextProvider;

/**
* `CompositeContextProvider` allows to combine multiple context providers into one.
*/
final class CompositeContextProvider implements ContextProviderInterface
{
/**
* @var ContextProviderInterface[]
*/
private array $providers;

public function __construct(
ContextProviderInterface ...$providers
) {
$this->providers = $providers;
}

public function getContext(): array
{
$contexts = [];
foreach ($this->providers as $provider) {
$contexts[] = $provider->getContext();
}
return array_merge(...$contexts);
}
}
133 changes: 133 additions & 0 deletions src/ContextProvider/ContextProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Log\ContextProvider;

use InvalidArgumentException;
use Yiisoft\Log\Message\CategoryFilter;

/**
* @psalm-type Backtrace = list<array{
* file:string,
* line:int,
* function?:string,
* class?:string,
* type?:string,
* }>
*/
final class ContextProvider implements ContextProviderInterface
{
/**
* @var string[] $excludedTracePaths Array of paths to exclude from tracing when tracing is enabled.
*/
private array $excludedTracePaths;

/**
* @param int $traceLevel How much call stack information (file name and line number) should be logged for each
* log message. If it is greater than 0, at most that number of call stacks will be logged. Note that only
* application call stacks are counted.
* @param string[] $excludedTracePaths Array of paths to exclude from tracing when tracing is enabled
* with {@see $traceLevel}.
*/
public function __construct(
private int $traceLevel = 0,
array $excludedTracePaths = [],
) {
/** @psalm-suppress DeprecatedMethod `setExcludedTracePaths` will be private and not deprecated */
$this->setExcludedTracePaths($excludedTracePaths);
}

public function getContext(): array
{
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
array_shift($trace);
return [
'time' => microtime(true),
'trace' => $this->collectTrace($trace),
'memory' => memory_get_usage(),
'category' => CategoryFilter::DEFAULT,
];
}

/**
* Sets how much call stack information (file name and line number) should be logged for each log message.
*
* @param int $traceLevel The number of call stack information.
*
* @see self::$traceLevel
*
* @deprecated since 2.1.0, to be removed in 3.0.0. Use constructor parameter "traceLevel" instead.
*/
public function setTraceLevel(int $traceLevel): self
{
$this->traceLevel = $traceLevel;
return $this;
}

/**
* Sets an array of paths to exclude from tracing when tracing is enabled with {@see self::$traceLevel}.
*
* @param string[] $excludedTracePaths The paths to exclude from tracing.
*
* @throws InvalidArgumentException for non-string values.
*
* @see self::$excludedTracePaths
*
* @deprecated since 2.1.0, to be removed in 3.0.0. Use constructor parameter "excludedTracePaths" instead.
*/
public function setExcludedTracePaths(array $excludedTracePaths): self
{
foreach ($excludedTracePaths as $excludedTracePath) {
/** @psalm-suppress DocblockTypeContradiction */
if (!is_string($excludedTracePath)) {
throw new InvalidArgumentException(
sprintf(
'The trace path must be a string, %s received.',
gettype($excludedTracePath)
)
);
}
}

$this->excludedTracePaths = $excludedTracePaths;
return $this;
}

/**
* Collects a trace when tracing is enabled with {@see Logger::setTraceLevel()}.
*
* @param array $backtrace The list of call stack information.
* @psalm-param Backtrace|list<array{object?:object,args?:array}> $backtrace
*
* @return array Collected a list of call stack information.
* @psalm-return Backtrace
*/
private function collectTrace(array $backtrace): array
{
$traces = [];

if ($this->traceLevel > 0) {
$count = 0;

foreach ($backtrace as $trace) {
if (isset($trace['file'], $trace['line'])) {
$excludedMatch = array_filter(
$this->excludedTracePaths,
static fn($path) => str_contains($trace['file'], $path)
);

if (empty($excludedMatch)) {
unset($trace['object'], $trace['args']);
$traces[] = $trace;
if (++$count >= $this->traceLevel) {
break;
}
}
}
}
}

return $traces;
}
}
13 changes: 13 additions & 0 deletions src/ContextProvider/ContextProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Yiisoft\Log\ContextProvider;

/**
* Context provider is used to add additional information to the log context.
*/
interface ContextProviderInterface
{
public function getContext(): array;
}
Loading