From c4eececa093a9f5db486da10b7d96956250f3de8 Mon Sep 17 00:00:00 2001 From: Subham Sinha Date: Tue, 9 Dec 2025 17:52:41 +0530 Subject: [PATCH] feat(spanner): log client configuration at startup --- google/cloud/spanner_v1/client.py | 30 +++++++++++++ tests/unit/test_client.py | 70 ++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/google/cloud/spanner_v1/client.py b/google/cloud/spanner_v1/client.py index 5f72905616..bd513746df 100644 --- a/google/cloud/spanner_v1/client.py +++ b/google/cloud/spanner_v1/client.py @@ -292,6 +292,36 @@ def __init__( self._nth_client_id = Client.NTH_CLIENT.increment() self._nth_request = AtomicCounter(0) + self._log_spanner_options() + + def _log_spanner_options(self): + """Logs Spanner client options.""" + host = "spanner.googleapis.com" + if self._emulator_host: + host = self._emulator_host + elif self._experimental_host: + host = self._experimental_host + elif self._client_options and self._client_options.api_endpoint: + host = self._client_options.api_endpoint + + log.info( + "Spanner options: \n" + " Project ID: %s\n" + " Host: %s\n" + " Route to leader enabled: %s\n" + " Directed read options: %s\n" + " Default transaction options: %s\n" + " Observability options: %s\n" + " Built-in metrics enabled: %s", + self.project, + host, + self.route_to_leader_enabled, + self._directed_read_options, + self._default_transaction_options, + self._observability_options, + _get_spanner_enable_builtin_metrics_env(), + ) + @property def _next_nth_request(self): return self._nth_request.increment() diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index ab00d45268..5c8b4a21cf 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -98,7 +98,7 @@ def _constructor_test_helper( query_options=query_options, directed_read_options=directed_read_options, default_transaction_options=default_transaction_options, - **kwargs + **kwargs, ) expected_creds = expected_creds or creds.with_scopes.return_value @@ -329,6 +329,74 @@ def test_constructor_w_default_transaction_options(self): default_transaction_options=self.DEFAULT_TRANSACTION_OPTIONS, ) + def test_constructor_logs_options(self): + from google.cloud.spanner_v1 import client as MUT + + creds = build_scoped_credentials() + observability_options = {"enable_extended_tracing": True} + with self.assertLogs(MUT.__name__, level="INFO") as cm: + client = self._make_one( + project=self.PROJECT, + credentials=creds, + route_to_leader_enabled=False, + directed_read_options=self.DIRECTED_READ_OPTIONS, + default_transaction_options=self.DEFAULT_TRANSACTION_OPTIONS, + observability_options=observability_options, + ) + self.assertIsNotNone(client) + + self.assertEqual(len(cm.output), 1) + log_output = cm.output[0] + self.assertIn("Spanner options:", log_output) + self.assertIn(f"\n Project ID: {self.PROJECT}", log_output) + self.assertIn("\n Host: spanner.googleapis.com", log_output) + self.assertIn("\n Route to leader enabled: False", log_output) + self.assertIn( + f"\n Directed read options: {self.DIRECTED_READ_OPTIONS}", log_output + ) + self.assertIn( + f"\n Default transaction options: {self.DEFAULT_TRANSACTION_OPTIONS}", + log_output, + ) + self.assertIn(f"\n Observability options: {observability_options}", log_output) + # SPANNER_DISABLE_BUILTIN_METRICS is "true" from class-level patch + self.assertIn("\n Built-in metrics enabled: False", log_output) + + # Test with custom host + endpoint = "test.googleapis.com" + with self.assertLogs(MUT.__name__, level="INFO") as cm: + self._make_one( + project=self.PROJECT, + credentials=creds, + client_options={"api_endpoint": endpoint}, + ) + + self.assertEqual(len(cm.output), 1) + log_output = cm.output[0] + self.assertIn(f"\n Host: {endpoint}", log_output) + + # Test with emulator host + emulator_host = "localhost:9010" + with mock.patch.dict(os.environ, {MUT.EMULATOR_ENV_VAR: emulator_host}): + with self.assertLogs(MUT.__name__, level="INFO") as cm: + self._make_one(project=self.PROJECT) + + self.assertEqual(len(cm.output), 1) + log_output = cm.output[0] + self.assertIn(f"\n Host: {emulator_host}", log_output) + + # Test with experimental host + experimental_host = "exp.googleapis.com" + with self.assertLogs(MUT.__name__, level="INFO") as cm: + self._make_one( + project=self.PROJECT, + experimental_host=experimental_host, + ) + + self.assertEqual(len(cm.output), 1) + log_output = cm.output[0] + self.assertIn(f"\n Host: {experimental_host}", log_output) + @mock.patch("google.cloud.spanner_v1.client._get_spanner_emulator_host") def test_instance_admin_api(self, mock_em): from google.cloud.spanner_v1.client import SPANNER_ADMIN_SCOPE