1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Tylercd100\LERN\Components; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Illuminate\Container\Container; |
7
|
|
|
use Monolog\Handler\HandlerInterface; |
8
|
|
|
use Monolog\Logger; |
9
|
|
|
use Tylercd100\LERN\Exceptions\NotifierFailedException; |
10
|
|
|
use Tylercd100\Notify\Drivers\FromConfig as Notify; |
11
|
|
|
|
12
|
|
|
class Notifier extends Component { |
13
|
|
|
protected $config; |
14
|
|
|
protected $log; |
15
|
|
|
protected $messageCb; |
16
|
|
|
protected $subjectCb; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* You can provide a Monolog Logger instance to use in the constructor |
20
|
|
|
* @param Logger|null $log Logger instance to use |
21
|
54 |
|
*/ |
22
|
54 |
|
public function __construct(Logger $log = null) { |
23
|
54 |
|
if ($log === null) { |
24
|
54 |
|
$log = new Logger(config('lern.notify.channel')); |
25
|
|
|
} |
26
|
54 |
|
|
27
|
54 |
|
$this->config = config('lern.notify'); |
28
|
54 |
|
$this->log = $log; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Transforms a value into a closure that returns itself when called |
33
|
|
|
* @param callable|string $cb The value that you want to wrap in a closure |
34
|
|
|
* @return callable |
35
|
12 |
|
*/ |
36
|
12 |
|
private function wrapValueInClosure($cb) { |
37
|
6 |
|
if (is_callable($cb)) { |
38
|
|
|
return $cb; |
39
|
|
|
} else { |
40
|
|
|
return function() use ($cb) { return $cb; }; |
41
|
|
|
} |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Set a string or a closure to be called that will generate the message body for the notification |
46
|
|
|
* @param callable|string $cb A closure or string that will be set for the message |
47
|
6 |
|
*/ |
48
|
|
|
public function setMessage($cb) |
49
|
6 |
|
{ |
50
|
6 |
|
$this->messageCb = $this->wrapValueInClosure($cb); |
51
|
|
|
return $this; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Returns the result of the message closure |
56
|
|
|
* @param Exception $e The Exception instance that you want to build the message around |
57
|
|
|
* @return string The message string |
58
|
15 |
|
*/ |
59
|
15 |
|
public function getMessage(Exception $e) { |
60
|
6 |
|
if (is_callable($this->messageCb)) { |
61
|
|
|
return $this->messageCb->__invoke($e); |
62
|
9 |
|
} else { |
63
|
9 |
|
$msg = get_class($e) . " was thrown! \n" . $e->getMessage(); |
64
|
9 |
|
if ($this->config['includeExceptionStackTrace'] === true) { |
65
|
9 |
|
$msg .= "\n\n" . $e->getTraceAsString(); |
66
|
9 |
|
} |
67
|
|
|
return $msg; |
68
|
|
|
} |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* Set a string or a closure to be called that will generate the subject line for the notification |
73
|
|
|
* @param callable|string $cb A closure or string that will be set for the subject line |
74
|
6 |
|
*/ |
75
|
|
|
public function setSubject($cb) |
76
|
6 |
|
{ |
77
|
6 |
|
$this->subjectCb = $this->wrapValueInClosure($cb); |
78
|
|
|
return $this; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Returns the result of the subject closure |
83
|
|
|
* @param Exception $e The Exception instance that you want to build the subject around |
84
|
|
|
* @return string The subject string |
85
|
15 |
|
*/ |
86
|
15 |
|
public function getSubject(Exception $e) { |
87
|
6 |
|
if (is_callable($this->subjectCb)) { |
88
|
|
|
return $this->subjectCb->__invoke($e); |
89
|
9 |
|
} else { |
90
|
|
|
return get_class($e); |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Pushes on another Monolog Handler |
96
|
|
|
* @param HandlerInterface $handler The handler instance to add on |
97
|
|
|
* @return Notifier Returns this |
98
|
6 |
|
*/ |
99
|
6 |
|
public function pushHandler(HandlerInterface $handler) { |
100
|
6 |
|
$this->log->pushHandler($handler); |
101
|
|
|
return $this; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Triggers the Monolog Logger instance to log an error to all handlers |
106
|
|
|
* @param Exception $e The exception to use |
107
|
|
|
* @param array $context Additional information that you would like to pass to Monolog |
108
|
|
|
* @return bool |
109
|
12 |
|
*/ |
110
|
|
|
public function send(Exception $e, array $context = []) { |
111
|
12 |
|
|
112
|
3 |
|
if ($this->shouldntHandle($e)) { |
113
|
|
|
return false; |
114
|
|
|
} |
115
|
9 |
|
|
116
|
9 |
|
$message = $this->getMessage($e); |
117
|
|
|
$subject = $this->getSubject($e); |
118
|
|
|
|
119
|
9 |
|
try { |
120
|
|
|
$context = $this->buildContext($context, $e); |
121
|
9 |
|
|
122
|
|
|
$notify = new Notify($this->config, $this->log, $subject); |
123
|
9 |
|
|
124
|
|
|
$notify->critical($message, $context); |
125
|
6 |
|
|
126
|
3 |
|
return true; |
127
|
3 |
|
} catch (Exception $e) { |
128
|
3 |
|
$code = (is_int($e->getCode()) ? $e->getCode() : 0); |
129
|
|
|
throw new NotifierFailedException($e->getMessage(), $code, $e); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Builds a context array to pass to Monolog |
135
|
|
|
* |
136
|
|
|
* @param array $context Additional information that you would like to pass to Monolog |
137
|
9 |
|
* @param \Exception $e |
138
|
9 |
|
* |
139
|
9 |
|
* @return array The modified context array |
140
|
9 |
|
*/ |
141
|
9 |
|
protected function buildContext(array $context = [], Exception $e) |
142
|
|
|
{ |
143
|
|
|
$app = app(); |
144
|
|
|
|
145
|
|
|
// Add sound ro pushover |
146
|
|
|
if(in_array('pushover', $this->config['drivers'])) |
147
|
|
|
{ |
148
|
|
|
$context['sound'] = $this->config['pushover']['sound']; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
// Add exception context for raven for better handling in Sentry |
152
|
|
|
if(in_array('raven', $this->config['drivers'])) |
153
|
|
|
{ |
154
|
|
|
$context['exception'] = $e; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
// Add auth data if available. |
158
|
|
|
if(isset($app['auth']) && $user = $app['auth']->user()) |
159
|
|
|
{ |
160
|
|
View Code Duplication |
if(empty($context['user']) or !is_array($context['user'])) |
|
|
|
|
161
|
|
|
{ |
162
|
|
|
$context['user'] = []; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
View Code Duplication |
if(!isset($context['user']['id']) && method_exists($user, 'getAuthIdentifier')) |
|
|
|
|
166
|
|
|
{ |
167
|
|
|
$context['user']['id'] = $user->getAuthIdentifier(); |
168
|
|
|
} |
169
|
|
|
|
170
|
|
View Code Duplication |
if(!isset($context['user']['id']) && method_exists($user, 'getKey')) |
|
|
|
|
171
|
|
|
{ |
172
|
|
|
$context['user']['id'] = $user->getKey(); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
View Code Duplication |
if(!isset($context['user']['id']) && isset($user->id)) |
|
|
|
|
176
|
|
|
{ |
177
|
|
|
$context['user']['id'] = $user->id; |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
// Add session data if available. |
182
|
|
|
if(isset($app['session']) && $session = $app['session']->all()) |
183
|
|
|
{ |
184
|
|
View Code Duplication |
if(empty($context['user']) or !is_array($context['user'])) |
|
|
|
|
185
|
|
|
{ |
186
|
|
|
$context['user'] = []; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
View Code Duplication |
if(!isset($context['user']['id'])) |
|
|
|
|
190
|
|
|
{ |
191
|
|
|
$context['user']['id'] = $app->session->getId(); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
if(isset($context['user']['data'])) |
195
|
|
|
{ |
196
|
|
|
$context['user']['data'] = array_merge($session, $context['user']['data']); |
197
|
|
|
} else |
198
|
|
|
{ |
199
|
|
|
$context['user']['data'] = $session; |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
// Automatic tags |
203
|
|
|
$tags = [ |
204
|
|
|
'environment' => $app->environment(), |
205
|
|
|
'server' => $app->request->server('HTTP_HOST'), |
206
|
|
|
'php_version' => phpversion(), |
207
|
|
|
]; |
208
|
|
|
|
209
|
|
|
// Add tags to context. |
210
|
|
View Code Duplication |
if(isset($context['tags'])) |
|
|
|
|
211
|
|
|
{ |
212
|
|
|
$context['tags'] = array_merge($tags, $context['tags']); |
213
|
|
|
} else |
214
|
|
|
{ |
215
|
|
|
$context['tags'] = $tags; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
// Automatic extra data. |
219
|
|
|
$extra = [ |
220
|
|
|
'ip' => $app->request->getClientIp(), |
221
|
|
|
]; |
222
|
|
|
|
223
|
|
|
// Everything that is not 'user', 'tags' or 'level' is automatically considered |
224
|
|
|
// as additonal 'extra' context data. |
225
|
|
|
$extra = array_merge($extra, array_except($context, ['user', 'tags', 'level', 'extra'])); |
226
|
|
|
|
227
|
|
|
// Add extra to context. |
228
|
|
View Code Duplication |
if(isset($context['extra'])) |
|
|
|
|
229
|
|
|
{ |
230
|
|
|
$context['extra'] = array_merge($extra, $context['extra']); |
231
|
|
|
} else |
232
|
|
|
{ |
233
|
|
|
$context['extra'] = $extra; |
234
|
|
|
} |
235
|
|
|
|
236
|
|
|
// Clean out other values from context. |
237
|
|
|
$context = array_only($context, ['user', 'tags', 'level', 'extra', 'exception']); |
238
|
|
|
|
239
|
|
|
return $context; |
240
|
|
|
} |
241
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.