Skip to content

Commit 14d6b1c

Browse files
feat: add standardized debug logging (#354)
1 parent c9ef40d commit 14d6b1c

File tree

5 files changed

+80
-10
lines changed

5 files changed

+80
-10
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,28 @@ pool = sqlalchemy.create_engine(
438438
connector.close()
439439
```
440440

441+
### Debug Logging
442+
443+
The AlloyDB Python Connector uses the standard [Python logging module][python-logging]
444+
for debug logging support.
445+
446+
Add the below code to your application to enable debug logging with the AlloyDB
447+
Python Connector:
448+
449+
```python
450+
import logging
451+
452+
logging.basicConfig(format="%(asctime)s [%(levelname)s]: %(message)s")
453+
logger = logging.getLogger(name="google.cloud.alloydb.connector")
454+
logger.setLevel(logging.DEBUG)
455+
```
456+
457+
For more details on configuring logging, please refer to the
458+
[Python logging docs][configure-logging].
459+
460+
[python-logging]: https://docs.python.org/3/library/logging.html
461+
[configure-logging]: https://docs.python.org/3/howto/logging.html#configuring-logging
462+
441463
## Support policy
442464

443465
### Major version lifecycle

google/cloud/alloydb/connector/async_connector.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
import asyncio
18+
import logging
1819
from types import TracebackType
1920
from typing import Any, Dict, Optional, Type, TYPE_CHECKING, Union
2021

@@ -33,6 +34,8 @@
3334
if TYPE_CHECKING:
3435
from google.auth.credentials import Credentials
3536

37+
logger = logging.getLogger(name=__name__)
38+
3639

3740
class AsyncConnector:
3841
"""A class to configure and create connections to Cloud SQL instances
@@ -141,10 +144,18 @@ async def connect(
141144
cache = self._cache[instance_uri]
142145
else:
143146
if self._refresh_strategy == RefreshStrategy.LAZY:
147+
logger.debug(
148+
f"['{instance_uri}']: Refresh strategy is set to lazy refresh"
149+
)
144150
cache = LazyRefreshCache(instance_uri, self._client, self._keys)
145151
else:
152+
logger.debug(
153+
f"['{instance_uri}']: Refresh strategy is set to background"
154+
" refresh"
155+
)
146156
cache = RefreshAheadCache(instance_uri, self._client, self._keys)
147157
self._cache[instance_uri] = cache
158+
logger.debug(f"['{instance_uri}']: Connection info added to cache")
148159

149160
connect_func = {
150161
"asyncpg": asyncpg.connect,
@@ -168,6 +179,7 @@ async def connect(
168179
ip_type = IPTypes(ip_type.upper())
169180
conn_info = await cache.connect_info()
170181
ip_address = conn_info.get_preferred_ip(ip_type)
182+
logger.debug(f"['{instance_uri}']: Connecting to {ip_address}:5433")
171183

172184
# callable to be used for auto IAM authn
173185
def get_authentication_token() -> str:

google/cloud/alloydb/connector/client.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,6 @@ async def _get_metadata(
118118
Returns:
119119
dict: IP addresses of the AlloyDB instance.
120120
"""
121-
logger.debug(f"['{project}/{region}/{cluster}/{name}']: Requesting metadata")
122-
123121
headers = {
124122
"Authorization": f"Bearer {self._credentials.token}",
125123
}
@@ -165,8 +163,6 @@ async def _get_client_certificate(
165163
Tuple[str, list[str]]: Tuple containing the CA certificate
166164
and certificate chain for the AlloyDB instance.
167165
"""
168-
logger.debug(f"['{project}/{region}/{cluster}']: Requesting client certificate")
169-
170166
headers = {
171167
"Authorization": f"Bearer {self._credentials.token}",
172168
}
@@ -252,4 +248,6 @@ async def get_connection_info(
252248

253249
async def close(self) -> None:
254250
"""Close AlloyDBClient gracefully."""
251+
logger.debug("Waiting for connector's http client to close")
255252
await self._client.close()
253+
logger.debug("Closed connector's http client")

google/cloud/alloydb/connector/connector.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import asyncio
1818
from functools import partial
19+
import logging
1920
import socket
2021
import struct
2122
from threading import Thread
@@ -41,6 +42,8 @@
4142

4243
from google.auth.credentials import Credentials
4344

45+
logger = logging.getLogger(name=__name__)
46+
4447
# the port the AlloyDB server-side proxy receives connections on
4548
SERVER_PROXY_PORT = 5433
4649
# the maximum amount of time to wait before aborting a metadata exchange
@@ -170,10 +173,18 @@ async def connect_async(self, instance_uri: str, driver: str, **kwargs: Any) ->
170173
cache = self._cache[instance_uri]
171174
else:
172175
if self._refresh_strategy == RefreshStrategy.LAZY:
176+
logger.debug(
177+
f"['{instance_uri}']: Refresh strategy is set to lazy refresh"
178+
)
173179
cache = LazyRefreshCache(instance_uri, self._client, self._keys)
174180
else:
181+
logger.debug(
182+
f"['{instance_uri}']: Refresh strategy is set to background"
183+
" refresh"
184+
)
175185
cache = RefreshAheadCache(instance_uri, self._client, self._keys)
176186
self._cache[instance_uri] = cache
187+
logger.debug(f"['{instance_uri}']: Connection info added to cache")
177188

178189
connect_func = {
179190
"pg8000": pg8000.connect,
@@ -197,6 +208,7 @@ async def connect_async(self, instance_uri: str, driver: str, **kwargs: Any) ->
197208
ip_type = IPTypes(ip_type.upper())
198209
conn_info = await cache.connect_info()
199210
ip_address = conn_info.get_preferred_ip(ip_type)
211+
logger.debug(f"['{instance_uri}']: Connecting to {ip_address}:5433")
200212

201213
# synchronous drivers are blocking and run using executor
202214
try:

google/cloud/alloydb/connector/instance.py

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
from __future__ import annotations
1616

1717
import asyncio
18+
from datetime import datetime
19+
from datetime import timedelta
20+
from datetime import timezone
1821
import logging
1922
import re
2023
from typing import Tuple, TYPE_CHECKING
@@ -104,7 +107,9 @@ async def _perform_refresh(self) -> ConnectionInfo:
104107
ConnectionInfo: Result of the refresh operation.
105108
"""
106109
self._refresh_in_progress.set()
107-
logger.debug(f"['{self._instance_uri}']: Entered _perform_refresh")
110+
logger.debug(
111+
f"['{self._instance_uri}']: Connection info refresh operation started"
112+
)
108113

109114
try:
110115
await self._refresh_rate_limiter.acquire()
@@ -115,10 +120,19 @@ async def _perform_refresh(self) -> ConnectionInfo:
115120
self._name,
116121
self._keys,
117122
)
123+
logger.debug(
124+
f"['{self._instance_uri}']: Connection info refresh operation"
125+
" complete"
126+
)
127+
logger.debug(
128+
f"['{self._instance_uri}']: Current certificate expiration = "
129+
f"{connection_info.expiration.isoformat()}"
130+
)
118131

119-
except Exception:
132+
except Exception as e:
120133
logger.debug(
121-
f"['{self._instance_uri}']: Error occurred during _perform_refresh."
134+
f"['{self._instance_uri}']: Connection info refresh operation"
135+
f" failed: {str(e)}"
122136
)
123137
raise
124138

@@ -153,7 +167,6 @@ async def _refresh_operation(self, delay: int) -> ConnectionInfo:
153167
refresh_task: asyncio.Task
154168
try:
155169
if delay > 0:
156-
logger.debug(f"['{self._instance_uri}']: Entering sleep")
157170
await asyncio.sleep(delay)
158171
refresh_task = asyncio.create_task(self._perform_refresh())
159172
refresh_result = await refresh_task
@@ -162,6 +175,11 @@ async def _refresh_operation(self, delay: int) -> ConnectionInfo:
162175
raise RefreshError(
163176
f"['{self._instance_uri}']: Invalid refresh operation. Certficate appears to be expired."
164177
)
178+
except asyncio.CancelledError:
179+
logger.debug(
180+
f"['{self._instance_uri}']: Scheduled refresh operation cancelled"
181+
)
182+
raise
165183
# bad refresh attempt
166184
except Exception:
167185
logger.info(
@@ -180,6 +198,12 @@ async def _refresh_operation(self, delay: int) -> ConnectionInfo:
180198
self._current = refresh_task
181199
# calculate refresh delay based on certificate expiration
182200
delay = _seconds_until_refresh(refresh_result.expiration)
201+
logger.debug(
202+
f"['{self._instance_uri}']: Connection info refresh operation"
203+
" scheduled for "
204+
f"{(datetime.now(timezone.utc) + timedelta(seconds=delay)).isoformat(timespec='seconds')} "
205+
f"(now + {timedelta(seconds=delay)})"
206+
)
183207
self._next = self._schedule_refresh(delay)
184208

185209
return refresh_result
@@ -207,9 +231,11 @@ async def close(self) -> None:
207231
"""
208232
Cancel refresh tasks.
209233
"""
210-
logger.debug(f"['{self._instance_uri}']: Waiting for _current to be cancelled")
234+
logger.debug(
235+
f"['{self._instance_uri}']: Canceling connection info refresh"
236+
" operation tasks"
237+
)
211238
self._current.cancel()
212-
logger.debug(f"['{self._instance_uri}']: Waiting for _next to be cancelled")
213239
self._next.cancel()
214240
# gracefully wait for tasks to cancel
215241
tasks = asyncio.gather(self._current, self._next, return_exceptions=True)

0 commit comments

Comments
 (0)