Completed
Pull Request — master (#20)
by Damian
01:35
created

SentryMonologHandler   A

Complexity

Total Complexity 12

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 4

Importance

Changes 0
Metric Value
wmc 12
lcom 0
cbo 4
dl 0
loc 128
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A getClient() 0 4 1
A write() 0 11 1
B backtrace() 0 39 5
A getUserData() 0 12 4
1
<?php
2
3
/**
4
 * Class: SentryMonologHandler.
5
 *
6
 * @author  Russell Michell 2017 <[email protected]>
7
 * @package phptek/sentry
8
 */
9
10
namespace PhpTek\Sentry\Handler;
11
12
use Exception;
13
use Monolog\Logger;
14
use PhpTek\Sentry\Log\SentryLogger;
15
use PhpTek\Sentry\Monolog\Handler\SentryRavenHandler;
16
use SilverStripe\Dev\Backtrace;
17
use SilverStripe\Security\Member;
18
19
/**
20
 * Monolog Handler for Sentry via Raven
21
 */
22
class SentryMonologHandler extends SentryRavenHandler
23
{
24
    /**
25
     * @var SentryClientAdaptor
26
     */
27
    protected $client;
28
29
    /**
30
     * @param  int $level
31
     * @param  bool $bubble
32
     * @param  array $extras Extra parameters that will become "tags" in Sentry.
33
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
34
     */
35
    public function __construct($level = Logger::DEBUG, $bubble = true, $extras = [])
36
    {
37
        // Returns an instance of {@link SentryLogger}
38
        $logger = SentryLogger::factory($extras);
39
        $sdk = $logger->client->getSDK();
0 ignored issues
show
Bug introduced by
The property client does not seem to exist in PhpTek\Sentry\Log\SentryLogger.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
40
        $this->client = $logger->client;
41
        $this->client->setData('user', $this->getUserData(null, $logger));
42
43
        parent::__construct($sdk, $level, $bubble);
44
    }
45
46
    /**
47
     * @return SentryClientAdaptor
48
     */
49
    public function getClient()
50
    {
51
        return $this->client;
52
    }
53
54
    /**
55
     * write() forms the entry point into the physical sending of the error. The
56
     * sending itself is done by the current adaptor's `send()` method.
57
     *
58
     * @param  array $record An array of error-context metadata with the following
59
     *                       available keys:
60
     *
61
     *                       - message
62
     *                       - context
63
     *                       - level
64
     *                       - level_name
65
     *                       - channel
66
     *                       - datetime
67
     *                       - extra
68
     *                       - formatted
69
     *
70
     * @return void
71
     */
72
    protected function write(array $record)
73
    {
74
        $record = array_merge($record, [
75
            'timestamp'  => $record['datetime']->getTimestamp(),
76
            'stacktrace' => $this->backtrace($record),
77
        ]);
78
79
        // write() calls one of RavenHandler::captureException() or RavenHandler::captureMessage()
80
        // depending on if $record['context']['exception'] is an instance of Exception or not
81
        parent::write($record);
82
    }
83
84
    /**
85
     * Generate a cleaned-up backtrace of the event that got us here.
86
     *
87
     * @param  array $record
88
     * @return array
89
     */
90
    private function backtrace($record)
91
    {
92
        // Provided trace
93
        if (!empty($record['context']['trace'])) {
94
            return $record['context']['trace'];
95
        }
96
97
        // Generate trace from exception
98
        if (isset($record['context']['exception'])) {
99
            /** @var Exception $exception */
100
            $exception = $record['context']['exception'];
101
            return $exception->getTrace();
102
        }
103
104
        // Failover: build custom trace
105
        $bt = debug_backtrace();
106
107
        // Push current line into context
108
        array_unshift($bt, [
109
            'file'     => !empty($bt['file']) ? $bt['file'] : 'N/A',
110
            'line'     => !empty($bt['line']) ? $bt['line'] : 'N/A',
111
            'function' => '',
112
            'class'    => '',
113
            'type'     => '',
114
            'args'     => [],
115
        ]);
116
117
        $bt = Backtrace::filter_backtrace($bt, [
118
            '',
119
            'Monolog\\Handler\\AbstractProcessingHandler->handle',
120
            'Monolog\\Logger->addRecord',
121
            'Monolog\\Logger->log',
122
            'Monolog\\Logger->warn',
123
            'PhpTek\\Sentry\\Handler\\SentryMonologHandler->write',
124
            'PhpTek\\Sentry\\Handler\\SentryMonologHandler->backtrace',
125
        ]);
126
127
        return $bt;
128
    }
129
130
    /**
131
     * Returns a default set of additional data specific to the user's part in
132
     * the request.
133
     *
134
     * @param  Member $member
135
     * @return array
136
     */
137
    private function getUserData(Member $member = null, $logger)
138
    {
139
        if (!$member) {
140
            $member = Member::currentUser();
0 ignored issues
show
Deprecated Code introduced by
The method SilverStripe\Security\Member::currentUser() has been deprecated with message: 5.0.0 use Security::getCurrentUser()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
141
        }
142
143
        return [
144
            'IPddress' => $logger->getIP(),
145
            'ID'       => $member ? $member->getField('ID') : SentryLogger::SLW_NOOP,
146
            'Email'    => $member ? $member->getField('Email') : SentryLogger::SLW_NOOP,
147
        ];
148
    }
149
}
150