holmesgpt 0.11.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of holmesgpt might be problematic. Click here for more details.

Files changed (183) hide show
  1. holmes/.git_archival.json +7 -0
  2. holmes/__init__.py +76 -0
  3. holmes/__init__.py.bak +76 -0
  4. holmes/clients/robusta_client.py +24 -0
  5. holmes/common/env_vars.py +47 -0
  6. holmes/config.py +526 -0
  7. holmes/core/__init__.py +0 -0
  8. holmes/core/conversations.py +578 -0
  9. holmes/core/investigation.py +152 -0
  10. holmes/core/investigation_structured_output.py +264 -0
  11. holmes/core/issue.py +54 -0
  12. holmes/core/llm.py +250 -0
  13. holmes/core/models.py +157 -0
  14. holmes/core/openai_formatting.py +51 -0
  15. holmes/core/performance_timing.py +72 -0
  16. holmes/core/prompt.py +42 -0
  17. holmes/core/resource_instruction.py +17 -0
  18. holmes/core/runbooks.py +26 -0
  19. holmes/core/safeguards.py +120 -0
  20. holmes/core/supabase_dal.py +540 -0
  21. holmes/core/tool_calling_llm.py +798 -0
  22. holmes/core/tools.py +566 -0
  23. holmes/core/tools_utils/__init__.py +0 -0
  24. holmes/core/tools_utils/tool_executor.py +65 -0
  25. holmes/core/tools_utils/toolset_utils.py +52 -0
  26. holmes/core/toolset_manager.py +418 -0
  27. holmes/interactive.py +229 -0
  28. holmes/main.py +1041 -0
  29. holmes/plugins/__init__.py +0 -0
  30. holmes/plugins/destinations/__init__.py +6 -0
  31. holmes/plugins/destinations/slack/__init__.py +2 -0
  32. holmes/plugins/destinations/slack/plugin.py +163 -0
  33. holmes/plugins/interfaces.py +32 -0
  34. holmes/plugins/prompts/__init__.py +48 -0
  35. holmes/plugins/prompts/_current_date_time.jinja2 +1 -0
  36. holmes/plugins/prompts/_default_log_prompt.jinja2 +11 -0
  37. holmes/plugins/prompts/_fetch_logs.jinja2 +36 -0
  38. holmes/plugins/prompts/_general_instructions.jinja2 +86 -0
  39. holmes/plugins/prompts/_global_instructions.jinja2 +12 -0
  40. holmes/plugins/prompts/_runbook_instructions.jinja2 +13 -0
  41. holmes/plugins/prompts/_toolsets_instructions.jinja2 +56 -0
  42. holmes/plugins/prompts/generic_ask.jinja2 +36 -0
  43. holmes/plugins/prompts/generic_ask_conversation.jinja2 +32 -0
  44. holmes/plugins/prompts/generic_ask_for_issue_conversation.jinja2 +50 -0
  45. holmes/plugins/prompts/generic_investigation.jinja2 +42 -0
  46. holmes/plugins/prompts/generic_post_processing.jinja2 +13 -0
  47. holmes/plugins/prompts/generic_ticket.jinja2 +12 -0
  48. holmes/plugins/prompts/investigation_output_format.jinja2 +32 -0
  49. holmes/plugins/prompts/kubernetes_workload_ask.jinja2 +84 -0
  50. holmes/plugins/prompts/kubernetes_workload_chat.jinja2 +39 -0
  51. holmes/plugins/runbooks/README.md +22 -0
  52. holmes/plugins/runbooks/__init__.py +100 -0
  53. holmes/plugins/runbooks/catalog.json +14 -0
  54. holmes/plugins/runbooks/jira.yaml +12 -0
  55. holmes/plugins/runbooks/kube-prometheus-stack.yaml +10 -0
  56. holmes/plugins/runbooks/networking/dns_troubleshooting_instructions.md +66 -0
  57. holmes/plugins/runbooks/upgrade/upgrade_troubleshooting_instructions.md +44 -0
  58. holmes/plugins/sources/github/__init__.py +77 -0
  59. holmes/plugins/sources/jira/__init__.py +123 -0
  60. holmes/plugins/sources/opsgenie/__init__.py +93 -0
  61. holmes/plugins/sources/pagerduty/__init__.py +147 -0
  62. holmes/plugins/sources/prometheus/__init__.py +0 -0
  63. holmes/plugins/sources/prometheus/models.py +104 -0
  64. holmes/plugins/sources/prometheus/plugin.py +154 -0
  65. holmes/plugins/toolsets/__init__.py +171 -0
  66. holmes/plugins/toolsets/aks-node-health.yaml +65 -0
  67. holmes/plugins/toolsets/aks.yaml +86 -0
  68. holmes/plugins/toolsets/argocd.yaml +70 -0
  69. holmes/plugins/toolsets/atlas_mongodb/instructions.jinja2 +8 -0
  70. holmes/plugins/toolsets/atlas_mongodb/mongodb_atlas.py +307 -0
  71. holmes/plugins/toolsets/aws.yaml +76 -0
  72. holmes/plugins/toolsets/azure_sql/__init__.py +0 -0
  73. holmes/plugins/toolsets/azure_sql/apis/alert_monitoring_api.py +600 -0
  74. holmes/plugins/toolsets/azure_sql/apis/azure_sql_api.py +309 -0
  75. holmes/plugins/toolsets/azure_sql/apis/connection_failure_api.py +445 -0
  76. holmes/plugins/toolsets/azure_sql/apis/connection_monitoring_api.py +251 -0
  77. holmes/plugins/toolsets/azure_sql/apis/storage_analysis_api.py +317 -0
  78. holmes/plugins/toolsets/azure_sql/azure_base_toolset.py +55 -0
  79. holmes/plugins/toolsets/azure_sql/azure_sql_instructions.jinja2 +137 -0
  80. holmes/plugins/toolsets/azure_sql/azure_sql_toolset.py +183 -0
  81. holmes/plugins/toolsets/azure_sql/install.md +66 -0
  82. holmes/plugins/toolsets/azure_sql/tools/__init__.py +1 -0
  83. holmes/plugins/toolsets/azure_sql/tools/analyze_connection_failures.py +324 -0
  84. holmes/plugins/toolsets/azure_sql/tools/analyze_database_connections.py +243 -0
  85. holmes/plugins/toolsets/azure_sql/tools/analyze_database_health_status.py +205 -0
  86. holmes/plugins/toolsets/azure_sql/tools/analyze_database_performance.py +249 -0
  87. holmes/plugins/toolsets/azure_sql/tools/analyze_database_storage.py +373 -0
  88. holmes/plugins/toolsets/azure_sql/tools/get_active_alerts.py +237 -0
  89. holmes/plugins/toolsets/azure_sql/tools/get_slow_queries.py +172 -0
  90. holmes/plugins/toolsets/azure_sql/tools/get_top_cpu_queries.py +170 -0
  91. holmes/plugins/toolsets/azure_sql/tools/get_top_data_io_queries.py +188 -0
  92. holmes/plugins/toolsets/azure_sql/tools/get_top_log_io_queries.py +180 -0
  93. holmes/plugins/toolsets/azure_sql/utils.py +83 -0
  94. holmes/plugins/toolsets/bash/__init__.py +0 -0
  95. holmes/plugins/toolsets/bash/bash_instructions.jinja2 +14 -0
  96. holmes/plugins/toolsets/bash/bash_toolset.py +208 -0
  97. holmes/plugins/toolsets/bash/common/bash.py +52 -0
  98. holmes/plugins/toolsets/bash/common/config.py +14 -0
  99. holmes/plugins/toolsets/bash/common/stringify.py +25 -0
  100. holmes/plugins/toolsets/bash/common/validators.py +24 -0
  101. holmes/plugins/toolsets/bash/grep/__init__.py +52 -0
  102. holmes/plugins/toolsets/bash/kubectl/__init__.py +100 -0
  103. holmes/plugins/toolsets/bash/kubectl/constants.py +96 -0
  104. holmes/plugins/toolsets/bash/kubectl/kubectl_describe.py +66 -0
  105. holmes/plugins/toolsets/bash/kubectl/kubectl_events.py +88 -0
  106. holmes/plugins/toolsets/bash/kubectl/kubectl_get.py +108 -0
  107. holmes/plugins/toolsets/bash/kubectl/kubectl_logs.py +20 -0
  108. holmes/plugins/toolsets/bash/kubectl/kubectl_run.py +46 -0
  109. holmes/plugins/toolsets/bash/kubectl/kubectl_top.py +81 -0
  110. holmes/plugins/toolsets/bash/parse_command.py +103 -0
  111. holmes/plugins/toolsets/confluence.yaml +19 -0
  112. holmes/plugins/toolsets/consts.py +5 -0
  113. holmes/plugins/toolsets/coralogix/api.py +158 -0
  114. holmes/plugins/toolsets/coralogix/toolset_coralogix_logs.py +103 -0
  115. holmes/plugins/toolsets/coralogix/utils.py +181 -0
  116. holmes/plugins/toolsets/datadog.py +153 -0
  117. holmes/plugins/toolsets/docker.yaml +46 -0
  118. holmes/plugins/toolsets/git.py +756 -0
  119. holmes/plugins/toolsets/grafana/__init__.py +0 -0
  120. holmes/plugins/toolsets/grafana/base_grafana_toolset.py +54 -0
  121. holmes/plugins/toolsets/grafana/common.py +68 -0
  122. holmes/plugins/toolsets/grafana/grafana_api.py +31 -0
  123. holmes/plugins/toolsets/grafana/loki_api.py +89 -0
  124. holmes/plugins/toolsets/grafana/tempo_api.py +124 -0
  125. holmes/plugins/toolsets/grafana/toolset_grafana.py +102 -0
  126. holmes/plugins/toolsets/grafana/toolset_grafana_loki.py +102 -0
  127. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.jinja2 +10 -0
  128. holmes/plugins/toolsets/grafana/toolset_grafana_tempo.py +299 -0
  129. holmes/plugins/toolsets/grafana/trace_parser.py +195 -0
  130. holmes/plugins/toolsets/helm.yaml +42 -0
  131. holmes/plugins/toolsets/internet/internet.py +275 -0
  132. holmes/plugins/toolsets/internet/notion.py +137 -0
  133. holmes/plugins/toolsets/kafka.py +638 -0
  134. holmes/plugins/toolsets/kubernetes.yaml +255 -0
  135. holmes/plugins/toolsets/kubernetes_logs.py +426 -0
  136. holmes/plugins/toolsets/kubernetes_logs.yaml +42 -0
  137. holmes/plugins/toolsets/logging_utils/__init__.py +0 -0
  138. holmes/plugins/toolsets/logging_utils/logging_api.py +217 -0
  139. holmes/plugins/toolsets/logging_utils/types.py +0 -0
  140. holmes/plugins/toolsets/mcp/toolset_mcp.py +135 -0
  141. holmes/plugins/toolsets/newrelic.py +222 -0
  142. holmes/plugins/toolsets/opensearch/__init__.py +0 -0
  143. holmes/plugins/toolsets/opensearch/opensearch.py +245 -0
  144. holmes/plugins/toolsets/opensearch/opensearch_logs.py +151 -0
  145. holmes/plugins/toolsets/opensearch/opensearch_traces.py +211 -0
  146. holmes/plugins/toolsets/opensearch/opensearch_traces_instructions.jinja2 +12 -0
  147. holmes/plugins/toolsets/opensearch/opensearch_utils.py +166 -0
  148. holmes/plugins/toolsets/prometheus/prometheus.py +818 -0
  149. holmes/plugins/toolsets/prometheus/prometheus_instructions.jinja2 +38 -0
  150. holmes/plugins/toolsets/rabbitmq/api.py +398 -0
  151. holmes/plugins/toolsets/rabbitmq/rabbitmq_instructions.jinja2 +37 -0
  152. holmes/plugins/toolsets/rabbitmq/toolset_rabbitmq.py +222 -0
  153. holmes/plugins/toolsets/robusta/__init__.py +0 -0
  154. holmes/plugins/toolsets/robusta/robusta.py +235 -0
  155. holmes/plugins/toolsets/robusta/robusta_instructions.jinja2 +24 -0
  156. holmes/plugins/toolsets/runbook/__init__.py +0 -0
  157. holmes/plugins/toolsets/runbook/runbook_fetcher.py +78 -0
  158. holmes/plugins/toolsets/service_discovery.py +92 -0
  159. holmes/plugins/toolsets/servicenow/install.md +37 -0
  160. holmes/plugins/toolsets/servicenow/instructions.jinja2 +3 -0
  161. holmes/plugins/toolsets/servicenow/servicenow.py +198 -0
  162. holmes/plugins/toolsets/slab.yaml +20 -0
  163. holmes/plugins/toolsets/utils.py +137 -0
  164. holmes/plugins/utils.py +14 -0
  165. holmes/utils/__init__.py +0 -0
  166. holmes/utils/cache.py +84 -0
  167. holmes/utils/cert_utils.py +40 -0
  168. holmes/utils/default_toolset_installation_guide.jinja2 +44 -0
  169. holmes/utils/definitions.py +13 -0
  170. holmes/utils/env.py +53 -0
  171. holmes/utils/file_utils.py +56 -0
  172. holmes/utils/global_instructions.py +20 -0
  173. holmes/utils/holmes_status.py +22 -0
  174. holmes/utils/holmes_sync_toolsets.py +80 -0
  175. holmes/utils/markdown_utils.py +55 -0
  176. holmes/utils/pydantic_utils.py +54 -0
  177. holmes/utils/robusta.py +10 -0
  178. holmes/utils/tags.py +97 -0
  179. holmesgpt-0.11.5.dist-info/LICENSE.txt +21 -0
  180. holmesgpt-0.11.5.dist-info/METADATA +400 -0
  181. holmesgpt-0.11.5.dist-info/RECORD +183 -0
  182. holmesgpt-0.11.5.dist-info/WHEEL +4 -0
  183. holmesgpt-0.11.5.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,309 @@
