1+ <?php
2+
3+ declare (strict_types=1 );
4+
5+ namespace WPGraphQL \Logging \Events ;
6+
7+ use GraphQL \Executor \ExecutionResult ;
8+ use Monolog \Level ;
9+ use WPGraphQL \Logging \Admin \Settings \Fields \Tab \Basic_Configuration_Tab ;
10+ use WPGraphQL \Logging \Logger \LoggerService ;
11+ use WPGraphQL \Logging \Logger \LoggingHelper ;
12+ use WPGraphQL \Request ;
13+ use WPGraphQL \WPSchema ;
14+
15+ /**
16+ * Handles logging for GraphQL actions.
17+ *
18+ * This class is a dedicated component for listening to and logging data
19+ * from specific WPGraphQL action hooks.
20+ *
21+ * @package WPGraphQL\Logging
22+ *
23+ * @since 0.0.1
24+ */
25+ class QueryActionLogger {
26+ use LoggingHelper;
27+
28+ /**
29+ * The logger service instance.
30+ *
31+ * @var \WPGraphQL\Logging\Logger\LoggerService
32+ */
33+ protected LoggerService $ logger ;
34+
35+ /**
36+ * The basic configuration settings.
37+ *
38+ * @var array<string, string|int|bool|array<string>>
39+ */
40+ protected array $ config ;
41+
42+ /**
43+ * QueryActionLogger constructor.
44+ *
45+ * @param \WPGraphQL\Logging\Logger\LoggerService $logger The logger instance.
46+ * @param array<string, mixed> $config The logging configuration.
47+ */
48+ public function __construct ( LoggerService $ logger , array $ config ) {
49+ $ this ->logger = $ logger ;
50+ $ this ->config = $ config ;
51+ }
52+
53+ /**
54+ * Initial Incoming Request.
55+ *
56+ * This method hooks into the `do_graphql_request` action.
57+ *
58+ * @param string $query
59+ * @param string|null $operation_name
60+ * @param array<string, mixed>|null $variables
61+ */
62+ public function log_pre_request ( string $ query , ?string $ operation_name , ?array $ variables ): void {
63+ try {
64+ if ( ! $ this ->is_logging_enabled ( $ this ->config ) ) {
65+ return ;
66+ }
67+ $ selected_events = $ this ->config [ Basic_Configuration_Tab::EVENT_LOG_SELECTION ] ?? [];
68+ if ( ! in_array ( Events::PRE_REQUEST , $ selected_events , true ) ) {
69+ return ;
70+ }
71+ $ context = [
72+ 'query ' => $ query ,
73+ 'variables ' => $ variables ,
74+ 'operation_name ' => $ operation_name ,
75+ ];
76+ $ payload = EventManager::transform ( Events::PRE_REQUEST , [ 'context ' => $ context , 'level ' => Level::Info ] );
77+ $ this ->logger ->log ( $ payload ['level ' ], 'WPGraphQL Pre Request ' , $ payload ['context ' ] );
78+ EventManager::publish ( Events::PRE_REQUEST , [ 'context ' => $ payload ['context ' ] ] );
79+ } catch ( \Throwable $ e ) {
80+ $ this ->process_application_error ( Events::PRE_REQUEST , $ e );
81+ }
82+ }
83+
84+ /**
85+ * Before Request Execution.
86+ *
87+ * This method hooks into the `graphql_before_execute` action.
88+ *
89+ * @param Request $request
90+ */
91+ public function log_graphql_before_execute ( Request $ request ): void {
92+ try {
93+ if ( ! $ this ->is_logging_enabled ( $ this ->config ) ) {
94+ return ;
95+ }
96+ $ selected_events = $ this ->config [ Basic_Configuration_Tab::EVENT_LOG_SELECTION ] ?? [];
97+ if ( ! in_array ( Events::BEFORE_GRAPHQL_EXECUTION , $ selected_events , true ) ) {
98+ return ;
99+ }
100+ /** @var \GraphQL\Server\OperationParams $params */
101+ $ params = $ request ->params ;
102+ $ context = [
103+ 'query ' => $ params ->query ,
104+ 'operation_name ' => $ params ->operation ,
105+ 'variables ' => $ params ->variables ,
106+ 'params ' => $ params ,
107+ ];
108+ $ payload = EventManager::transform ( Events::BEFORE_GRAPHQL_EXECUTION , [ 'context ' => $ context , 'level ' => Level::Info ] );
109+ $ this ->logger ->log ( $ payload ['level ' ], 'WPGraphQL Before Query Execution ' , $ payload ['context ' ] );
110+ EventManager::publish ( Events::BEFORE_GRAPHQL_EXECUTION , [ 'context ' => $ payload ['context ' ] ] );
111+ } catch ( \Throwable $ e ) {
112+ $ this ->process_application_error ( Events::BEFORE_GRAPHQL_EXECUTION , $ e );
113+ }
114+ }
115+
116+ /**
117+ * Before the GraphQL response is returned to the client.
118+ *
119+ * This method hooks into the `graphql_return_response` action.
120+ *
121+ * @param array<mixed>|\GraphQL\Executor\ExecutionResult $filtered_response
122+ * @param array<mixed>|\GraphQL\Executor\ExecutionResult $response
123+ * @param WPSchema $schema
124+ * @param string|null $operation
125+ * @param string $query
126+ * @param array<string, mixed>|null $variables
127+ * @param Request $request
128+ * @param string|null $query_id
129+ */
130+ public function log_before_response_returned (
131+ array |ExecutionResult $ filtered_response ,
132+ array |ExecutionResult $ response ,
133+ WPSchema $ schema ,
134+ ?string $ operation ,
135+ string $ query ,
136+ ?array $ variables ,
137+ Request $ request ,
138+ ?string $ query_id
139+ ): void {
140+ try {
141+ if ( ! $ this ->is_logging_enabled ( $ this ->config ) ) {
142+ return ;
143+ }
144+ $ selected_events = $ this ->config [ Basic_Configuration_Tab::EVENT_LOG_SELECTION ] ?? [];
145+ if ( ! in_array ( Events::BEFORE_RESPONSE_RETURNED , $ selected_events , true ) ) {
146+ return ;
147+ }
148+ $ context = [
149+ 'response ' => $ response ,
150+ 'schema ' => $ schema ,
151+ 'operation_name ' => $ operation ,
152+ 'query ' => $ query ,
153+ 'variables ' => $ variables ,
154+ 'request ' => $ request ,
155+ 'query_id ' => $ query_id ,
156+ ];
157+ $ level = Level::Info;
158+ $ message = 'WPGraphQL Response ' ;
159+ $ errors = $ this ->get_response_errors ( $ response );
160+ if ( null !== $ errors && count ( $ errors ) > 0 ) {
161+ $ context ['errors ' ] = $ errors ;
162+ $ level = Level::Error;
163+ $ message = 'WPGraphQL Response with Errors ' ;
164+ }
165+ $ payload = EventManager::transform ( Events::BEFORE_RESPONSE_RETURNED , [ 'context ' => $ context , 'level ' => $ level ] );
166+ $ this ->logger ->log ( $ payload ['level ' ], $ message , $ payload ['context ' ] );
167+ EventManager::publish ( Events::BEFORE_RESPONSE_RETURNED , [ 'context ' => $ payload ['context ' ] ] );
168+ } catch ( \Throwable $ e ) {
169+ $ this ->process_application_error ( Events::BEFORE_RESPONSE_RETURNED , $ e );
170+ }
171+ }
172+
173+ /**
174+ * Get the context for the response.
175+ *
176+ * @param array<mixed>|\GraphQL\Executor\ExecutionResult $response The response.
177+ *
178+ * @return array<mixed>|null
179+ */
180+ protected function get_response_errors ( array |ExecutionResult $ response ): ?array {
181+ if ( $ response instanceof ExecutionResult && [] !== $ response ->errors ) {
182+ return $ response ->errors ;
183+ }
184+
185+ if ( ! is_array ( $ response ) ) {
186+ return null ;
187+ }
188+
189+ $ errors = $ response ['errors ' ] ?? null ;
190+ if ( null === $ errors || [] === $ errors ) {
191+ return null ;
192+ }
193+
194+ return $ errors ;
195+ }
196+
197+ /**
198+ * Handles and logs application errors.
199+ *
200+ * @param string $event
201+ * @param \Throwable $exception
202+ */
203+ protected function process_application_error ( string $ event , \Throwable $ exception ): void {
204+ error_log ( 'Error for WPGraphQL Logging - ' . $ event . ': ' . $ exception ->getMessage () . ' in ' . $ exception ->getFile () . ' on line ' . $ exception ->getLine () ); //phpcs:ignore
205+ }
206+ }
0 commit comments