flexmetric 0.1.0__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.
- flexmetric/__init__.py +0 -0
- flexmetric/file_recognition/__init__.py +0 -0
- flexmetric/file_recognition/exec_file.py +125 -0
- flexmetric/logging_module/__init__.py +0 -0
- flexmetric/logging_module/logger.py +15 -0
- flexmetric/metric_process/__init__.py +0 -0
- flexmetric/metric_process/database_processing.py +75 -0
- flexmetric/metric_process/process_commands.py +73 -0
- flexmetric/metric_process/prometheus_agent.py +140 -0
- flexmetric-0.1.0.dist-info/METADATA +152 -0
- flexmetric-0.1.0.dist-info/RECORD +14 -0
- flexmetric-0.1.0.dist-info/WHEEL +5 -0
- flexmetric-0.1.0.dist-info/entry_points.txt +2 -0
- flexmetric-0.1.0.dist-info/top_level.txt +1 -0
flexmetric/__init__.py
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,125 @@
|
|
1
|
+
import os
|
2
|
+
import ast
|
3
|
+
import importlib.util
|
4
|
+
from typing import Dict, List, Any
|
5
|
+
|
6
|
+
|
7
|
+
def get_python_files(folder_path: str) -> List[str]:
|
8
|
+
|
9
|
+
return [ os.path.join(root,file) for root, _ , files in os.walk(folder_path) for file in files if file.endswith('.py') ]
|
10
|
+
|
11
|
+
def extract_functions_with_code(file_path: str) -> Dict[str, str]:
|
12
|
+
with open(file_path, "r") as file:
|
13
|
+
file_content = file.read() # Read file content once
|
14
|
+
|
15
|
+
tree = ast.parse(file_content)
|
16
|
+
|
17
|
+
functions = {}
|
18
|
+
for node in ast.walk(tree):
|
19
|
+
if isinstance(node, ast.FunctionDef):
|
20
|
+
function_name = node.name
|
21
|
+
function_code = ast.get_source_segment(file_content, node)
|
22
|
+
functions[function_name] = function_code
|
23
|
+
return functions
|
24
|
+
|
25
|
+
def analyze_dependencies(function_code: str) -> List[str]:
|
26
|
+
try:
|
27
|
+
tree = ast.parse(function_code)
|
28
|
+
except SyntaxError as e:
|
29
|
+
print(f"Syntax error in function code: {e}")
|
30
|
+
return []
|
31
|
+
|
32
|
+
dependencies = []
|
33
|
+
for node in ast.walk(tree):
|
34
|
+
if isinstance(node, ast.Call):
|
35
|
+
if isinstance(node.func, ast.Name):
|
36
|
+
dependencies.append(node.func.id)
|
37
|
+
elif isinstance(node.func, ast.Attribute):
|
38
|
+
dependencies.append(node.func.attr)
|
39
|
+
|
40
|
+
return dependencies
|
41
|
+
|
42
|
+
def find_function_dependencies(file_path: str) -> Dict[str, List[str]]:
|
43
|
+
functions_with_code = extract_functions_with_code(file_path)
|
44
|
+
function_dependencies = {}
|
45
|
+
|
46
|
+
for func_name, func_code in functions_with_code.items():
|
47
|
+
dependencies = analyze_dependencies(func_code)
|
48
|
+
function_dependencies[func_name] = dependencies
|
49
|
+
|
50
|
+
return function_dependencies
|
51
|
+
|
52
|
+
def topological_sort(dependency_graph: Dict[str, List[str]]) -> List[str]:
|
53
|
+
visited = set()
|
54
|
+
stack = []
|
55
|
+
|
56
|
+
def visit(node):
|
57
|
+
if node not in visited:
|
58
|
+
visited.add(node)
|
59
|
+
for neighbor in dependency_graph.get(node, []):
|
60
|
+
visit(neighbor)
|
61
|
+
stack.append(node)
|
62
|
+
|
63
|
+
for node in dependency_graph:
|
64
|
+
visit(node)
|
65
|
+
|
66
|
+
return stack[::-1]
|
67
|
+
|
68
|
+
def execute_function(file_path: str, function_name: str, attributes: Dict[str,Any]):
|
69
|
+
dependencies = find_function_dependencies(file_path)
|
70
|
+
sorted_functions = topological_sort(dependencies)
|
71
|
+
|
72
|
+
if function_name not in sorted_functions:
|
73
|
+
return
|
74
|
+
|
75
|
+
module_name = os.path.splitext(os.path.basename(file_path))[0]
|
76
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
77
|
+
module = importlib.util.module_from_spec(spec)
|
78
|
+
spec.loader.exec_module(module)
|
79
|
+
|
80
|
+
for func in sorted_functions:
|
81
|
+
if hasattr(module, func):
|
82
|
+
func_to_call = getattr(module, func)
|
83
|
+
if callable(func_to_call):
|
84
|
+
try:
|
85
|
+
args = attributes.get(func,())
|
86
|
+
if not isinstance(args,tuple):
|
87
|
+
args = (args,)
|
88
|
+
result = func_to_call(*args)
|
89
|
+
if func == function_name:
|
90
|
+
return result
|
91
|
+
except Exception as e:
|
92
|
+
print(f"Error executing {func}: {e}")
|
93
|
+
|
94
|
+
def main(folder_path: str, function_name: str, attributes: Dict[str,Any]):
|
95
|
+
python_files = get_python_files(folder_path)
|
96
|
+
|
97
|
+
for file_path in python_files:
|
98
|
+
return execute_function(file_path, function_name,attributes)
|
99
|
+
|
100
|
+
|
101
|
+
def read_function_file(custom_path):
|
102
|
+
with open(custom_path,'r') as file:
|
103
|
+
lines = file.readlines()
|
104
|
+
return lines
|
105
|
+
|
106
|
+
def get_env_dir():
|
107
|
+
current_path = os.getcwd()
|
108
|
+
return os.path.abspath(os.path.join(current_path,'env/bin/activate'))
|
109
|
+
|
110
|
+
def install_requirements(requirements_path):
|
111
|
+
env_dir = get_env_dir()
|
112
|
+
command = f"source {env_dir}; pip3 install -r {requirements_path}"
|
113
|
+
os.system(command)
|
114
|
+
|
115
|
+
def load_variable_from_file(file_path, variable_name="ARGS"):
|
116
|
+
spec = importlib.util.spec_from_file_location("module_name", file_path)
|
117
|
+
module = importlib.util.module_from_spec(spec)
|
118
|
+
spec.loader.exec_module(module)
|
119
|
+
return getattr(module, variable_name, None)
|
120
|
+
|
121
|
+
def execute_functions(folder_path,file_path):
|
122
|
+
functions = read_function_file(file_path)
|
123
|
+
args= { 'collect_disk_metrics' : ()}
|
124
|
+
result = [ main(folder_path, function_name.strip('\n'), args) for function_name in functions ]
|
125
|
+
return result
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import logging
|
2
|
+
import sys
|
3
|
+
|
4
|
+
|
5
|
+
def get_logger(name: str , level = logging.DEBUG) -> logging.Logger:
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
if not logger.hasHandlers():
|
8
|
+
logger.setLevel(level)
|
9
|
+
handler = logging.StreamHandler(sys.stdout)
|
10
|
+
handler.setFormatter(
|
11
|
+
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
12
|
+
)
|
13
|
+
logger.addHandler(handler)
|
14
|
+
return logger
|
15
|
+
|
File without changes
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import yaml
|
2
|
+
import sqlite3
|
3
|
+
import re
|
4
|
+
|
5
|
+
def read_yaml_file(file_path):
|
6
|
+
with open(file_path, 'r') as f:
|
7
|
+
return yaml.safe_load(f)
|
8
|
+
|
9
|
+
def get_database_config(databases, db_name):
|
10
|
+
for db in databases:
|
11
|
+
if db['name'] == db_name:
|
12
|
+
return db
|
13
|
+
raise ValueError(f"[ERROR] Database config for '{db_name}' not found in database.yaml.")
|
14
|
+
|
15
|
+
def execute_sqlite_query(db_path, query):
|
16
|
+
try:
|
17
|
+
conn = sqlite3.connect(db_path)
|
18
|
+
cursor = conn.cursor()
|
19
|
+
cursor.execute(query)
|
20
|
+
result = cursor.fetchone()
|
21
|
+
conn.close()
|
22
|
+
return float(result[0]) if result and result[0] is not None else None
|
23
|
+
except Exception as e:
|
24
|
+
print(f"[ERROR] SQLite query failed on {db_path}: {e}")
|
25
|
+
return None
|
26
|
+
|
27
|
+
def is_safe_query(query):
|
28
|
+
# Remove leading spaces and brackets
|
29
|
+
cleaned_query = query.strip().lower()
|
30
|
+
# Match only queries that start with "select"
|
31
|
+
return re.match(r'^\(*\s*select', cleaned_query) is not None
|
32
|
+
|
33
|
+
def process_database_queries(queries_file, databases_file):
|
34
|
+
# Get queries from queries file
|
35
|
+
queries_config = read_yaml_file(queries_file)
|
36
|
+
# Get database from database file
|
37
|
+
databases_config = read_yaml_file(databases_file)
|
38
|
+
|
39
|
+
commands = queries_config.get('commands', [])
|
40
|
+
databases = databases_config.get('databases', [])
|
41
|
+
|
42
|
+
all_results = []
|
43
|
+
|
44
|
+
for cmd in commands:
|
45
|
+
try:
|
46
|
+
db_conf = get_database_config(databases, cmd['database'])
|
47
|
+
|
48
|
+
if db_conf['db_type'] != 'sqlite':
|
49
|
+
print(f"[WARN] Unsupported database type: {db_conf['db_type']} in command {cmd['name']}")
|
50
|
+
continue
|
51
|
+
|
52
|
+
db_path = db_conf['db_connection']
|
53
|
+
query = cmd['query']
|
54
|
+
label = cmd['label']
|
55
|
+
label_value = cmd['label_value']
|
56
|
+
# check if query is safe
|
57
|
+
if is_safe_query(query):
|
58
|
+
value = execute_sqlite_query(db_path, query)
|
59
|
+
else:
|
60
|
+
print(f"[WARN] Unsupported query type: {query}")
|
61
|
+
return None
|
62
|
+
|
63
|
+
if value is not None:
|
64
|
+
result = {
|
65
|
+
'result': [{'label': label_value, 'value': value}],
|
66
|
+
'labels': [label]
|
67
|
+
}
|
68
|
+
all_results.append(result)
|
69
|
+
else:
|
70
|
+
print(f"[INFO] No result for command '{cmd['name']}' on database '{cmd['database']}'")
|
71
|
+
|
72
|
+
except Exception as e:
|
73
|
+
print(f"[ERROR] Processing command '{cmd.get('name', 'unknown')}' failed: {e}")
|
74
|
+
|
75
|
+
return all_results
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import subprocess
|
2
|
+
import yaml
|
3
|
+
import re
|
4
|
+
|
5
|
+
# 1. Read YAML commands
|
6
|
+
def read_commands_from_yaml(file_path):
|
7
|
+
with open(file_path, 'r') as f:
|
8
|
+
config = yaml.safe_load(f)
|
9
|
+
return config.get('commands', [])
|
10
|
+
|
11
|
+
# 2. Execute command with timeout
|
12
|
+
def execute_command_with_timeout(command, timeout):
|
13
|
+
try:
|
14
|
+
result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=timeout)
|
15
|
+
if result.returncode != 0:
|
16
|
+
return ''
|
17
|
+
return result.stdout.strip()
|
18
|
+
except subprocess.TimeoutExpired:
|
19
|
+
return ''
|
20
|
+
|
21
|
+
def process_commands(config_file):
|
22
|
+
commands = read_commands_from_yaml(config_file)
|
23
|
+
all_results = []
|
24
|
+
|
25
|
+
for cmd_info in commands:
|
26
|
+
command = cmd_info['command']
|
27
|
+
label_name = cmd_info['label']
|
28
|
+
timeout = cmd_info.get('timeout_seconds', 30)
|
29
|
+
label_column = cmd_info.get('label_column', -1)
|
30
|
+
value_column = cmd_info.get('value_column', 0)
|
31
|
+
fixed_label_value = cmd_info.get('label_value')
|
32
|
+
|
33
|
+
raw_output = execute_command_with_timeout(command, timeout)
|
34
|
+
lines = raw_output.strip().splitlines()
|
35
|
+
|
36
|
+
result_list = []
|
37
|
+
|
38
|
+
for line in lines:
|
39
|
+
parts = line.strip().split()
|
40
|
+
if not parts:
|
41
|
+
continue
|
42
|
+
if label_column == 'fixed':
|
43
|
+
label = fixed_label_value or 'label'
|
44
|
+
else:
|
45
|
+
try:
|
46
|
+
label = parts[label_column]
|
47
|
+
except IndexError:
|
48
|
+
label = 'unknown'
|
49
|
+
try:
|
50
|
+
raw_value = parts[value_column]
|
51
|
+
cleaned_value = re.sub(r'[^\d\.\-]', '', raw_value)
|
52
|
+
value = float(cleaned_value) if cleaned_value else 1
|
53
|
+
except (IndexError, ValueError):
|
54
|
+
value = 1
|
55
|
+
|
56
|
+
result_list.append({
|
57
|
+
'label': label,
|
58
|
+
'value': value
|
59
|
+
})
|
60
|
+
|
61
|
+
formatted = {
|
62
|
+
'result': result_list,
|
63
|
+
'labels': [label_name]
|
64
|
+
}
|
65
|
+
all_results.append(formatted)
|
66
|
+
|
67
|
+
return all_results
|
68
|
+
|
69
|
+
|
70
|
+
# # Example usage:
|
71
|
+
# if __name__ == "__main__":
|
72
|
+
# results = process_commands('/Users/nlingadh/code/custom_prometheus_agent/src/commands.yaml')
|
73
|
+
# print(results)
|
@@ -0,0 +1,140 @@
|
|
1
|
+
from prometheus_client import Gauge, start_http_server
|
2
|
+
import psutil
|
3
|
+
import time
|
4
|
+
import sys
|
5
|
+
import os
|
6
|
+
|
7
|
+
from flexmetric.config.configuration import CA_PATH , CERT_PATH, KEY_PATH
|
8
|
+
from flexmetric.logging_module.logger import get_logger
|
9
|
+
from flexmetric.file_recognition.exec_file import execute_functions
|
10
|
+
from flexmetric.metric_process.process_commands import process_commands
|
11
|
+
from flexmetric.metric_process.database_processing import process_database_queries
|
12
|
+
import argparse
|
13
|
+
import os
|
14
|
+
def arguments():
|
15
|
+
parser = argparse.ArgumentParser(
|
16
|
+
description='FlexMetric: A flexible Prometheus exporter for commands, databases, scripts, and Python functions.'
|
17
|
+
)
|
18
|
+
|
19
|
+
# Input type flags
|
20
|
+
parser.add_argument('--database', action='store_true', help='Process database.yaml and queries.yaml to extract metrics from databases.')
|
21
|
+
parser.add_argument('--commands', action='store_true', help='Process commands.yaml to extract metrics from system commands.')
|
22
|
+
parser.add_argument('--functions', action='store_true', help='Process Python functions from the provided path to extract metrics.')
|
23
|
+
|
24
|
+
|
25
|
+
# Config file paths
|
26
|
+
parser.add_argument('--database-config', type=str, default=None, help='Path to the database configuration YAML file.')
|
27
|
+
parser.add_argument('--queries-config', type=str, default=None, help='Path to the database queries YAML file.')
|
28
|
+
parser.add_argument('--commands-config', type=str, default=None, help='Path to the commands configuration YAML file.')
|
29
|
+
parser.add_argument('--functions-dir', type=str, default=None, help='Path to the python files dir.')
|
30
|
+
parser.add_argument('--functions-file', type=str, default=None, help='Path to the file containing which function to execute')
|
31
|
+
parser.add_argument('--port', type=int, default=8000, help='port on which exportor runs')
|
32
|
+
|
33
|
+
return parser.parse_args()
|
34
|
+
|
35
|
+
logger = get_logger(__name__)
|
36
|
+
|
37
|
+
logger.info("prometheus is running")
|
38
|
+
|
39
|
+
def convert_to_data_type(value):
|
40
|
+
if isinstance(value, str) and '%' in value:
|
41
|
+
return float(value.strip('%'))
|
42
|
+
elif isinstance(value, str) and ('GB' in value or 'MB' in value):
|
43
|
+
return float(value.split()[0].replace(',', ''))
|
44
|
+
return value
|
45
|
+
gauges = []
|
46
|
+
|
47
|
+
def validate_required_files(mode_name, required_files):
|
48
|
+
missing = [desc for desc, path in required_files.items() if path == None]
|
49
|
+
if missing:
|
50
|
+
print(f"Missing {', '.join(missing)} for '{mode_name}' mode. Skipping...")
|
51
|
+
return False
|
52
|
+
|
53
|
+
return True
|
54
|
+
|
55
|
+
def validate_all_modes(args):
|
56
|
+
"""
|
57
|
+
Validates all selected modes and their required files.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
args: Parsed command-line arguments.
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
bool: True if at least one valid mode is properly configured, False otherwise.
|
64
|
+
"""
|
65
|
+
has_valid_mode = False
|
66
|
+
|
67
|
+
mode_validations = [
|
68
|
+
(args.database, 'database', {
|
69
|
+
'database-config': args.database_config,
|
70
|
+
'queries-config': args.queries_config
|
71
|
+
}),
|
72
|
+
(args.commands, 'commands', {
|
73
|
+
'commands-config': args.commands_config
|
74
|
+
}),
|
75
|
+
(args.functions, 'functions', {
|
76
|
+
'functions-dir': args.functions_dir,
|
77
|
+
'functions-file':args.functions_file
|
78
|
+
})
|
79
|
+
]
|
80
|
+
|
81
|
+
for is_enabled, mode_name, files in mode_validations:
|
82
|
+
if is_enabled:
|
83
|
+
if validate_required_files(mode_name, files):
|
84
|
+
has_valid_mode = True
|
85
|
+
|
86
|
+
return has_valid_mode
|
87
|
+
|
88
|
+
|
89
|
+
def measure(init_flag,args):
|
90
|
+
exec_result = []
|
91
|
+
if args.database:
|
92
|
+
db_results = process_database_queries(args.queries_config, args.database_config)
|
93
|
+
exec_result.extend(db_results)
|
94
|
+
if args.functions:
|
95
|
+
function_results = execute_functions(args.functions_dir,args.functions_file)
|
96
|
+
exec_result.extend(function_results)
|
97
|
+
if args.commands:
|
98
|
+
cmd_results = process_commands(args.commands_config)
|
99
|
+
exec_result.extend(cmd_results)
|
100
|
+
# exec_result = process_commands('commands.yaml')
|
101
|
+
# print(exec_result)
|
102
|
+
global gauges
|
103
|
+
count = 0
|
104
|
+
for data in exec_result:
|
105
|
+
results= data['result']
|
106
|
+
labels = data['labels']
|
107
|
+
gauge_name = '_'.join(labels).lower() + "_gauge"
|
108
|
+
print(labels)
|
109
|
+
if init_flag:
|
110
|
+
gauge = Gauge(gauge_name, f"{gauge_name} for different metrics", labels)
|
111
|
+
gauges.append(gauge)
|
112
|
+
else:
|
113
|
+
gauge = gauges[count]
|
114
|
+
count += 1
|
115
|
+
for result in results:
|
116
|
+
print(result,isinstance(result['label'],list))
|
117
|
+
if isinstance(result['label'],str):
|
118
|
+
try:
|
119
|
+
gauge.labels(result['label']).set(convert_to_data_type(result['value']))
|
120
|
+
except Exception as ex:
|
121
|
+
logger.error("Cannot pass string")
|
122
|
+
elif isinstance(result['label'],list):
|
123
|
+
label_dict = dict(zip(labels, result['label']))
|
124
|
+
gauge.labels(**label_dict).set(convert_to_data_type(result['value']))
|
125
|
+
|
126
|
+
if __name__ == "__main__":
|
127
|
+
args = arguments()
|
128
|
+
print("Validating configuration...")
|
129
|
+
if not validate_all_modes(args):
|
130
|
+
print("No valid modes with proper configuration found. Exiting.")
|
131
|
+
exit(1)
|
132
|
+
|
133
|
+
print(f"Starting Prometheus metrics server on port {args.port}...")
|
134
|
+
print("Starting server")
|
135
|
+
start_http_server(args.port)
|
136
|
+
flag = True
|
137
|
+
while True:
|
138
|
+
measure(flag,args)
|
139
|
+
flag = False
|
140
|
+
time.sleep(5)
|
@@ -0,0 +1,152 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: flexmetric
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A flexible Prometheus exporter for commands, databases, functions, and scripts.
|
5
|
+
Home-page: https://github.com/nikhillingadhal1999/custom_prometheus_agent
|
6
|
+
Author: Nikhil Lingadhal
|
7
|
+
License: MIT
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Requires-Python: >=3.7
|
12
|
+
Description-Content-Type: text/markdown
|
13
|
+
Requires-Dist: prometheus_client
|
14
|
+
Requires-Dist: PyYAML
|
15
|
+
Requires-Dist: psutil
|
16
|
+
Requires-Dist: setuptools
|
17
|
+
Requires-Dist: wheel
|
18
|
+
Requires-Dist: twine
|
19
|
+
Dynamic: author
|
20
|
+
Dynamic: classifier
|
21
|
+
Dynamic: description
|
22
|
+
Dynamic: description-content-type
|
23
|
+
Dynamic: home-page
|
24
|
+
Dynamic: license
|
25
|
+
Dynamic: requires-dist
|
26
|
+
Dynamic: requires-python
|
27
|
+
Dynamic: summary
|
28
|
+
|
29
|
+
# FlexMetric
|
30
|
+
|
31
|
+
FlexMetric is a lightweight, flexible, and extensible Prometheus exporter that allows you to expose system metrics, database query results, script outputs, and Python function outputs as Prometheus-compatible metrics with minimal setup and maximum customization.
|
32
|
+
|
33
|
+
---
|
34
|
+
|
35
|
+
## Features
|
36
|
+
|
37
|
+
- Run shell commands and expose the results as Prometheus metrics.
|
38
|
+
- Execute SQL queries (e.g., SQLite) and monitor database statistics.
|
39
|
+
- Automatically discover and expose Python function outputs.
|
40
|
+
- Run custom shell scripts and monitor their outputs.
|
41
|
+
- Modular and easy to extend—add your own integrations.
|
42
|
+
- Built-in Prometheus HTTP server (`/metrics`) with configurable port.
|
43
|
+
|
44
|
+
---
|
45
|
+
|
46
|
+
## Installation
|
47
|
+
|
48
|
+
Install from PyPI:
|
49
|
+
|
50
|
+
```bash
|
51
|
+
pip install flexmetric
|
52
|
+
|
53
|
+
## Usage
|
54
|
+
|
55
|
+
Run FlexMetric from the command line:
|
56
|
+
|
57
|
+
```bash
|
58
|
+
flexmetric --commands --commands-config commands.yaml --port 8000
|
59
|
+
```
|
60
|
+
|
61
|
+
## Available Modes
|
62
|
+
|
63
|
+
FlexMetric supports multiple modes that can be used individually or combined to expose metrics:
|
64
|
+
|
65
|
+
| Mode | Description | Required Configuration File(s) |
|
66
|
+
|---------------|--------------------------------------------------|------------------------------------------|
|
67
|
+
| `--commands` | Runs system commands and exports outputs as Prometheus metrics. | `commands.yaml` |
|
68
|
+
| `--database` | Executes SQL queries on databases and exports results. | `database.yaml` and `queries.yaml` |
|
69
|
+
| `--functions` | Discovers and runs user-defined Python functions and exports outputs. | `executable_functions.txt` |
|
70
|
+
| `--scripts` | Executes custom shell scripts and exports outputs. | `scripts.yaml` |
|
71
|
+
|
72
|
+
### Example of Using Multiple Modes Together
|
73
|
+
|
74
|
+
```bash
|
75
|
+
flexmetric --commands --commands-config commands.yaml --database --database-config database.yaml --queries-config queries.yaml
|
76
|
+
```
|
77
|
+
|
78
|
+
## Configuration File Examples
|
79
|
+
|
80
|
+
Below are example configurations for each supported mode.
|
81
|
+
|
82
|
+
### commands.yaml
|
83
|
+
|
84
|
+
```yaml
|
85
|
+
commands:
|
86
|
+
- name: disk_usage
|
87
|
+
command: df -h
|
88
|
+
label: path
|
89
|
+
timeout_seconds: 60
|
90
|
+
```
|
91
|
+
```yaml
|
92
|
+
databases:
|
93
|
+
- name: mydb
|
94
|
+
db_type: sqlite
|
95
|
+
db_connection: /path/to/my.db
|
96
|
+
````
|
97
|
+
```yaml
|
98
|
+
queries:
|
99
|
+
- name: user_count
|
100
|
+
db_type: sqlite
|
101
|
+
db_name: mydb
|
102
|
+
query: "SELECT COUNT(*) FROM users;"
|
103
|
+
label: table
|
104
|
+
label_value: users
|
105
|
+
```
|
106
|
+
executable_functions.txt
|
107
|
+
```
|
108
|
+
function_name_1
|
109
|
+
function_name_2
|
110
|
+
```
|
111
|
+
|
112
|
+
## Command-Line Options
|
113
|
+
|
114
|
+
The following command-line options are available when running FlexMetric:
|
115
|
+
|
116
|
+
| Option | Description | Default |
|
117
|
+
|---------------------|----------------------------------------------------------|----------------------------|
|
118
|
+
| `--port` | Port for the Prometheus metrics server | `8000` |
|
119
|
+
| `--commands` | Enable commands mode | |
|
120
|
+
| `--commands-config` | Path to commands YAML file | `commands.yaml` |
|
121
|
+
| `--database` | Enable database mode | |
|
122
|
+
| `--database-config` | Path to database YAML file | `database.yaml` |
|
123
|
+
| `--queries-config` | Path to queries YAML file | `queries.yaml` |
|
124
|
+
| `--functions` | Enable Python functions mode | |
|
125
|
+
| `--functions-file` | Path to functions file | `executable_functions.txt` |
|
126
|
+
| `--scripts` | Enable shell scripts mode | |
|
127
|
+
| `--scripts-config` | Path to scripts YAML file | `scripts.yaml` |
|
128
|
+
|
129
|
+
### Example Command:
|
130
|
+
|
131
|
+
```bash
|
132
|
+
flexmetric --commands --commands-config commands.yaml --port 8000
|
133
|
+
|
134
|
+
## Example Prometheus Output
|
135
|
+
|
136
|
+
Once FlexMetric is running, the `/metrics` endpoint will expose metrics in the Prometheus format.
|
137
|
+
|
138
|
+
Example output:
|
139
|
+
```bash
|
140
|
+
disk_usage_gauge{path="/"} 45.0
|
141
|
+
```
|
142
|
+
|
143
|
+
Each metric includes labels and numeric values that Prometheus can scrape and visualize.
|
144
|
+
|
145
|
+
---
|
146
|
+
|
147
|
+
## Future Enhancements
|
148
|
+
|
149
|
+
The following features are planned or under consideration to improve FlexMetric:
|
150
|
+
|
151
|
+
- Support for additional databases such as PostgreSQL and MySQL.
|
152
|
+
- Enhanced support for more complex scripts and richer label extraction.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
flexmetric/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
flexmetric/file_recognition/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
flexmetric/file_recognition/exec_file.py,sha256=9wBbnqPConxtLhqWTgigEr8VQG-fa9K_U9UOLjqF8Xw,4273
|
4
|
+
flexmetric/logging_module/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
flexmetric/logging_module/logger.py,sha256=hXj9m2Q_KxJVI5YRHRoK6PXV5tO6NmvuVjq2Ipx_1tE,447
|
6
|
+
flexmetric/metric_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
+
flexmetric/metric_process/database_processing.py,sha256=OAJ0FbvjQLSqUh85KenuEQ5YG7ghWQYT-pzqogCOafk,2542
|
8
|
+
flexmetric/metric_process/process_commands.py,sha256=cHtoyzP0IgZOwoS_BgFZU0VHuOKQO0UVvGOCwWuByNM,2236
|
9
|
+
flexmetric/metric_process/prometheus_agent.py,sha256=kb_egYolKh7Q6BufbvswuhPanBGzAjKMkUHPRE7nP4Y,5374
|
10
|
+
flexmetric-0.1.0.dist-info/METADATA,sha256=ozCoaEh4TUeWSO9jAQsQkFZ8BsO6JykmImw0rBuseuU,5367
|
11
|
+
flexmetric-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
12
|
+
flexmetric-0.1.0.dist-info/entry_points.txt,sha256=urVePn5EWr3JqNvkYP7OsB_h2_Bqvv-Wq1MJRBexm8A,79
|
13
|
+
flexmetric-0.1.0.dist-info/top_level.txt,sha256=zBlrNwKhXUNhgu9RRZnXxYwYnmE-eocRe6wKSmQROA4,11
|
14
|
+
flexmetric-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
flexmetric
|