1<?php declare(strict_types=1);
2
3/*
4 * This file is part of the Monolog package.
5 *
6 * (c) Jordi Boggiano <j.boggiano@seld.be>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Monolog\Handler;
13
14use Monolog\Formatter\FormatterInterface;
15use Monolog\Formatter\LineFormatter;
16use Monolog\Logger;
17
18/**
19 * Sends logs to Fleep.io using Webhook integrations
20 *
21 * You'll need a Fleep.io account to use this handler.
22 *
23 * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation
24 * @author Ando Roots <ando@sqroot.eu>
25 *
26 * @phpstan-import-type FormattedRecord from AbstractProcessingHandler
27 */
28class FleepHookHandler extends SocketHandler
29{
30    protected const FLEEP_HOST = 'fleep.io';
31
32    protected const FLEEP_HOOK_URI = '/hook/';
33
34    /**
35     * @var string Webhook token (specifies the conversation where logs are sent)
36     */
37    protected $token;
38
39    /**
40     * Construct a new Fleep.io Handler.
41     *
42     * For instructions on how to create a new web hook in your conversations
43     * see https://fleep.io/integrations/webhooks/
44     *
45     * @param  string                    $token  Webhook token
46     * @throws MissingExtensionException
47     */
48    public function __construct(
49        string $token,
50        $level = Logger::DEBUG,
51        bool $bubble = true,
52        bool $persistent = false,
53        float $timeout = 0.0,
54        float $writingTimeout = 10.0,
55        ?float $connectionTimeout = null,
56        ?int $chunkSize = null
57    ) {
58        if (!extension_loaded('openssl')) {
59            throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler');
60        }
61
62        $this->token = $token;
63
64        $connectionString = 'ssl://' . static::FLEEP_HOST . ':443';
65        parent::__construct(
66            $connectionString,
67            $level,
68            $bubble,
69            $persistent,
70            $timeout,
71            $writingTimeout,
72            $connectionTimeout,
73            $chunkSize
74        );
75    }
76
77    /**
78     * Returns the default formatter to use with this handler
79     *
80     * Overloaded to remove empty context and extra arrays from the end of the log message.
81     *
82     * @return LineFormatter
83     */
84    protected function getDefaultFormatter(): FormatterInterface
85    {
86        return new LineFormatter(null, null, true, true);
87    }
88
89    /**
90     * Handles a log record
91     */
92    public function write(array $record): void
93    {
94        parent::write($record);
95        $this->closeSocket();
96    }
97
98    /**
99     * {@inheritDoc}
100     */
101    protected function generateDataStream(array $record): string
102    {
103        $content = $this->buildContent($record);
104
105        return $this->buildHeader($content) . $content;
106    }
107
108    /**
109     * Builds the header of the API Call
110     */
111    private function buildHeader(string $content): string
112    {
113        $header = "POST " . static::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n";
114        $header .= "Host: " . static::FLEEP_HOST . "\r\n";
115        $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
116        $header .= "Content-Length: " . strlen($content) . "\r\n";
117        $header .= "\r\n";
118
119        return $header;
120    }
121
122    /**
123     * Builds the body of API call
124     *
125     * @phpstan-param FormattedRecord $record
126     */
127    private function buildContent(array $record): string
128    {
129        $dataArray = [
130            'message' => $record['formatted'],
131        ];
132
133        return http_build_query($dataArray);
134    }
135}
136