1
+ from typing import Dict, List
2
+ import pyodbc
3
+ import logging
4
+ import struct
5
+ from azure.core.credentials import TokenCredential
6
+ from azure.mgmt.sql import SqlManagementClient
7
+
8
+
9
+ class AzureSQLAPIClient:
10
+ def __init__(
11
+ self,
12
+ credential: TokenCredential,
13
+ subscription_id: str,
14
+ ):
15
+ self.sql_client = SqlManagementClient(credential, subscription_id)
16
+ self.credential = credential
17
+
18
+ def _get_access_token_struct(self) -> bytes:
19
+ """Get access token formatted as struct for Azure SQL Database ODBC authentication."""
20
+ try:
21
+ token = self.credential.get_token("https://database.windows.net/.default")
22
+ if not token or not token.token:
23
+ raise ValueError(
24
+ "Failed to obtain valid access token from Azure credential"
25
+ )
26
+
27
+ # Encode token as UTF-16-LE and pack as struct with length prefix
28
+ token_bytes = token.token.encode("UTF-16-LE")
29
+ token_struct = struct.pack(
30
+ f"<I{len(token_bytes)}s", len(token_bytes), token_bytes
31
+ )
32
+ return token_struct
33
+ except Exception as e:
34
+ logging.error(f"Failed to get access token: {str(e)}")
35
+ raise ConnectionError(f"Azure authentication failed: {str(e)}") from e
36
+
37
+ def execute_query(
38
+ self, server_name: str, database_name: str, query: str
39
+ ) -> List[Dict]:
40
+ """Execute a T-SQL query against the Azure SQL database."""
41
+ conn = None
42
+ cursor = None
43
+
44
+ # Validate connection parameters to prevent segfault
45
+ if not server_name or not database_name:
46
+ raise ValueError("Server name and database name must be provided")
47
+
48
+ # Fall back to Azure AD token authentication only if no SQL credentials
49
+ try:
50
+ access_token_struct = self._get_access_token_struct()
51
+
52
+ # Build connection string with access token
53
+ connection_string = (
54
+ f"Driver={{ODBC Driver 18 for SQL Server}};"
55
+ f"Server=tcp:{server_name}.database.windows.net,1433;"
56
+ f"Database={database_name};"
57
+ f"Encrypt=yes;"
58
+ f"TrustServerCertificate=no;"
59
+ f"Connection Timeout=30;"
60
+ )
61
+
62
+ logging.info(
63
+ f"Attempting to connect to {server_name}.database.windows.net with Azure AD token"
64
+ )
65
+
66
+ # Create connection with properly formatted access token
67
+ SQL_COPT_SS_ACCESS_TOKEN = (
68
+ 1256 # This connection option is defined by microsoft in msodbcsql.h
69
+ )
70
+ conn = pyodbc.connect(
71
+ connection_string,
72
+ attrs_before={SQL_COPT_SS_ACCESS_TOKEN: access_token_struct},
73
+ timeout=10,
74
+ )
75
+ cursor = conn.cursor()
76
+
77
+ cursor.execute(query)
78
+ columns = [column[0] for column in cursor.description]
79
+ results = []
80
+
81
+ for row in cursor.fetchall():
82
+ results.append(dict(zip(columns, row)))
83
+
84
+ return results
85
+
86
+ except pyodbc.Error as e:
87
+ error_msg = (
88
+ f"ODBC Error connecting to {server_name}.{database_name}: {str(e)}"
89
+ )
90
+ logging.error(error_msg, exc_info=True)
91
+ raise ConnectionError(error_msg) from e
92
+ except Exception as e:
93
+ error_msg = (
94
+ f"Failed to execute query on {server_name}.{database_name}: {str(e)}"
95
+ )
96
+ logging.error(error_msg, exc_info=True)
97
+ raise
98
+ finally:
99
+ # Ensure resources are properly cleaned up
100
+ if cursor:
101
+ try:
102
+ cursor.close()
103
+ except Exception:
104
+ pass
105
+ if conn:
106
+ try:
107
+ conn.close()
108
+ except Exception:
109
+ pass
110
+
111
+ def get_database_advisors(
112
+ self,
113
+ subscription_id: str,
114
+ resource_group: str,
115
+ server_name: str,
116
+ database_name: str,
117
+ ) -> Dict:
118
+ advisors = self.sql_client.database_advisors.list_by_database(
119
+ resource_group_name=resource_group,
120
+ server_name=server_name,
121
+ database_name=database_name,
122
+ )
123
+ return {"value": [advisor.as_dict() for advisor in advisors]}
124
+
125
+ def get_database_recommended_actions(
126
+ self,
127
+ subscription_id: str,
128
+ resource_group: str,
129
+ server_name: str,
130
+ database_name: str,
131
+ advisor_name: str,
132
+ ) -> Dict:
133
+ actions = self.sql_client.database_recommended_actions.list_by_database_advisor(
134
+ resource_group_name=resource_group,
135
+ server_name=server_name,
136
+ database_name=database_name,
137
+ advisor_name=advisor_name,
138
+ )
139
+ return {"value": [action.as_dict() for action in actions]}
140
+
141
+ def get_database_usages(
142
+ self,
143
+ subscription_id: str,
144
+ resource_group: str,
145
+ server_name: str,
146
+ database_name: str,
147
+ ) -> Dict:
148
+ usages = self.sql_client.database_usages.list_by_database(
149
+ resource_group_name=resource_group,
150
+ server_name=server_name,
151
+ database_name=database_name,
152
+ )
153
+ return {"value": [usage.as_dict() for usage in usages]}
154
+
155
+ def get_database_operations(
156
+ self,
157
+ subscription_id: str,
158
+ resource_group: str,
159
+ server_name: str,
160
+ database_name: str,
161
+ ) -> Dict:
162
+ operations = self.sql_client.database_operations.list_by_database(
163
+ resource_group_name=resource_group,
164
+ server_name=server_name,
165
+ database_name=database_name,
166
+ )
167
+ return {"value": [op.as_dict() for op in operations]}
168
+
169
+ def get_database_automatic_tuning(
170
+ self,
171
+ subscription_id: str,
172
+ resource_group: str,
173
+ server_name: str,
174
+ database_name: str,
175
+ ) -> Dict:
176
+ tuning = self.sql_client.database_automatic_tuning.get(
177
+ resource_group_name=resource_group,
178
+ server_name=server_name,
179
+ database_name=database_name,
180
+ )
181
+ return tuning.as_dict()
182
+
183
+ def get_top_cpu_queries(
184
+ self,
185
+ subscription_id: str,
186
+ resource_group: str,
187
+ server_name: str,
188
+ database_name: str,
189
+ top_count: int = 15,
190
+ hours_back: int = 2,
191
+ ) -> List[Dict]:
192
+ """Get top CPU consuming queries from Query Store."""
193
+ query = f"""
194
+ SELECT TOP {top_count}
195
+ qt.query_sql_text,
196
+ rs.avg_cpu_time,
197
+ rs.count_executions as execution_count,
198
+ (rs.avg_cpu_time * rs.count_executions) as total_cpu_time,
199
+ rs.max_cpu_time,
200
+ rs.min_cpu_time,
201
+ CAST(rs.last_execution_time as DATETIME2) as last_execution_time,
202
+ rs.avg_duration,
203
+ (rs.avg_duration * rs.count_executions) as total_duration
204
+ FROM sys.query_store_query_text qt
205
+ JOIN sys.query_store_query q ON qt.query_text_id = q.query_text_id
206
+ JOIN sys.query_store_plan p ON q.query_id = p.query_id
207
+ JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
208
+ WHERE rs.last_execution_time > DATEADD(hour, -{hours_back}, GETDATE())
209
+ ORDER BY rs.avg_cpu_time DESC;
210
+ """
211
+
212
+ return self.execute_query(server_name, database_name, query)
213
+
214
+ def get_slow_queries(
215
+ self,
216
+ subscription_id: str,
217
+ resource_group: str,
218
+ server_name: str,
219
+ database_name: str,
220
+ top_count: int = 15,
221
+ hours_back: int = 2,
222
+ ) -> List[Dict]:
223
+ """Get slow/long-running queries from Query Store."""
224
+ query = f"""
225
+ SELECT TOP {top_count}
226
+ qt.query_sql_text,
227
+ rs.avg_duration,
228
+ rs.count_executions as execution_count,
229
+ (rs.avg_duration * rs.count_executions) as total_duration,
230
+ rs.max_duration,
231
+ rs.min_duration,
232
+ CAST(rs.last_execution_time as DATETIME2) as last_execution_time,
233
+ rs.avg_cpu_time,
234
+ (rs.avg_cpu_time * rs.count_executions) as total_cpu_time
235
+ FROM sys.query_store_query_text qt
236
+ JOIN sys.query_store_query q ON qt.query_text_id = q.query_text_id
237
+ JOIN sys.query_store_plan p ON q.query_id = p.query_id
238
+ JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
239
+ WHERE rs.last_execution_time > DATEADD(hour, -{hours_back}, GETDATE())
240
+ ORDER BY rs.avg_duration DESC;
241
+ """
242
+
243
+ return self.execute_query(server_name, database_name, query)
244
+
245
+ def get_top_data_io_queries(
246
+ self,
247
+ subscription_id: str,
248
+ resource_group: str,
249
+ server_name: str,
250
+ database_name: str,
251
+ top_count: int = 15,
252
+ hours_back: int = 2,
253
+ ) -> List[Dict]:
254
+ """Get top data I/O consuming queries from Query Store."""
255
+ query = f"""
256
+ SELECT TOP {top_count}
257
+ qt.query_sql_text,
258
+ rs.avg_logical_io_reads as avg_logical_reads,
259
+ rs.count_executions as execution_count,
260
+ (rs.avg_logical_io_reads * rs.count_executions) as total_logical_reads,
261
+ rs.max_logical_io_reads as max_logical_reads,
262
+ rs.min_logical_io_reads as min_logical_reads,
263
+ rs.avg_logical_io_writes as avg_logical_writes,
264
+ (rs.avg_logical_io_writes * rs.count_executions) as total_logical_writes,
265
+ rs.max_logical_io_writes as max_logical_writes,
266
+ rs.min_logical_io_writes as min_logical_writes,
267
+ CAST(rs.last_execution_time as DATETIME2) as last_execution_time,
268
+ rs.avg_cpu_time,
269
+ rs.avg_duration
270
+ FROM sys.query_store_query_text qt
271
+ JOIN sys.query_store_query q ON qt.query_text_id = q.query_text_id
272
+ JOIN sys.query_store_plan p ON q.query_id = p.query_id
273
+ JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
274
+ WHERE rs.last_execution_time > DATEADD(hour, -{hours_back}, GETDATE())
275
+ ORDER BY rs.avg_logical_io_reads DESC;
276
+ """
277
+
278
+ return self.execute_query(server_name, database_name, query)
279
+
280
+ def get_top_log_io_queries(
281
+ self,
282
+ subscription_id: str,
283
+ resource_group: str,
284
+ server_name: str,
285
+ database_name: str,
286
+ top_count: int = 15,
287
+ hours_back: int = 2,
288
+ ) -> List[Dict]:
289
+ """Get top log I/O consuming queries from Query Store."""
290
+ query = f"""
291
+ SELECT TOP {top_count}
292
+ qt.query_sql_text,
293
+ rs.avg_log_bytes_used,
294
+ rs.count_executions as execution_count,
295
+ (rs.avg_log_bytes_used * rs.count_executions) as total_log_bytes_used,
296
+ rs.max_log_bytes_used,
297
+ rs.min_log_bytes_used,
298
+ CAST(rs.last_execution_time as DATETIME2) as last_execution_time,
299
+ rs.avg_cpu_time,
300
+ rs.avg_duration
301
+ FROM sys.query_store_query_text qt
302
+ JOIN sys.query_store_query q ON qt.query_text_id = q.query_text_id
303
+ JOIN sys.query_store_plan p ON q.query_id = p.query_id
304
+ JOIN sys.query_store_runtime_stats rs ON p.plan_id = rs.plan_id
305
+ WHERE rs.last_execution_time > DATEADD(hour, -{hours_back}, GETDATE())
306
+ ORDER BY rs.avg_log_bytes_used DESC;
307
+ """
308
+
309
+ return self.execute_query(server_name, database_name, query)