flexmetric 0.4.2__py3-none-any.whl → 0.4.4__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.
@@ -0,0 +1,55 @@
1
+ import clickhouse_connect
2
+ import sqlite3
3
+ from flexmetric.logging_module.logger import get_logger
4
+ import os
5
+
6
+ logger = get_logger(__name__)
7
+
8
+ logger.info("database logs")
9
+
10
+ def create_clickhouse_client(db_conf):
11
+ id = db_conf.get('id')
12
+ host = db_conf.get('host', 'localhost')
13
+ port = db_conf.get('port', 9440)
14
+ username = db_conf.get('username', 'default')
15
+ password = db_conf.get('password', '')
16
+
17
+ client_cert = db_conf.get('client_cert')
18
+ client_cert_key = db_conf.get('client_cert_key')
19
+ ca_cert = db_conf.get('ca_cert')
20
+
21
+ secure = bool(client_cert and client_cert_key and ca_cert)
22
+
23
+ settings = {
24
+ 'host': host,
25
+ 'port': port,
26
+ 'username': username,
27
+ 'password': password,
28
+ 'secure': secure,
29
+ }
30
+
31
+ if secure:
32
+ settings.update({
33
+ 'client_cert': client_cert,
34
+ 'client_cert_key': client_cert_key,
35
+ 'secure_ca': ca_cert,
36
+ })
37
+
38
+ client = clickhouse_connect.get_client(**settings)
39
+ logger.info(f"Clickhouse connection '{id}' created")
40
+ return client
41
+
42
+
43
+ def create_sqlite_client(db_conf: dict):
44
+ db_path = db_conf.get('db_connection')
45
+ db_name = db_conf.get('db_name', 'default_sqlite_db')
46
+
47
+ if not db_path or not os.path.isfile(db_path):
48
+ raise FileNotFoundError(f"SQLite database file not found at {db_path}")
49
+
50
+ try:
51
+ conn = sqlite3.connect(db_path)
52
+ logger.info(f"SQLite connection '{db_name}' created")
53
+ return conn
54
+ except Exception as e:
55
+ raise ConnectionError(f"Failed to create SQLite client: {e}")
@@ -1,6 +1,11 @@
1
1
  import yaml
2
- import sqlite3
3
2
  import re
3
+ from flexmetric.metric_process.database_connections import create_clickhouse_client,create_sqlite_client
4
+ from flexmetric.metric_process.queries_execution import execute_clickhouse_command,execute_sqlite_query
5
+ from flexmetric.logging_module.logger import get_logger
6
+ logger = get_logger(__name__)
7
+
8
+ logger.info("query execution")
4
9
 
5
10
 
6
11
  def read_yaml_file(file_path):
@@ -17,74 +22,119 @@ def get_database_config(databases, db_name):
17
22
  )
18
23
 
19
24
 
20
- def execute_sqlite_query(db_path, query):
21
- try:
22
- conn = sqlite3.connect(db_path)
23
- cursor = conn.cursor()
24
- cursor.execute(query)
25
- result = cursor.fetchone()
26
- conn.close()
27
- return float(result[0]) if result and result[0] is not None else None
28
- except Exception as e:
29
- print(f"[ERROR] SQLite query failed on {db_path}: {e}")
30
- return None
31
-
32
25
 
33
26
  def is_safe_query(query):
34
- # Remove leading spaces and brackets
35
27
  cleaned_query = query.strip().lower()
36
- # Match only queries that start with "select"
37
28
  return re.match(r"^\(*\s*select", cleaned_query) is not None
38
29
 
39
30
 
40
- def process_database_queries(queries_file, databases_file):
41
- # Get queries from queries file
42
- queries_config = read_yaml_file(queries_file)
43
- # Get database from database file
44
- databases_config = read_yaml_file(databases_file)
31
+ def read_yaml_file(filepath: str) -> dict:
32
+ try:
33
+ with open(filepath, 'r') as f:
34
+ return yaml.safe_load(f)
35
+ except Exception as e:
36
+ raise Exception(f"Error reading {filepath}: {e}")
45
37
 
46
- commands = queries_config.get("commands", [])
47
- databases = databases_config.get("databases", [])
48
38
 
