PyObservability 0.1.0__py3-none-any.whl → 1.0.1__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.
@@ -7,8 +7,12 @@
7
7
  }
8
8
 
9
9
  * { box-sizing: border-box; }
10
- html,body { height:100%; margin:0; font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; background:var(--bg); color:#e6eef8; }
10
+ html,body {
11
+ height:100%; margin:0; font-family: Inter, system-ui, -apple-system,
12
+ "Segoe UI", Roboto, "Helvetica Neue", Arial; background:var(--bg); color:#e6eef8;
13
+ }
11
14
 
15
+ .hidden { display: none !important; }
12
16
  .topbar {
13
17
  display:flex; align-items:center; justify-content:space-between;
14
18
  padding:12px 20px; background:linear-gradient(90deg,#071028,#0b1530); border-bottom:1px solid rgba(255,255,255,0.02);
@@ -16,16 +20,20 @@ html,body { height:100%; margin:0; font-family: Inter, system-ui, -apple-system,
16
20
  .brand { font-weight:700; font-size:18px; }
17
21
  .controls { display:flex; gap:8px; align-items:center; }
18
22
 
19
- .controls select, .controls button { background:var(--panel); color:inherit; border:1px solid rgba(255,255,255,0.04); padding:6px 8px; border-radius:6px; }
23
+ .controls select, .controls button {
24
+ background:var(--panel); color:inherit; border:1px solid rgba(255,255,255,0.04); padding:6px 8px; border-radius:6px;
25
+ }
20
26
  .controls label { font-size:14px; color:var(--muted); margin-right:6px; }
21
27
 
22
28
  .container { padding:18px; display:flex; flex-direction:column; gap:16px; max-width:1200px; margin:0 auto; }
23
29
 
24
- .meta-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
25
- /* .meta-row { display:flex; gap:12px; } */
26
- .meta-card { background:var(--panel); padding:12px; border-radius:8px; width:1fr; min-width:120px; flex:1; box-shadow:0 4px 12px rgba(2,6,23,0.6); }
30
+ .meta-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; align-items: stretch; }
31
+ .meta-card {
32
+ display: flex; flex-direction: column; background:var(--panel); padding:12px; border-radius:8px; width:1fr;
33
+ min-width:120px; flex:1; box-shadow:0 4px 12px rgba(2,6,23,0.6);
34
+ }
27
35
  .meta-title { font-size:12px; color:var(--muted); }
28
- .meta-value { margin-top:6px; font-weight:600; font-size:16px; }
36
+ .meta-value { flex: 1; margin-top:6px; font-weight:600; font-size:16px; }
29
37
  .meta-value.pre { white-space:pre-wrap; font-family:monospace; font-size:13px; }
30
38
 
31
39
  .charts-row { display:flex; gap:12px; }
@@ -48,7 +56,9 @@ html,body { height:100%; margin:0; font-family: Inter, system-ui, -apple-system,
48
56
  .tables-row { display:grid; grid-template-columns:1fr; grid-auto-rows:auto; gap:12px; }
49
57
  .table { width:100%; border-collapse:collapse; font-size:13px; }
50
58
  .table th, .table td { padding:8px; border-bottom:1px solid rgba(255,255,255,0.03); text-align:left; }
51
- .pre { background:rgba(255,255,255,0.02); padding:8px; border-radius:6px; overflow:auto; white-space:pre-wrap; font-family:monospace; font-size:13px; }
59
+ .pre {
60
+ background:rgba(255,255,255,0.02); padding:8px; border-radius:6px; overflow:auto; white-space:pre-wrap; font-family:monospace; font-size:13px;
61
+ }
52
62
  .list { padding-left:14px; margin:8px 0; }
53
63
  .footer { padding:12px 20px; font-size:12px; color:var(--muted); text-align:center; border-top:1px solid rgba(255,255,255,0.02); margin-top:20px; }
54
64
  .service-controls { display:flex; gap:8px; padding-bottom:8px; }
@@ -61,3 +71,46 @@ input#proc-filter { flex:1; padding:6px 8px; background:var(--panel); border:1px
61
71
  .details-row { flex-direction:column; }
62
72
  .meta-row { grid-template-columns: 1fr; }
63
73
  }
74
+
75
+ /* ---------------- SPINNER ------------------- */
76
+ .loading-overlay {
77
+ position: absolute;
78
+ inset: 0;
79
+ background: rgba(0,0,0,0.65); /* Darker so spinner is visible */
80
+ backdrop-filter: blur(2px); /* Add blur to ensure visual separation */
81
+ display: flex;
82
+ justify-content: center;
83
+ align-items: center;
84
+ z-index: 50;
85
+ border-radius: 8px;
86
+ }
87
+
88
+ .spinner {
89
+ width: 32px;
90
+ height: 32px;
91
+ border: 4px solid rgba(255,255,255,0.25);
92
+ border-top: 4px solid white; /* More contrast */
93
+ border-radius: 50%;
94
+ animation: spin 0.7s linear infinite;
95
+ }
96
+
97
+ @keyframes spin {
98
+ to { transform: rotate(360deg); }
99
+ }
100
+
101
+ /* --------------- PAGINATION ------------------ */
102
+ .pagination { display: flex; justify-content: center; gap: 6px; padding: 6px; margin-top: 6px; font-size: 13px; }
103
+
104
+ .pagination button {
105
+ background: var(--panel); color: inherit; border: 1px solid rgba(255,255,255,0.05); padding: 4px 8px; border-radius: 4px; cursor: pointer;
106
+ }
107
+
108
+ .pagination button.active {
109
+ background: var(--accent);
110
+ color: #000;
111
+ }
112
+ .pagination-info {
113
+ font-size: 13px;
114
+ margin-bottom: 4px;
115
+ opacity: 0.85;
116
+ }
@@ -23,32 +23,32 @@
23
23
  <section class="meta-row">
24
24
  <div class="meta-card">
25
25
  <div class="meta-title">System</div>
26
- <div id="system" class="meta-value pre">—</div>
26
+ <div id="system" class="meta-value pre"></div>
27
27
  </div>
28
28
 
29
29
  <div class="meta-card">
30
30
  <div class="meta-title">IP</div>
31
- <div id="ip-info" class="meta-value pre">—</div>
31
+ <div id="ip-info" class="meta-value pre"></div>
32
32
  </div>
33
33
 
34
34
  <div class="meta-card">
35
35
  <div class="meta-title">GPU / CPU Model</div>
36
- <div id="processor" class="meta-value pre">—</div>
36
+ <div id="processor" class="meta-value pre"></div>
37
37
  </div>
38
38
 
39
39
  <div class="meta-card">
40
40
  <div class="meta-title">CPU Load (1/5/15)</div>
41
- <div id="cpuload" class="meta-value pre">—</div>
41
+ <div id="cpuload" class="meta-value pre"></div>
42
42
  </div>
43
43
 
44
44
  <div class="meta-card">
45
45
  <div class="meta-title">Disk ( / )</div>
46
- <div id="disk" class="meta-value pre">—</div>
46
+ <div id="disk" class="meta-value pre"></div>
47
47
  </div>
48
48
 
49
49
  <div class="meta-card">
50
50
  <div class="meta-title">Memory</div>
51
- <div id="memory" class="meta-value pre">—</div>
51
+ <div id="memory" class="meta-value pre"></div>
52
52
  </div>
53
53
  </section>
54
54
 
@@ -57,7 +57,7 @@
57
57
  <div class="panel-header">
58
58
  <h3>CPU (Average)</h3>
59
59
  <div class="panel-actions">
60
- <label><input type="checkbox" id="show-cores" /> Show per-core</label>
60
+ <label><input type="checkbox" id="show-cores" checked="true" /> Show per-core</label>
61
61
  </div>
62
62
  </div>
63
63
  <canvas id="cpu-avg-chart" class="chart"></canvas>
@@ -83,7 +83,12 @@
83
83
 
84
84
  <section class="tables-row">
85
85
  <div class="panel">
86
- <div class="panel-header"><h3>Services</h3></div>
86
+ <div class="panel-header">
87
+ <h3>Services</h3>
88
+ <div class="panel-actions">
89
+ <label><input type="checkbox" id="get-all-services" /> Get all</label>
90
+ </div>
91
+ </div>
87
92
  <div class="panel-body service-controls">
88
93
  <input id="svc-filter" placeholder="filter service name..." />
89
94
  </div>
@@ -147,9 +152,10 @@
147
152
  </section>
148
153
  </main>
149
154
 
150
- <!-- TODO: Remove or provide context -->
151
155
  <footer class="footer">
152
- <div>OpenAPI: <code>/static/openapi.json</code> (uploaded file available at <code>/mnt/data/openapi.json</code>)</div>
156
+ <div><code>PyObservability: v{{ version }}</code><br>
157
+ <a href="https://github.com/thevickypedia/PyObservability" target="_blank">https://github.com/thevickypedia/PyObservability</a>
158
+ </div>
153
159
  </footer>
154
160
 
155
161
  <script>
@@ -0,0 +1,71 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+
5
+ from fastapi import WebSocket, WebSocketDisconnect
6
+
7
+ from pyobservability.config import settings
8
+ from pyobservability.monitor import Monitor
9
+
10
+ LOGGER = logging.getLogger("uvicorn.default")
11
+
12
+
13
+ async def _forward_metrics(websocket: WebSocket, q: asyncio.Queue):
14
+ while True:
15
+ payload = await q.get()
16
+ await websocket.send_json(payload)
17
+
18
+
19
+ async def websocket_endpoint(websocket: WebSocket):
20
+ await websocket.accept()
21
+
22
+ monitor: Monitor | None = None
23
+ q: asyncio.Queue | None = None
24
+
25
+ try:
26
+ while True:
27
+ msg = await websocket.receive_text()
28
+ data = json.loads(msg)
29
+ if data.get("type") == "update_flags":
30
+ if monitor:
31
+ await monitor.update_flags(
32
+ all_services=data.get("all_services", False),
33
+ )
34
+ continue
35
+
36
+ # -------------------------------------------
37
+ # UI requests a specific target to monitor
38
+ # -------------------------------------------
39
+ if data.get("type") == "select_target":
40
+ base_url = data["base_url"]
41
+
42
+ # stop old monitor
43
+ if monitor:
44
+ monitor.unsubscribe(q)
45
+ await monitor.stop()
46
+
47
+ if target := settings.targets_by_url.get(base_url):
48
+ LOGGER.info("Gathering metrics for: %s", target["name"])
49
+ else:
50
+ LOGGER.warning(f"Invalid base url: {base_url}")
51
+ raise WebSocketDisconnect(code=400, reason=f"Invalid base url: {base_url}")
52
+
53
+ # create new monitor
54
+ monitor = Monitor(target)
55
+ await monitor.start()
56
+
57
+ # new subscription queue
58
+ q = monitor.subscribe()
59
+
60
+ # start forwarding metrics
61
+ asyncio.create_task(_forward_metrics(websocket, q))
62
+ except WebSocketDisconnect:
63
+ pass
64
+ except Exception as err:
65
+ LOGGER.error("WS error: %s", err)
66
+
67
+ # cleanup
68
+ if monitor:
69
+ if q:
70
+ monitor.unsubscribe(q)
71
+ await monitor.stop()
@@ -1 +1 @@
1
- __version__ = "0.1.0"
1
+ __version__ = "1.0.1"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyObservability
3
- Version: 0.1.0
3
+ Version: 1.0.1
4
4
  Summary: Lightweight OS-agnostic observability UI for PyNinja
5
5
  Author-email: Vignesh Rao <svignesh1793@gmail.com>
6
6
  License: MIT License
@@ -33,7 +33,7 @@ Project-URL: Release Notes, https://github.com/thevickypedia/PyObservability/blo
33
33
  Keywords: PyObservability,observability,system-monitor,PyNinja
34
34
  Classifier: License :: OSI Approved :: MIT License
35
35
  Classifier: Programming Language :: Python :: 3
36
- Classifier: Development Status :: 3 - Alpha
36
+ Classifier: Development Status :: 5 - Production/Stable
37
37
  Classifier: Operating System :: MacOS :: MacOS X
38
38
  Classifier: Operating System :: Microsoft :: Windows
39
39
  Classifier: Operating System :: POSIX :: Linux
@@ -0,0 +1,16 @@
1
+ pyobservability/__init__.py,sha256=rr4udGMbbNPl3yo7l8R3FUUVVahBtYVaW6vSWWgXlv0,2617
2
+ pyobservability/main.py,sha256=CI6qfOxpwIykrQXu0hYdgD16foo33IqlNHjJCtrSynA,3592
3
+ pyobservability/monitor.py,sha256=4Xd8k7gcOmHM-WvQpgFiDVGtzMu_RpFPZXPPoz4GoA4,6224
4
+ pyobservability/transport.py,sha256=FyzJAMZPn7JUZIGgxnSw3on1K6T4ciZE1EuGdAcxt_w,2188
5
+ pyobservability/version.py,sha256=d4QHYmS_30j0hPN8NmNPnQ_Z0TphDRbu4MtQj9cT9e8,22
6
+ pyobservability/config/enums.py,sha256=jXDtyM1_nDKgq_8gVeZULEC5EEYBzqmZWd25qsIs64I,148
7
+ pyobservability/config/settings.py,sha256=C2sK9VRfDBsHm8xP0PTMFAdm7K3ySwiQ0vn6Vh6wAM4,5233
8
+ pyobservability/static/app.js,sha256=6hjFy2jt4ndYJUI1DZT08CpuxHyWj9iSAZ2vxaTqH2A,20664
9
+ pyobservability/static/styles.css,sha256=dnYSXNeXd6Ohu4h8sJCO87vzPbkYcw9XVGGwB8IJbxw,4703
10
+ pyobservability/templates/index.html,sha256=2aQdb0QlDY5Hboev-_lJPlpnGxiC6h3fp0FlvL72S9k,5227
11
+ pyobservability-1.0.1.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
12
+ pyobservability-1.0.1.dist-info/METADATA,sha256=U5yMXMcpC5NVaAy2Urkt45KED7rGmSNukgMBHqsFPtg,6834
13
+ pyobservability-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ pyobservability-1.0.1.dist-info/entry_points.txt,sha256=DSGIr_VA8Tb3FYa2iNUYpf55eAvuFCAoInNS4ngXaME,57
15
+ pyobservability-1.0.1.dist-info/top_level.txt,sha256=p20T0EmihDYW1uMintRXr7X9bg3XWYKyoSbBHOVC1xI,16
16
+ pyobservability-1.0.1.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- pyobservability/__init__.py,sha256=rr4udGMbbNPl3yo7l8R3FUUVVahBtYVaW6vSWWgXlv0,2617
2
- pyobservability/main.py,sha256=HpKYxQ3XgyvxRP6giVOBIpBYTptZ-txL98KV5D5P8uI,3623
3
- pyobservability/monitor.py,sha256=wu-CYsMCMQ1wZLTBaIyYLdu2FJvQ4rb41Lv2NEJ3yKM,4759
4
- pyobservability/version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
5
- pyobservability/config/enums.py,sha256=iMIOpa8LYSszkPIYBhupX--KrEXVTTsBurinpAxLvMA,86
6
- pyobservability/config/settings.py,sha256=HoDRuzwCCbCdNQLdOiy9JUoiM2BqUsRkX6zUyDLordY,3811
7
- pyobservability/static/app.js,sha256=QQJ-1m7zwKQZz7nWTACS97mF79WGCXl5pNNQDWLmByM,17663
8
- pyobservability/static/styles.css,sha256=ljK6m-q7GMZkhiYQ9bqqR1NbYu2pRXzlvNTol_PK9go,3521
9
- pyobservability/templates/index.html,sha256=di7LKLVf3Ri0nxUn6dXVXlVPomQSRzZgWvNfTfxQb8s,5035
10
- pyobservability-0.1.0.dist-info/licenses/LICENSE,sha256=_sOIKJWdD2o1WwwDIwYB2qTP2nlSWqT5Tyg9jr1Xa4w,1070
11
- pyobservability-0.1.0.dist-info/METADATA,sha256=nDYtRE5m3gCHwO8LI7F0w1B1qy7sLLjHTg7hQfD8RLA,6822
12
- pyobservability-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- pyobservability-0.1.0.dist-info/entry_points.txt,sha256=DSGIr_VA8Tb3FYa2iNUYpf55eAvuFCAoInNS4ngXaME,57
14
- pyobservability-0.1.0.dist-info/top_level.txt,sha256=p20T0EmihDYW1uMintRXr7X9bg3XWYKyoSbBHOVC1xI,16
15
- pyobservability-0.1.0.dist-info/RECORD,,