• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JBZoo / Cli / 7458612269

12 Dec 2023 10:40PM UTC coverage: 83.803% (-0.05%) from 83.848%
7458612269

push

github

web-flow
Feature flag to add `timestamp_real` (#23)

2 of 3 new or added lines in 1 file covered. (66.67%)

41 existing lines in 5 files now uncovered.

952 of 1136 relevant lines covered (83.8%)

138.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.43
/src/OutputMods/Logstash.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - Cli.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/Cli
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\Cli\OutputMods;
18

19
use JBZoo\Cli\CliApplication;
20
use JBZoo\Cli\CliHelper;
21
use JBZoo\Cli\OutLvl;
22
use JBZoo\Cli\ProgressBars\AbstractProgressBar;
23
use JBZoo\Cli\ProgressBars\ProgressBarLight;
24
use JBZoo\Utils\Slug;
25
use Monolog\DateTimeImmutable;
26
use Monolog\Formatter\LogstashFormatter;
27
use Monolog\Handler\StreamHandler;
28
use Monolog\Level;
29
use Monolog\Logger;
30
use Symfony\Component\Console\Input\InputInterface;
31
use Symfony\Component\Console\Output\OutputInterface;
32

33
use function JBZoo\Utils\bool;
34

35
class Logstash extends AbstractOutputMode
36
{
37
    private Logger $logger;
38

39
    public function __construct(InputInterface $input, OutputInterface $output, CliApplication $application)
40
    {
41
        $output->getFormatter()->setDecorated(false);
156✔
42

43
        $handler = new StreamHandler('php://stdout', OutLvl::mapToMonologLevel($output->getVerbosity()));
156✔
44
        $handler->setFormatter(new LogstashFormatter('cli'));
156✔
45

46
        $this->logger = new Logger(Slug::filter($application->getName()));
156✔
47
        $this->logger->pushHandler($handler);
156✔
48

49
        parent::__construct($input, $output, $application);
156✔
50
    }
51

52
    /**
53
     * @SuppressWarnings(PHPMD.Superglobals)
54
     */
55
    public function onExecBefore(): void
56
    {
57
        $this->_('Command Start: ' . (string)$this->input->getFirstArgument(), OutLvl::INFO, [
156✔
58
            'service' => [
78✔
59
                'name'        => $this->application->getName(),
156✔
60
                'version'     => $this->application->getVersion(),
156✔
61
                'type'        => 'php',
78✔
62
                'php_version' => \PHP_VERSION,
78✔
63
            ],
78✔
64
            'process' => [
78✔
65
                'pid'               => \getmypid(),
156✔
66
                'executable'        => $_SERVER['PHP_SELF'] ?? null,
156✔
67
                'command_line'      => $this->input->__toString(),
156✔
68
                'process_command'   => $this->input->getFirstArgument(),
156✔
69
                'working_directory' => \getcwd(),
156✔
70
            ],
78✔
71
        ]);
78✔
72
    }
73

74
    public function onExecException(\Exception $exception): void
75
    {
76
        $this->logger->log(
36✔
77
            Level::Critical,
18✔
78
            'Command Exception: ' . $exception->getMessage(),
36✔
79
            $this->prepareContext([
36✔
80
                'error' => [
18✔
81
                    'type'        => \get_class($exception),
36✔
82
                    'code'        => $exception->getCode(),
36✔
83
                    'message'     => $exception->getMessage(),
36✔
84
                    'file'        => $exception->getFile() . ':' . $exception->getLine(),
36✔
85
                    'stack_trace' => $exception->getTraceAsString(),
36✔
86
                ],
18✔
87
            ]),
18✔
88
        );
18✔
89
    }
90

91
    public function onExecAfter(int $exitCode, ?string $outputLevel = null): void
92
    {
93
        $outputLevel ??= OutLvl::INFO;
156✔
94
        $this->_('Command Finish: ExitCode=' . $exitCode, $outputLevel, [
156✔
95
            'process' => ['exit_code' => $exitCode],
78✔
96
        ]);
78✔
97
    }
98

99
    public function isProgressBarDisabled(): bool
100
    {
101
        return false;
×
102
    }
103

104
    public function createProgressBar(): AbstractProgressBar
105
    {
106
        return new ProgressBarLight($this);
76✔
107
    }
108

109
    public static function getName(): string
110
    {
111
        return 'logstash';
584✔
112
    }
113

114
    public static function getDescription(): string
115
    {
116
        return 'Logstash output format, for integration with ELK stack.';
584✔
117
    }
118

119
    protected function printMessage(
120
        ?string $message = '',
121
        string $verboseLevel = OutLvl::DEFAULT,
122
        array $context = [],
123
    ): void {
124
        $nonZeroOnError = bool($this->getInput()->getOption('non-zero-on-error'));
156✔
125
        $psrErrorLevel  = OutLvl::mapToMonologLevel($verboseLevel);
156✔
126

127
        if ($nonZeroOnError && OutLvl::isPsrErrorLevel($psrErrorLevel)) {
156✔
128
            $this->markOutputHasErrors(true);
8✔
129
        }
130

131
        if ($message !== null && $message !== '') {
156✔
132
            $this->logger->log($psrErrorLevel, \strip_tags($message), $context);
156✔
133
        }
134
    }
135

136
    protected function prepareContext(array $context): array
137
    {
138
        // We use timestamp_real to use the value from it in @timestamp using the rules of the logstash service.
139
        // In cases if the default field `@timestamp` doesn't work with the logstash service for some reason.
140
        if (
NEW
141
            \defined('JBZOO_CLI_TIMESTAMP_REAL')
×
142
            && JBZOO_CLI_TIMESTAMP_REAL
143
            && !isset($context['timestamp_real'])
156✔
144
        ) {
145
            $context['timestamp_real'] = new DateTimeImmutable(true, new \DateTimeZone(\date_default_timezone_get()));
156✔
146
        }
147

148
        $newContext = [
156✔
149
            'trace'   => ['id' => CliHelper::createOrGetTraceId()],
156✔
150
            'profile' => $this->getProfileInfo(),
156✔
151
        ] + $context;
78✔
152

153
        return parent::prepareContext($newContext);
156✔
154
    }
155
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc