@@ -112,13 +112,17 @@ of this software and associated documentation files (the "Software"), to deal
112112import com .google .gson .Gson ;
113113
114114import org .apache .arrow .flight .sql .FlightSqlClient ;
115+ import org .slf4j .Logger ;
116+ import org .slf4j .LoggerFactory ;
115117
116118/**
117119 * Client to execute SQL queries against Spice.ai Cloud and Spice.ai OSS.
118120 * Supports both regular queries and parameterized queries using ADBC.
119121 */
120122public class SpiceClient implements AutoCloseable {
121123
124+ private static final Logger logger = LoggerFactory .getLogger (SpiceClient .class );
125+
122126 private static final long BYTES_PER_MB = 1024L * 1024L ;
123127
124128 // Cached Gson instance for JSON serialization (thread-safe)
@@ -210,6 +214,7 @@ public SpiceClient(String appId, String apiKey, URI flightAddress, URI httpAddre
210214 if (Strings .isNullOrEmpty (apiKey )) {
211215 this .flightClient = new FlightSqlClient (builder .build ());
212216 initRetryers ();
217+ logger .debug ("SpiceClient initialized (unauthenticated) - flightAddress={}" , this .flightAddress );
213218 return ;
214219 }
215220
@@ -238,6 +243,8 @@ public SpiceClient(String appId, String apiKey, URI flightAddress, URI httpAddre
238243
239244 // Initialize cached retryers (immutable, built once)
240245 initRetryers ();
246+
247+ logger .debug ("SpiceClient initialized (authenticated) - flightAddress={}, appId={}" , this .flightAddress , this .appId );
241248 }
242249
243250 /**
@@ -286,10 +293,14 @@ public FlightStream query(String sql) throws ExecutionException {
286293 throw new IllegalArgumentException ("No SQL query provided" );
287294 }
288295
296+ logger .debug ("Executing query: {}" , sql );
289297 try {
290- return this .queryInternalWithRetry (sql );
298+ FlightStream result = this .queryInternalWithRetry (sql );
299+ logger .debug ("Query executed successfully" );
300+ return result ;
291301 } catch (RetryException e ) {
292302 Throwable err = e .getLastFailedAttempt ().getExceptionCause ();
303+ logger .error ("Query failed after {} attempts: {}" , e .getNumberOfFailedAttempts (), err .getMessage ());
293304 throw new ExecutionException ("Failed to execute query due to error: " + err .toString (), err );
294305 }
295306 }
@@ -339,13 +350,18 @@ public ArrowReader queryWithParams(String sql, Object... params) throws Executio
339350 throw new IllegalArgumentException ("No SQL query provided" );
340351 }
341352
353+ logger .debug ("Executing parameterized query with {} parameters: {}" , params != null ? params .length : 0 , sql );
342354 try {
343355 initADBCIfNeeded ();
344- return queryWithParamsInternal (sql , params );
356+ ArrowReader result = queryWithParamsInternal (sql , params );
357+ logger .debug ("Parameterized query executed successfully" );
358+ return result ;
345359 } catch (AdbcException e ) {
360+ logger .error ("Parameterized query failed: {}" , e .getMessage ());
346361 throw new ExecutionException ("Failed to execute parameterized query: " + e .getMessage (), e );
347362 } catch (RetryException e ) {
348363 Throwable err = e .getLastFailedAttempt ().getExceptionCause ();
364+ logger .error ("Parameterized query failed after {} attempts: {}" , e .getNumberOfFailedAttempts (), err .getMessage ());
349365 throw new ExecutionException ("Failed to execute parameterized query due to error: " + err .toString (), err );
350366 }
351367 }
@@ -359,6 +375,8 @@ private synchronized void initADBCIfNeeded() throws AdbcException {
359375 return ;
360376 }
361377
378+ logger .debug ("Initializing ADBC connection" );
379+
362380 // Format the URI for ADBC FlightSQL driver
363381 String uri = this .flightAddress .toString ();
364382
@@ -391,6 +409,8 @@ private synchronized void initADBCIfNeeded() throws AdbcException {
391409 FlightSqlDriver driver = new FlightSqlDriver (allocator );
392410 adbcDatabase = driver .open (options );
393411 adbcConnection = adbcDatabase .connect ();
412+
413+ logger .debug ("ADBC connection established - uri={}" , uri );
394414 }
395415
396416 /**
@@ -400,16 +420,18 @@ private void closeADBC() {
400420 if (adbcConnection != null ) {
401421 try {
402422 adbcConnection .close ();
423+ logger .debug ("ADBC connection closed" );
403424 } catch (Exception e ) {
404- // Log but don't fail
425+ logger . warn ( "Error closing ADBC connection: {}" , e . getMessage ());
405426 }
406427 adbcConnection = null ;
407428 }
408429 if (adbcDatabase != null ) {
409430 try {
410431 adbcDatabase .close ();
432+ logger .debug ("ADBC database closed" );
411433 } catch (Exception e ) {
412- // Log but don't fail
434+ logger . warn ( "Error closing ADBC database: {}" , e . getMessage ());
413435 }
414436 adbcDatabase = null ;
415437 }
@@ -787,6 +809,7 @@ public void refreshDataset(String dataset, RefreshOptions refreshOptions) throws
787809 throw new IllegalArgumentException ("No dataset name provided" );
788810 }
789811
812+ logger .debug ("Refreshing dataset: {}" , dataset );
790813 try {
791814 HttpRequest .Builder builder = HttpRequest .newBuilder ()
792815 .uri (new URI (String .format ("%s/v1/datasets/%s/acceleration/refresh" , this .httpAddress , dataset )))
@@ -804,19 +827,23 @@ public void refreshDataset(String dataset, RefreshOptions refreshOptions) throws
804827 HttpResponse <String > response = HTTP_CLIENT .send (request , HttpResponse .BodyHandlers .ofString ());
805828
806829 if (response .statusCode () != 201 ) {
830+ logger .error ("Dataset refresh failed - dataset={}, statusCode={}, response={}" , dataset , response .statusCode (), response .body ());
807831 throw new ExecutionException (
808832 String .format ("Failed to trigger dataset refresh. Status Code: %d, Response: %s" ,
809833 response .statusCode (),
810834 response .body ()),
811835 null );
812836 }
837+ logger .debug ("Dataset refresh triggered successfully: {}" , dataset );
813838 } catch (ExecutionException e ) {
814839 // no need to wrap ExecutionException
815840 throw e ;
816841 } catch (ConnectException err ) {
842+ logger .error ("Cannot connect to Spice runtime at {}: {}" , this .httpAddress , err .getMessage ());
817843 throw new ExecutionException (
818844 String .format ("The Spice runtime is unavailable at %s. Is it running?" , this .httpAddress ), err );
819845 } catch (Exception err ) {
846+ logger .error ("Dataset refresh failed: {}" , err .getMessage ());
820847 throw new ExecutionException ("Failed to trigger dataset refresh due to error: " + err .toString (), err );
821848 }
822849 }
@@ -845,28 +872,34 @@ private boolean shouldRetry(CallStatus status) {
845872
846873 @ Override
847874 public void close () throws Exception {
875+ logger .debug ("Closing SpiceClient" );
848876 List <Exception > exceptions = new ArrayList <>();
849877
850878 // Close ADBC resources first
851879 try {
852880 closeADBC ();
853881 } catch (Exception e ) {
882+ logger .warn ("Error during ADBC cleanup: {}" , e .getMessage ());
854883 exceptions .add (e );
855884 }
856885
857886 // Close Flight client
858887 try {
859888 this .flightClient .close ();
889+ logger .debug ("Flight client closed" );
860890 } catch (Exception e ) {
891+ logger .warn ("Error closing Flight client: {}" , e .getMessage ());
861892 exceptions .add (e );
862893 }
863894
864895 // Close allocator
865896 try {
866897 if (this .allocator != null ) {
867898 this .allocator .close ();
899+ logger .debug ("Arrow allocator closed" );
868900 }
869901 } catch (Exception e ) {
902+ logger .warn ("Error closing Arrow allocator: {}" , e .getMessage ());
870903 exceptions .add (e );
871904 }
872905
@@ -875,7 +908,10 @@ public void close() throws Exception {
875908 for (int i = 1 ; i < exceptions .size (); i ++) {
876909 first .addSuppressed (exceptions .get (i ));
877910 }
911+ logger .error ("SpiceClient closed with {} error(s)" , exceptions .size ());
878912 throw first ;
879913 }
914+
915+ logger .debug ("SpiceClient closed successfully" );
880916 }
881917}
0 commit comments