diff --git a/checks.yaml b/checks.yaml
index ce0f868..4db4ea0 100644
--- a/checks.yaml
+++ b/checks.yaml
@@ -1,71 +1,77 @@
-- name: GitHub Home
- type: http
- host: https://github.com
- expected_code: 200
+- title: 'Group 1'
+ checks:
+ - name: GitHub Home
+ type: http
+ host: https://github.com
+ expected_code: 200
-- name: GitHub API
- type: http
- host: https://api.github.com
- expected_code: 200
+ - name: GitHub API
+ type: http
+ host: https://api.github.com
+ expected_code: 200
-- name: Wikipedia Home
- type: http
- host: https://www.wikipedia.org
- expected_code: 200
+ - name: Wikipedia Home
+ type: http
+ host: https://www.wikipedia.org
+ expected_code: 200
-- name: Wikipedia API
- type: http
- host: https://en.wikipedia.org/w/api.php
- expected_code: 200
+ - name: Wikipedia API
+ type: http
+ host: https://en.wikipedia.org/w/api.php
+ expected_code: 200
-- name: Hacker News
- type: http
- host: https://news.ycombinator.com
- expected_code: 200
+ - name: Hacker News
+ type: http
+ host: https://news.ycombinator.com
+ expected_code: 200
-- name: Google Home
- type: http
- host: https://www.google.com
- expected_code: 200
+ - name: Google Home
+ type: http
+ host: https://www.google.com
+ expected_code: 200
-- name: Cloudflare DNS Checker
- type: ping
- host: 1.1.1.1
+- title: 'Group 2'
+ checks:
+ - name: Cloudflare DNS Checker
+ type: ping
+ host: 1.1.1.1
-- name: Google Public DNS
- type: ping
- host: 8.8.8.8
+ - name: Google Public DNS
+ type: ping
+ host: 8.8.8.8
-- name: Dummy Postgres Database
- type: port
- host: ec2-54-173-89-248.compute-1.amazonaws.com
- port: 5432
+ - name: Dummy Postgres Database
+ type: port
+ host: ec2-54-173-89-248.compute-1.amazonaws.com
+ port: 5432
-- name: Dummy MySQL Database
- type: port
- host: db.example.com
- port: 3306
+ - name: Dummy MySQL Database
+ type: port
+ host: db.example.com
+ port: 3306
-- name: Amazon Web Services
- type: http
- host: https://aws.amazon.com
- expected_code: 200
+- title: 'Group 3'
+ checks:
+ - name: Amazon Web Services
+ type: http
+ host: https://aws.amazon.com
+ expected_code: 200
-- name: AWS S3 API
- type: http
- host: https://s3.amazonaws.com
- expected_code: 200
+ - name: AWS S3 API
+ type: http
+ host: https://s3.amazonaws.com
+ expected_code: 200
-- name: Twitter
- type: http
- host: https://twitter.com
- expected_code: 200
+ - name: Twitter
+ type: http
+ host: https://twitter.com
+ expected_code: 200
-- name: Facebook Home
- type: http
- host: https://www.facebook.com
- expected_code: 200
-
-- name: Localhost
- type: ping
- host: localhost
+ - name: Facebook Home
+ type: http
+ host: https://www.facebook.com
+ expected_code: 200
+
+ - name: Localhost
+ type: ping
+ host: localhost
diff --git a/index.html.theme b/index.html.theme
index 6df6869..0a236b1 100644
--- a/index.html.theme
+++ b/index.html.theme
@@ -83,6 +83,8 @@
TinyStatus
Current Status
+ {% for group, checks in groups.items() %}
+ {{group}} Status
{% for check in checks %}
@@ -93,6 +95,7 @@
{% endfor %}
+ {% endfor %}
Incident History
{{ incidents | safe }}
diff --git a/tinystatus.py b/tinystatus.py
index 69f4876..a2279e0 100644
--- a/tinystatus.py
+++ b/tinystatus.py
@@ -25,6 +25,7 @@
STATUS_HISTORY_FILE = os.getenv('STATUS_HISTORY_FILE', 'history.json')
HTML_OUTPUT_DIRECTORY = os.getenv('HTML_OUTPUT_DIRECTORY', os.getcwd())
+
# Service check functions
async def check_http(url, expected_code):
async with aiohttp.ClientSession() as session:
@@ -34,6 +35,7 @@ async def check_http(url, expected_code):
except:
return False
+
async def check_ping(host):
try:
result = subprocess.run(['ping', '-c', '1', '-W', '2', host], capture_output=True, text=True)
@@ -41,6 +43,7 @@ async def check_ping(host):
except:
return False
+
async def check_port(host, port):
try:
_, writer = await asyncio.open_connection(host, port)
@@ -50,6 +53,7 @@ async def check_port(host, port):
except:
return False
+
async def run_checks(checks):
background_tasks = {}
async with asyncio.TaskGroup() as tg:
@@ -70,6 +74,7 @@ async def run_checks(checks):
return results
+
# History management
def load_history():
if os.path.exists(STATUS_HISTORY_FILE):
@@ -77,28 +82,34 @@ def load_history():
return json.load(f)
return {}
+
def save_history(history):
with open(STATUS_HISTORY_FILE, 'w') as f:
json.dump(history, f, indent=2)
+
def update_history(results):
+ # TODO: Configure groups in history page
history = load_history()
current_time = datetime.now().isoformat()
- for check in results:
- if check['name'] not in history:
- history[check['name']] = []
- history[check['name']].append({'timestamp': current_time, 'status': check['status']})
- history[check['name']] = history[check['name']][-MAX_HISTORY_ENTRIES:]
+ for group in results.keys():
+ for check in results[group]:
+ if check['name'] not in history:
+ history[check['name']] = []
+ history[check['name']].append({'timestamp': current_time, 'status': check['status']})
+ history[check['name']] = history[check['name']][-MAX_HISTORY_ENTRIES:]
save_history(history)
+
# Main monitoring loop
async def monitor_services():
os.makedirs(HTML_OUTPUT_DIRECTORY, exist_ok=True)
-
+
while True:
+ down_services = []
try:
with open(CHECKS_FILE, 'r') as f:
- checks = yaml.safe_load(f)
+ groups = yaml.safe_load(f)
with open(INCIDENTS_FILE, 'r') as f:
incidents = markdown.markdown(f.read())
@@ -109,11 +120,13 @@ async def monitor_services():
with open(HISTORY_TEMPLATE_FILE, 'r') as f:
history_template = Template(f.read())
- results = await run_checks(checks)
+ results = {}
+ for group in groups:
+ results[group['title']] = await run_checks(group['checks'])
update_history(results)
- html = template.render(checks=results, incidents=incidents, last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+ html = template.render(groups=results, incidents=incidents, last_updated=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
with open(os.path.join(HTML_OUTPUT_DIRECTORY, 'index.html'), 'w') as f:
f.write(html)
@@ -122,18 +135,24 @@ async def monitor_services():
f.write(history_html)
logging.info(f"Status page and history updated at {datetime.now()}")
- down_services = [check['name'] for check in results if not check['status']]
- if down_services:
- logging.warning(f"Services currently down: {', '.join(down_services)}")
+
+ for group in results:
+ group_down = [check['name'] for check in results[group] if not check['status']]
+ down_services += group_down
except Exception as e:
logging.error(f"An error occurred: {str(e)}")
- if MONITOR_CONTINOUSLY == False:
+ # down_services = [group[check]['name'] for check in group in results if not check['status']]
+ if down_services:
+ logging.warning(f"Services currently down: {', '.join(down_services)}")
+
+ if not MONITOR_CONTINOUSLY:
return
await asyncio.sleep(CHECK_INTERVAL)
+
# Main function
def main():
with open(CHECKS_FILE, 'r') as f: