24
24
from .version_utils import VersionUtils
25
25
26
26
# default collection intervals in seconds
27
+ DEFAULT_EXTENSIONS_COLLECTION_INTERVAL = 600
27
28
DEFAULT_SETTINGS_COLLECTION_INTERVAL = 600
28
29
DEFAULT_SCHEMAS_COLLECTION_INTERVAL = 600
29
30
DEFAULT_RESOURCES_COLLECTION_INTERVAL = 300
30
31
DEFAULT_SETTINGS_IGNORED_PATTERNS = ["plpgsql%" ]
31
32
32
- # PG_SETTINGS_QURERY is used to collect all the settings from the pg_settings table
33
+ # PG_EXTENSION_INFO_QUERY is used to collect extension names and versions from
34
+ # the pg_extension table. Schema names and roles are retrieved from their re-
35
+ # spective catalog tables.
36
+ PG_EXTENSION_INFO_QUERY = """
37
+ SELECT
38
+ e.oid id,
39
+ e.extname name,
40
+ r.rolname owner,
41
+ ns.nspname schema_name,
42
+ e.extrelocatable relocatable,
43
+ e.extversion version
44
+ FROM pg_extension e
45
+ LEFT JOIN pg_namespace ns on e.extnamespace = ns.oid
46
+ JOIN pg_roles r ON e.extowner = r.oid;
47
+ """
48
+
49
+ # PG_SETTINGS_QUERY is used to collect all the settings from the pg_settings table
33
50
# Edge case: If source is 'session', it uses reset_val
34
51
# (which represents the value that the setting would revert to on session end or reset),
35
52
# otherwise, it uses the current setting value.
@@ -211,6 +228,9 @@ def __init__(self, check, config, shutdown_callback):
211
228
self .pg_settings_ignored_patterns = config .settings_metadata_config .get (
212
229
"ignored_settings_patterns" , DEFAULT_SETTINGS_IGNORED_PATTERNS
213
230
)
231
+ self .pg_extensions_collection_interval = config .settings_metadata_config .get (
232
+ "collection_interval" , DEFAULT_EXTENSIONS_COLLECTION_INTERVAL
233
+ )
214
234
self .pg_settings_collection_interval = config .settings_metadata_config .get (
215
235
"collection_interval" , DEFAULT_SETTINGS_COLLECTION_INTERVAL
216
236
)
@@ -221,9 +241,12 @@ def __init__(self, check, config, shutdown_callback):
221
241
"collection_interval" , DEFAULT_RESOURCES_COLLECTION_INTERVAL
222
242
)
223
243
224
- # by default, send resources every 5 minutes
244
+ # by default, send resources every 10 minutes
225
245
self .collection_interval = min (
226
- resources_collection_interval , self .pg_settings_collection_interval , self .schemas_collection_interval
246
+ resources_collection_interval ,
247
+ self .pg_extensions_collection_interval ,
248
+ self .pg_settings_collection_interval ,
249
+ self .schemas_collection_interval ,
227
250
)
228
251
229
252
super (PostgresMetadata , self ).__init__ (
@@ -240,10 +263,12 @@ def __init__(self, check, config, shutdown_callback):
240
263
self ._check = check
241
264
self ._config = config
242
265
self .db_pool = self ._check .db_pool
266
+ self ._collect_extensions_enabled = is_affirmative (config .settings_metadata_config .get ("enabled" , False ))
243
267
self ._collect_pg_settings_enabled = is_affirmative (config .settings_metadata_config .get ("enabled" , False ))
244
268
self ._collect_schemas_enabled = is_affirmative (config .schemas_metadata_config .get ("enabled" , False ))
245
269
self ._is_schemas_collection_in_progress = False
246
270
self ._pg_settings_cached = None
271
+ self ._time_since_last_extension_query = 0
247
272
self ._time_since_last_settings_query = 0
248
273
self ._last_schemas_query_time = 0
249
274
self ._conn_ttl_ms = self ._config .idle_connection_timeout
@@ -268,6 +293,40 @@ def run_job(self):
268
293
self .report_postgres_metadata ()
269
294
self ._check .db_pool .prune_connections ()
270
295
296
+ @tracked_method (agent_check_getter = agent_check_getter )
297
+ def report_postgres_extensions (self ):
298
+ # Only query if configured, according to interval
299
+ elapsed_s = time .time () - self ._time_since_last_extension_query
300
+ if elapsed_s >= self .pg_extensions_collection_interval and self ._collect_extensions_enabled :
301
+ self ._extensions_cached = self ._collect_postgres_extensions ()
302
+ event = {
303
+ "host" : self ._check .reported_hostname ,
304
+ "database_instance" : self ._check .database_identifier ,
305
+ "agent_version" : datadog_agent .get_version (),
306
+ "dbms" : "postgres" ,
307
+ "kind" : "pg_extension" ,
308
+ "collection_interval" : self .collection_interval ,
309
+ "dbms_version" : payload_pg_version (self ._check .version ),
310
+ "tags" : self ._tags_no_db ,
311
+ "timestamp" : time .time () * 1000 ,
312
+ "cloud_metadata" : self ._check .cloud_metadata ,
313
+ "metadata" : self ._extensions_cached ,
314
+ }
315
+ self ._check .database_monitoring_metadata (json .dumps (event , default = default_json_event_encoding ))
316
+
317
+ @tracked_method (agent_check_getter = agent_check_getter )
318
+ def _collect_postgres_extensions (self ):
319
+ with self ._check ._get_main_db () as conn :
320
+ with conn .cursor (cursor_factory = CommenterDictCursor ) as cursor :
321
+ self ._time_since_last_extension_query = time .time ()
322
+
323
+ # Get loaded extensions
324
+ cursor .execute (PG_EXTENSION_INFO_QUERY )
325
+ rows = cursor .fetchall ()
326
+
327
+ self ._log .debug ("Loaded %s rows from pg_extension" , len (rows ))
328
+ return [dict (row ) for row in rows ]
329
+
271
330
@tracked_method (agent_check_getter = agent_check_getter )
272
331
def report_postgres_metadata (self ):
273
332
# Only query for settings if configured to do so &&
0 commit comments