11import asyncio
22import functools
3+ import logging
34import sys
45import typing
56from types import TracebackType
1617else : # pragma: no cover
1718 from aiocontextvars import ContextVar
1819
20+ try : # pragma: no cover
21+ import click
22+
23+ # Extra log info for optional coloured terminal outputs.
24+ LOG_EXTRA = {
25+ "color_message" : "Query: " + click .style ("%s" , bold = True ) + " Args: %s"
26+ }
27+ CONNECT_EXTRA = {
28+ "color_message" : "Connected to database " + click .style ("%s" , bold = True )
29+ }
30+ DISCONNECT_EXTRA = {
31+ "color_message" : "Disconnected from database " + click .style ("%s" , bold = True )
32+ }
33+ except ImportError : # pragma: no cover
34+ LOG_EXTRA = {}
35+ CONNECT_EXTRA = {}
36+ DISCONNECT_EXTRA = {}
37+
38+
39+ logger = logging .getLogger ("databases" )
40+
1941
2042class Database :
2143 SUPPORTED_BACKENDS = {
@@ -63,6 +85,9 @@ async def connect(self) -> None:
6385 assert not self .is_connected , "Already connected."
6486
6587 await self ._backend .connect ()
88+ logger .info (
89+ "Connected to database %s" , self .url .obscure_password , extra = CONNECT_EXTRA
90+ )
6691 self .is_connected = True
6792
6893 if self ._force_rollback :
@@ -80,6 +105,11 @@ async def disconnect(self) -> None:
80105 await self ._global_transaction .__aexit__ ()
81106
82107 await self ._backend .disconnect ()
108+ logger .info (
109+ "Disconnected from database %s" ,
110+ self .url .obscure_password ,
111+ extra = DISCONNECT_EXTRA ,
112+ )
83113 self .is_connected = False
84114
85115 async def __aenter__ (self ) -> "Database" :
@@ -412,14 +442,17 @@ def replace(self, **kwargs: typing.Any) -> "DatabaseURL":
412442 components = self .components ._replace (** kwargs )
413443 return self .__class__ (components .geturl ())
414444
445+ @property
446+ def obscure_password (self ) -> str :
447+ if self .password :
448+ return self .replace (password = "********" )._url
449+ return self ._url
450+
415451 def __str__ (self ) -> str :
416452 return self ._url
417453
418454 def __repr__ (self ) -> str :
419- url = str (self )
420- if self .password :
421- url = str (self .replace (password = "********" ))
422- return f"{ self .__class__ .__name__ } ({ repr (url )} )"
455+ return f"{ self .__class__ .__name__ } ({ repr (self .obscure_password )} )"
423456
424457 def __eq__ (self , other : typing .Any ) -> bool :
425458 return str (self ) == str (other )
0 commit comments