49
- all_results = []
39
+ def create_clients_from_config(config_file: str):
40
+ db_configs = read_yaml_file(config_file).get('databases', [])
41
+ clients = {}
42
+
43
+ for db_conf in db_configs:
44
+ db_id = db_conf.get('id')
45
+ logger.info(db_id)
46
+ db_type = db_conf.get('type')
47
+
48
+ if not db_id:
49
+ logger.info(f"Skipping unnamed database block: {db_conf}")
50
+ continue
50
51
 
51
- for cmd in commands:
52
52
  try:
53
- db_conf = get_database_config(databases, cmd["database"])
53
+ if db_type == 'clickhouse':
54
+ client = create_clickhouse_client(db_conf)
55
+ clients[db_id] = client
56
+ logger.info(clients)
54
57
 
55
- if db_conf["db_type"] != "sqlite":
56
- print(
57
- f"[WARN] Unsupported database type: {db_conf['db_type']} in command {cmd['name']}"
58
- )
59
- continue
58
+ elif db_type == 'sqlite':
59
+ client = create_sqlite_client(db_conf)
60
+ clients[db_id] = client
61
+ logger.info(clients)
60
62
 
61
- db_path = db_conf["db_connection"]
62
- query = cmd["query"]
63
- labels = cmd.get('labels', [])
64
- label_values = cmd.get('label_values', [])
65
- main_label = cmd.get('main_label', 'default_db_metric')
63
+ else:
64
+ logger.info(f"Unsupported database type: {db_type}")
66
65
 
67
- # check if query is safe
68
- if is_safe_query(query):
69
- value = execute_sqlite_query(db_path, query)
66
+ except Exception as e:
67
+ logger.error(f"Failed to create client '{db_id}': {e}")
68
+ return clients
69
+
70
+ def process_and_get_value(cmd,rows_data,column_names):
71
+ labels = cmd.get('labels', [])
72
+ value_column = cmd.get('value_column', [])
73
+ label_values = cmd.get('label_values', [])
74
+ main_label = cmd.get('main_label', 'default_db_metric')
75
+ missing_columns = [col for col in labels + [value_column] if col not in column_names]
76
+ if missing_columns:
77
+ raise ValueError(f"Missing columns in result: {missing_columns}")
78
+ rows = []
79
+ for row in rows_data:
80
+ label_values = [str(row[column_names.index(col)]) for col in labels]
81
+ value = row[column_names.index(value_column)]
82
+
83
+ rows.append({
84
+ 'label': label_values,
85
+ 'value': value
86
+ })
87
+
88
+ return {
89
+ 'result': rows,
90
+ 'labels': labels,
91
+ 'main_label': main_label
92
+ }
93
+
94
+ def execute_commands(clients: dict, commands_file: str):
95
+ commands = read_yaml_file(commands_file).get('commands', [])
96
+ results = []
97
+ for cmd in commands:
98
+ print("CMD : ",cmd)
99
+ if cmd == None:
100
+ continue
101
+ cmd_id = cmd.get('id')
102
+ db_id = cmd.get('database_id')
103
+ db_type = cmd.get('type')
104
+ query = cmd.get('query')
105
+ if not cmd_id or not db_id or not db_type or not query:
106
+ logger.info(f"Missing required fields in command '{cmd_id}'")
107
+ continue
108
+
109
+ client = clients.get(db_id)
110
+ logger.info(clients)
111
+ logger.info(db_id)
112
+ logger.info(client)
113
+ if not client:
114
+ logger.info(f"No client for database_id '{db_id}' in command '{cmd_id}'")
115
+ continue
116
+ try:
117
+ if db_type == 'clickhouse':
118
+ logger.info("In clickhouse")
119
+ response,column_names = execute_clickhouse_command(client,query)
120
+ result = process_and_get_value(cmd,response,column_names)
121
+ results.append(result)
122
+ elif db_type == 'sqlite':
123
+ response ,column_names = execute_sqlite_query(client,query)
124
+ result = process_and_get_value(cmd,response,column_names)
125
+ results.append(result)
70
126
  else:
