1+ <?php
2+
3+ namespace SoysalTan \LaravelPluginSystem \Commands ;
4+
5+ use Illuminate \Console \Command ;
6+ use SoysalTan \LaravelPluginSystem \Services \PluginHealthMonitor ;
7+ use Symfony \Component \Console \Command \Command as CommandAlias ;
8+
9+ class PluginHealthCommand extends Command
10+ {
11+ protected $ signature = 'plugin:health
12+ {plugin? : Specific plugin name to check}
13+ {--watch : Watch mode - continuously monitor health}
14+ {--json : Output in JSON format}
15+ {--detailed : Show detailed metrics}
16+ {--errors : Show recent errors}
17+ {--clear-errors= : Clear errors for specific plugin} ' ;
18+
19+ protected $ description = 'Monitor plugin health status and metrics ' ;
20+
21+ protected PluginHealthMonitor $ healthMonitor ;
22+
23+ public function __construct (PluginHealthMonitor $ healthMonitor )
24+ {
25+ parent ::__construct ();
26+ $ this ->healthMonitor = $ healthMonitor ;
27+ }
28+
29+ public function handle (): int
30+ {
31+ if ($ this ->option ('clear-errors ' )) {
32+ return $ this ->clearErrors ();
33+ }
34+
35+ if ($ this ->option ('watch ' )) {
36+ return $ this ->watchMode ();
37+ }
38+
39+ $ pluginName = $ this ->argument ('plugin ' );
40+
41+ if ($ pluginName ) {
42+ return $ this ->showPluginHealth ($ pluginName );
43+ }
44+
45+ return $ this ->showAllPluginsHealth ();
46+ }
47+
48+ protected function showPluginHealth (string $ pluginName ): int
49+ {
50+ $ health = $ this ->healthMonitor ->checkPluginHealth ($ pluginName );
51+
52+ if (empty ($ health )) {
53+ $ this ->error ("Plugin ' {$ pluginName }' not found or not enabled. " );
54+ return CommandAlias::FAILURE ;
55+ }
56+
57+ if ($ this ->option ('json ' )) {
58+ $ this ->line (json_encode ($ health , JSON_PRETTY_PRINT ));
59+ return CommandAlias::SUCCESS ;
60+ }
61+
62+ $ this ->displayPluginHealth ($ health );
63+ return CommandAlias::SUCCESS ;
64+ }
65+
66+ protected function showAllPluginsHealth (): int
67+ {
68+ $ healthReport = $ this ->healthMonitor ->getHealthReport ();
69+
70+ if ($ this ->option ('json ' )) {
71+ $ this ->line (json_encode ($ healthReport , JSON_PRETTY_PRINT ));
72+ return CommandAlias::SUCCESS ;
73+ }
74+
75+ $ this ->displayHealthSummary ($ healthReport ['summary ' ]);
76+ $ this ->newLine ();
77+
78+ foreach ($ healthReport ['plugins ' ] as $ pluginHealth ) {
79+ $ this ->displayPluginHealth ($ pluginHealth , false );
80+ $ this ->newLine ();
81+ }
82+
83+ return CommandAlias::SUCCESS ;
84+ }
85+
86+ protected function displayHealthSummary (array $ summary ): void
87+ {
88+ $ this ->info ('=== Plugin Health Summary === ' );
89+
90+ $ statusColor = match ($ summary ['overall_status ' ]) {
91+ 'healthy ' => 'green ' ,
92+ 'warning ' => 'yellow ' ,
93+ 'critical ' => 'red ' ,
94+ default => 'white '
95+ };
96+
97+ $ this ->line ("Overall Status: <fg= {$ statusColor }> " . strtoupper ($ summary ['overall_status ' ]) . "</> " );
98+ $ this ->line ("Total Plugins: {$ summary ['total_plugins ' ]}" );
99+ $ this ->line ("<fg=green>Healthy: {$ summary ['healthy_plugins ' ]}</> " );
100+ $ this ->line ("<fg=yellow>Warning: {$ summary ['warning_plugins ' ]}</> " );
101+ $ this ->line ("<fg=red>Critical: {$ summary ['critical_plugins ' ]}</> " );
102+ $ this ->line ("Last Check: {$ summary ['last_check ' ]}" );
103+ }
104+
105+ protected function displayPluginHealth (array $ health , bool $ detailed = null ): void
106+ {
107+ $ detailed = $ detailed ?? $ this ->option ('detailed ' );
108+
109+ $ statusColor = match ($ health ['status ' ]) {
110+ 'healthy ' => 'green ' ,
111+ 'warning ' => 'yellow ' ,
112+ 'critical ' => 'red ' ,
113+ default => 'white '
114+ };
115+
116+ $ this ->line ("Plugin: <fg=cyan> {$ health ['plugin_name ' ]}</> " );
117+ $ this ->line ("Status: <fg= {$ statusColor }> " . strtoupper ($ health ['status ' ]) . "</> " );
118+ $ this ->line ("Uptime: {$ health ['uptime ' ]}% " );
119+
120+ if ($ detailed ) {
121+ $ this ->displayDetailedMetrics ($ health ['metrics ' ]);
122+ }
123+
124+ if ($ this ->option ('errors ' ) && !empty ($ health ['recent_errors ' ])) {
125+ $ this ->displayRecentErrors ($ health ['recent_errors ' ]);
126+ }
127+
128+ if (!empty ($ health ['recommendations ' ])) {
129+ $ this ->displayRecommendations ($ health ['recommendations ' ]);
130+ }
131+ }
132+
133+ protected function displayDetailedMetrics (array $ metrics ): void
134+ {
135+ $ this ->line ("Metrics: " );
136+ $ this ->line (" Memory Usage: " . $ this ->formatBytes ($ metrics ['memory_usage ' ] ?? 0 ));
137+ $ this ->line (" Execution Time: " . ($ metrics ['execution_time ' ] ?? 0 ) . "ms " );
138+ $ this ->line (" Request Count: " . ($ metrics ['request_count ' ] ?? 0 ));
139+ $ this ->line (" Error Count: " . ($ metrics ['error_count ' ] ?? 0 ));
140+ $ this ->line (" Response Time: " . ($ metrics ['response_time ' ] ?? 0 ) . "ms " );
141+ $ this ->line (" Database Queries: " . ($ metrics ['database_queries ' ] ?? 0 ));
142+ $ this ->line (" Cache Hits: " . ($ metrics ['cache_hits ' ] ?? 0 ));
143+ $ this ->line (" Cache Misses: " . ($ metrics ['cache_misses ' ] ?? 0 ));
144+
145+ if ($ metrics ['last_activity ' ]) {
146+ $ this ->line (" Last Activity: {$ metrics ['last_activity ' ]}" );
147+ }
148+ }
149+
150+ protected function displayRecentErrors (array $ errors ): void
151+ {
152+ $ this ->line ("<fg=red>Recent Errors:</> " );
153+
154+ foreach ($ errors as $ index => $ error ) {
155+ $ severityColor = match ($ error ['severity ' ]) {
156+ 'critical ' => 'red ' ,
157+ 'warning ' => 'yellow ' ,
158+ 'info ' => 'blue ' ,
159+ default => 'white '
160+ };
161+
162+ $ this ->line (" " . ($ index + 1 ) . ". <fg= {$ severityColor }>[ {$ error ['severity ' ]}]</> {$ error ['message ' ]}" );
163+ $ this ->line (" File: {$ error ['file ' ]}: {$ error ['line ' ]}" );
164+ $ this ->line (" Time: {$ error ['timestamp ' ]}" );
165+ }
166+ }
167+
168+ protected function displayRecommendations (array $ recommendations ): void
169+ {
170+ $ this ->line ("<fg=yellow>Recommendations:</> " );
171+
172+ foreach ($ recommendations as $ index => $ recommendation ) {
173+ $ this ->line (" " . ($ index + 1 ) . ". {$ recommendation }" );
174+ }
175+ }
176+
177+ protected function watchMode (): int
178+ {
179+ $ this ->info ('Starting health monitoring in watch mode. Press Ctrl+C to stop. ' );
180+
181+ try {
182+ while (true ) {
183+ $ this ->call ('clear ' );
184+ $ this ->line ('Plugin Health Monitor - ' . now ()->format ('Y-m-d H:i:s ' ));
185+ $ this ->line (str_repeat ('= ' , 60 ));
186+
187+ $ this ->showAllPluginsHealth ();
188+
189+ sleep (5 ); // Refresh every 5 seconds
190+ }
191+ } catch (\Exception $ e ) {
192+ $ this ->error ('Watch mode interrupted: ' . $ e ->getMessage ());
193+ return CommandAlias::FAILURE ;
194+ }
195+
196+ return CommandAlias::SUCCESS ;
197+ }
198+
199+ protected function clearErrors (): int
200+ {
201+ $ pluginName = $ this ->option ('clear-errors ' );
202+
203+ if ($ pluginName === 'all ' ) {
204+ // Clear errors for all plugins
205+ $ healthReport = $ this ->healthMonitor ->getHealthReport ();
206+
207+ foreach ($ healthReport ['plugins ' ] as $ plugin ) {
208+ $ this ->healthMonitor ->clearPluginErrors ($ plugin ['plugin_name ' ]);
209+ }
210+
211+ $ this ->info ('Cleared errors for all plugins. ' );
212+ } else {
213+ $ this ->healthMonitor ->clearPluginErrors ($ pluginName );
214+ $ this ->info ("Cleared errors for plugin: {$ pluginName }" );
215+ }
216+
217+ return CommandAlias::SUCCESS ;
218+ }
219+
220+ protected function formatBytes (int $ bytes ): string
221+ {
222+ $ units = ['B ' , 'KB ' , 'MB ' , 'GB ' ];
223+ $ bytes = max ($ bytes , 0 );
224+ $ pow = floor (($ bytes ? log ($ bytes ) : 0 ) / log (1024 ));
225+ $ pow = min ($ pow , count ($ units ) - 1 );
226+
227+ $ bytes /= (1 << (10 * $ pow ));
228+
229+ return round ($ bytes , 2 ) . ' ' . $ units [$ pow ];
230+ }
231+ }
0 commit comments