44
55namespace WPGraphQL \Logging \Events ;
66
7+ use GraphQL \Executor \ExecutionResult ;
8+ use GraphQL \Server \OperationParams ;
79use Monolog \Level ;
810use WPGraphQL \Logging \Logger \LoggerService ;
11+ use WPGraphQL \Request ;
912
1013/**
1114 * WPGraphQL Query Event Lifecycle -
1215 *
13- * POC @TODO - Add pub/sub for query events.
16+ * Handles logging for GraphQL query lifecycle events.
1417 *
1518 * @package WPGraphQL\Logging
1619 *
@@ -29,7 +32,7 @@ class QueryEventLifecycle {
2932 *
3033 * @param \WPGraphQL\Logging\Logger\LoggerService $logger
3134 */
32- protected function __construct (readonly LoggerService $ logger ) {
35+ protected function __construct ( readonly LoggerService $ logger ) {
3336 }
3437
3538 /**
@@ -42,20 +45,26 @@ public static function init(): QueryEventLifecycle {
4245 self ::$ instance = new self ( $ logger );
4346 self ::$ instance ->setup ();
4447 }
48+
4549 return self ::$ instance ;
4650 }
4751
4852 /**
4953 * Logs the pre-request event for a GraphQL query.
54+ * This method is hooked into 'do_graphql_request'.
5055 *
51- * @param string $query The GraphQL query.
52- * @param mixed $variables The variables for the query .
53- * @param string $operation_name The name of the operation .
56+ * @param string $query The GraphQL query string .
57+ * @param string|null $operation_name The name of the operation. Made nullable .
58+ * @param array|null $variables The variables for the query. Made nullable .
5459 */
55- public function log_pre_request ( $ query , $ variables , $ operation_name ): void {
56-
60+ public function log_pre_request ( string $ query , ?string $ operation_name , ?array $ variables ): void {
5761 try {
58- $ context = [];
62+ $ context = [
63+ 'query ' => $ query ,
64+ 'variables ' => $ variables ,
65+ 'operation_name ' => $ operation_name ,
66+ ];
67+
5968 $ context = apply_filters ( 'wpgraphql_logging_pre_request_context ' , $ context , $ query , $ variables , $ operation_name );
6069 $ level = apply_filters ( 'wpgraphql_logging_pre_request_level ' , Level::Info, $ query , $ variables , $ operation_name );
6170 $ this ->logger ->log ( $ level , 'WPGraphQL Incoming Request ' , $ context );
@@ -67,34 +76,93 @@ public function log_pre_request( $query, $variables, $operation_name ): void {
6776
6877 /**
6978 * Logs the post-request event for a GraphQL query.
79+ * This method is now hooked into 'graphql_after_execute'.
7080 *
71- * @param mixed $response The response from the GraphQL request.
72- * @param mixed $result The result of the GraphQL request.
73- * @param string $operation_name The name of the operation.
74- * @param string $query The GraphQL query.
75- * @param array<string, mixed> $variables The variables for the query.
81+ * @param \GraphQL\Executor\ExecutionResult|array<int, \GraphQL\Executor\ExecutionResult> $response The GraphQL execution result(s).
82+ * This can be a single ExecutionResult object or an array of them for batch requests.
83+ * @param \WPGraphQL\Request $request_instance The WPGraphQL Request instance.
7684 */
77- public function log_post_request ( $ response , $ result , string $ operation_name , string $ query , array $ variables ): void {
85+ public function log_post_request ( $ response , Request $ request_instance ): void {
86+ // Extract relevant data from the WPGraphQL Request instance
87+ $ params = $ request_instance ->get_params (); // Can be OperationParams or array of OperationParams
88+ $ query = null ;
89+ $ operation_name = null ;
90+ $ variables = null ;
91+ $ status_code = 200 ; // Default success status
92+
93+ // Handle single or batch requests to get query details
94+ if ( $ params instanceof OperationParams ) {
95+ $ query = $ params ->query ;
96+ $ operation_name = $ params ->operation ;
97+ $ variables = $ params ->variables ;
98+ } elseif ( is_array ( $ params ) && ! empty ( $ params [0 ] ) && $ params [0 ] instanceof OperationParams ) {
99+ $ query = $ params [0 ]->query ;
100+ $ operation_name = $ params [0 ]->operation ;
101+ $ variables = $ params [0 ]->variables ;
102+ }
103+
104+ // Determine status code if available (WPGraphQL Router sets this)
105+ if ( class_exists ( '\WPGraphQL\Router ' ) && property_exists ( '\WPGraphQL\Router ' , '$http_status_code ' ) ) {
106+ $ status_code = \WPGraphQL \Router::$ http_status_code ;
107+ }
108+
109+ // Extract data and errors from the ExecutionResult object(s)
110+ $ response_data = null ;
111+ $ response_errors = null ;
112+
113+ if ( $ response instanceof ExecutionResult ) {
114+ $ response_data = $ response ->data ;
115+ $ response_errors = $ response ->errors ;
116+ } elseif ( is_array ( $ response ) && ! empty ( $ response [0 ] ) && $ response [0 ] instanceof ExecutionResult ) {
117+ // For batch requests, aggregate data/errors from all results
118+ $ response_data = array_map ( static fn ( $ res ) => $ res ->data , $ response );
119+ $ response_errors = array_reduce ( $ response , static fn ( $ carry , $ res ) => array_merge ( $ carry , $ res ->errors ?? [] ), [] );
120+ if ( empty ( $ response_errors ) ) {
121+ $ response_errors = null ; // Ensure it's null if no errors
122+ }
123+ }
124+
78125
79126 try {
80- $ context = [];
127+ $ context = [
128+ 'query ' => $ query ,
129+ 'operation_name ' => $ operation_name ,
130+ 'variables ' => $ variables ,
131+ 'status_code ' => $ status_code ,
132+ 'response_data ' => $ response_data ,
133+ 'response_errors ' => $ response_errors ,
134+ ];
81135 $ level = Level::Info;
82- $ context = apply_filters ( 'wpgraphql_logging_post_request_context ' , $ context , $ response , $ result , $ operation_name , $ query , $ variables );
83- $ level = apply_filters ( 'wpgraphql_logging_post_request_level ' , $ level , $ response , $ result , $ operation_name , $ query , $ variables );
136+
137+ // Apply filters for context and level
138+ $ context = apply_filters ( 'wpgraphql_logging_post_request_context ' , $ context , $ response , $ request_instance );
139+ $ level = apply_filters ( 'wpgraphql_logging_post_request_level ' , $ level , $ response , $ request_instance );
140+
84141 $ this ->logger ->log ( $ level , 'WPGraphQL Outgoing Response ' , $ context );
142+
143+ // Log errors specifically if present in the response
144+ if ( ! empty ( $ response_errors ) ) {
145+ $ this ->logger ->error (
146+ 'GraphQL query completed with errors. ' ,
147+ [
148+ 'query ' => $ query ,
149+ 'operation_name ' => $ operation_name ,
150+ 'status_code ' => $ status_code ,
151+ 'errors ' => array_map ( static fn ( $ error ) => $ error ->getMessage (), $ response_errors ), // Extract message from error object
152+ 'full_errors ' => $ response_errors , // Include full error details for debugging
153+ ]
154+ );
155+ }
85156 } catch ( \Throwable $ e ) {
86157 // @TODO - Handle logging errors gracefully.
87- error_log ( 'Error in log_post_request: ' . $ e ->getMessage () ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
158+ error_log ( 'Error in log_post_request: ' . $ e ->getMessage () . ' in ' . $ e -> getFile () . ' on line ' . $ e -> getLine () );
88159 }
89160 }
90161
91162 /**
92163 * Register actions and filters.
93164 */
94165 protected function setup (): void {
95-
96- // @TODO: Update POC and use pub/sub for query events.
97-
98166 /**
99167 * @psalm-suppress HookNotFound
100168 */
@@ -103,6 +171,6 @@ protected function setup(): void {
103171 /**
104172 * @psalm-suppress HookNotFound
105173 */
106- add_action ( 'graphql_process_http_request_response ' , [ $ this , 'log_post_request ' ], 10 , 5 );
174+ add_action ( 'graphql_after_execute ' , [ $ this , 'log_post_request ' ], 10 , 2 );
107175 }
108176}
0 commit comments