71
- print(f"[WARN] Unsupported query type: {query}")
72
- return None
73
-
74
- if not isinstance(label_values, list):
75
- label_values = [label_values]
76
-
77
- result = {
78
- 'result': [{
79
- 'label': label_values,
80
- 'value': value
81
- }],
82
- 'labels': labels,
83
- 'main_label': main_label
84
- }
85
- all_results.append(result)
127
+ logger.info(f"Unknown type '{db_type}' in command '{cmd_id}'")
128
+ continue
129
+
86
130
  except Exception as e:
87
- print(
88
- f"[ERROR] Processing command '{cmd.get('name', 'unknown')}' failed: {e}"
89
- )
90
- return all_results
131
+ logger.error(f"Command '{cmd_id}' failed: {e}")
132
+ return results
133
+
134
+
135
+ def process_database_queries(queries_file, databases_file):
136
+ try:
137
+ client_configs = create_clients_from_config(databases_file)
138
+ return execute_commands(clients=client_configs,commands_file=queries_file)
139
+ except Exception as ex:
140
+ logger.error(f"Exception : {ex}")
@@ -256,4 +256,4 @@ def main():
256
256
  run_flask(args.host, args.port)
257
257
  # # args = arguments()
258
258
  # # measure(args)
259
- # main()
259
+ main()
@@ -0,0 +1,24 @@
1
+ from flexmetric.logging_module.logger import get_logger
2
+ logger = get_logger(__name__)
3
+
4
+ logger.info("query execution")
5
+
6
+ def execute_clickhouse_command(client, command: str):
7
+ try:
8
+ result = client.query(command)
9
+ row_list = result.result_rows
10
+ column_names = result.column_names
11
+ return row_list,column_names
12
+ except Exception as e:
13
+ logger.error(f"Error executing command: {e}")
14
+ return None
15
+ def execute_sqlite_query(conn, query):
16
+ try:
17
+ cursor = conn.cursor()
18
+ cursor.execute(query)
19
+ rows = cursor.fetchall()
20
+ column_names = [desc[0] for desc in cursor.description]
21
+ return rows, column_names
22
+ except Exception as ex:
23
+ logger.error(f"Exception during SQLite query: {ex}")
24
+ return [], []
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flexmetric
3
- Version: 0.4.2
4
- Summary: A secure flexible Prometheus exporter for commands, databases, functions, and scripts.
3
+ Version: 0.4.4
4
+ Summary: A secure flexible Prometheus exporter for commands, databases, functions.
5
5
  Home-page: https://github.com/nikhillingadhal1999/flexmetric
6
6
  Author: Nikhil Lingadhal
7
7
  License: MIT
@@ -20,6 +20,7 @@ Requires-Dist: setuptools
20
20
  Requires-Dist: wheel
21
21
  Requires-Dist: twine
22
22
  Requires-Dist: flask
23
+ Requires-Dist: clickhouse-connect
23
24
  Dynamic: author
24
25
  Dynamic: classifier
25
26
  Dynamic: description
@@ -188,21 +189,52 @@ Filesystem Size Used Avail Use% Mounted on
188
189
  | `timeout_seconds` | Maximum time (in seconds) to wait for the command to complete. If it exceeds this time, the command is aborted. |
189
190
 
190
191
  ## Database mode
191
-
192
+ file - database.yaml
192
193
  ```yaml
193
194
  databases:
194
- - name: mydb
195
- db_type: sqlite
196
- db_connection: /path/to/my.db
197
- ````
195
+ - id: "active_user_count"
196
+ type: "clickhouse"
197
+ host: "localhost"
198
+ port: 8123
199
+ username: "default"
200
+ password: ""
201
+ client_cert: ""
202
+ client_key: ""
203
+ ca_cert: ""
204
+
205
+ - id: "userdb"
206
+ type: "sqlite"
207
+ db_connection: "/path/to/my.db"
208
+ ```
209
+ file - queries.yaml
198
210
  ```yaml
199
211
  commands:
200
- - name: user_count
201
- database: userdb
202
- query: "SELECT COUNT(*) FROM users;"
203
- main_label: database_user_count
204
- labels: ["database_name", "table_name"]
205
- label_values: ["userdb", "users"]
212
+ - id: "active_user_count"
213
+ type: "clickhouse"
214
+ database_id: "active_user_count"
215
+ query: |
216
+ SELECT
217
+ country AS country_name,
218
+ COUNT() AS active_user_count
219
+ FROM users
220
+ WHERE is_active = 1
221
+ GROUP BY country
222
+ main_label: "active_user_count"
223
+ labels: ["country_name"]
224
+ value_column: "active_user_count"
225
+
226
+ - id: "list_all_users_sqlite"
227
+ type: "sqlite"
228
+ database_id: "userdb"
229
+ query: |
230
+ SELECT
231
+ id,
232
+ name
233
+ FROM users
234
+ main_label: "user_list"
235
+ labels: ["id", "name"]
236
+ value_column: "id"
237
+
206
238
  ```
207
239
  ## Functions mode
208
240
 
@@ -6,13 +6,15 @@ flexmetric/file_recognition/exec_file.py,sha256=9wBbnqPConxtLhqWTgigEr8VQG-fa9K_
6
6
  flexmetric/logging_module/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  flexmetric/logging_module/logger.py,sha256=hXj9m2Q_KxJVI5YRHRoK6PXV5tO6NmvuVjq2Ipx_1tE,447
8
8
  flexmetric/metric_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- flexmetric/metric_process/database_processing.py,sha256=hbVbzIdO21NF0F3nILJ4d1x8fkks9P8s-wtn8qZ91qw,2739
9
+ flexmetric/metric_process/database_connections.py,sha256=CqYrzXkwoB7yq4SpIu7_jDDNHSemaPsfDJj5DLwHSXM,1574
10
+ flexmetric/metric_process/database_processing.py,sha256=_7C1DyC9jtgiTUti4_YYFpQlE0BlJwbaEx3OPpDCGSs,4633
10
11
  flexmetric/metric_process/expiring_queue.py,sha256=1oC0MjloitPiRo7yDgVarz81ETEQavKI_W-GFUvp5_Y,1920
11
12
  flexmetric/metric_process/process_commands.py,sha256=clGMQhLNcuJUO1gElpAS9Dyk0KU5w41DIguczjo7ceA,4089
12
- flexmetric/metric_process/prometheus_agent.py,sha256=rZ9pQrqA3J_-sJtl48qkFIHIUH01iert05u4SmGN4Yc,7714
13
+ flexmetric/metric_process/prometheus_agent.py,sha256=pDPqiPtHLv1cfF6dPRNg2LtAhVF1UKf8MNZ_S9xuRQY,7712
14
+ flexmetric/metric_process/queries_execution.py,sha256=NdnQ9Flbc8_6VOSY3aHxSmhcCsZf2zIxKTnc0hCp1oU,790
13
15
  flexmetric/metric_process/views.py,sha256=BY695dCpTkJRc1KLC9RNpFTieFdHeHvyqyefmHhMauE,3297
14
- flexmetric-0.4.2.dist-info/METADATA,sha256=qu-Y5W68ysqbjGsw-6NtQUC8D166f78-ivkUcdzvLls,10651
15
- flexmetric-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- flexmetric-0.4.2.dist-info/entry_points.txt,sha256=urVePn5EWr3JqNvkYP7OsB_h2_Bqvv-Wq1MJRBexm8A,79
17
- flexmetric-0.4.2.dist-info/top_level.txt,sha256=zBlrNwKhXUNhgu9RRZnXxYwYnmE-eocRe6wKSmQROA4,11
18
- flexmetric-0.4.2.dist-info/RECORD,,
16
+ flexmetric-0.4.4.dist-info/METADATA,sha256=EvJA9-Ov8X-KyO810Qj_UQsDeoEA38SyLTcnKU-6dgc,11276
17
+ flexmetric-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ flexmetric-0.4.4.dist-info/entry_points.txt,sha256=urVePn5EWr3JqNvkYP7OsB_h2_Bqvv-Wq1MJRBexm8A,79
19
+ flexmetric-0.4.4.dist-info/top_level.txt,sha256=zBlrNwKhXUNhgu9RRZnXxYwYnmE-eocRe6wKSmQROA4,11
20
+ flexmetric-0.4.4.dist-info/RECORD,,