plain.observer 0.1.0__py3-none-any.whl → 0.2.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.
Potentially problematic release.
This version of plain.observer might be problematic. Click here for more details.
- plain/observer/CHANGELOG.md +15 -0
- plain/observer/admin.py +0 -8
- plain/observer/cli.py +558 -5
- plain/observer/migrations/0002_trace_share_created_at_trace_share_id_trace_summary_and_more.py +58 -0
- plain/observer/models.py +56 -27
- plain/observer/otel.py +7 -0
- plain/observer/templates/observer/{_trace_detail.html → trace.html} +155 -98
- plain/observer/templates/observer/trace_detail.html +24 -0
- plain/observer/templates/observer/trace_share.html +19 -0
- plain/observer/templates/observer/traces.html +161 -135
- plain/observer/templates/toolbar/observer_button.html +27 -43
- plain/observer/urls.py +3 -1
- plain/observer/views.py +90 -56
- {plain_observer-0.1.0.dist-info → plain_observer-0.2.0.dist-info}/METADATA +1 -1
- plain_observer-0.2.0.dist-info/RECORD +25 -0
- plain/observer/templates/admin/observer/trace_detail.html +0 -10
- plain_observer-0.1.0.dist-info/RECORD +0 -23
- {plain_observer-0.1.0.dist-info → plain_observer-0.2.0.dist-info}/WHEEL +0 -0
- {plain_observer-0.1.0.dist-info → plain_observer-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Shared Trace - {{ trace.trace_id }} - Observer</title>
|
|
7
|
+
{% tailwind_css %}
|
|
8
|
+
{% htmx_js %}
|
|
9
|
+
</head>
|
|
10
|
+
<body class="bg-stone-950 text-stone-300 min-h-screen">
|
|
11
|
+
<div class="container mx-auto p-6 max-w-6xl">
|
|
12
|
+
<div class="mb-4 text-sm text-stone-400">
|
|
13
|
+
Shared trace
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
{% include "observer/trace.html" %}
|
|
17
|
+
</div>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
@@ -3,27 +3,50 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>
|
|
6
|
+
<title>Observer Traces</title>
|
|
7
7
|
{% tailwind_css %}
|
|
8
8
|
{% htmx_js %}
|
|
9
|
+
<script>
|
|
10
|
+
if (window.self !== window.top) {
|
|
11
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
12
|
+
document.body.setAttribute('data-iframe', 'true');
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
</script>
|
|
9
16
|
</head>
|
|
10
|
-
<body class="text-
|
|
17
|
+
<body class="text-white/70 overscroll-contain bg-black data-[iframe=true]:bg-transparent" hx-ext="morph">
|
|
11
18
|
|
|
12
19
|
<div id="main-content">
|
|
13
20
|
{% if traces %}
|
|
14
21
|
<div class="flex h-full">
|
|
15
|
-
<aside
|
|
16
|
-
<div class="
|
|
17
|
-
<div class="flex items-center justify-between
|
|
18
|
-
<h3 class="text-sm
|
|
22
|
+
<aside class="fixed left-0 top-0 bottom-0 w-80 border-r border-white/10 flex flex-col">
|
|
23
|
+
<div class="flex-shrink-0 p-3 border-b border-white/10 z-10">
|
|
24
|
+
<div class="flex items-center justify-between">
|
|
25
|
+
<h3 class="text-sm text-white/90">Traces ({{ traces|length }})</h3>
|
|
19
26
|
<div class="flex items-center space-x-2">
|
|
27
|
+
{% if observer.is_persisting() %}
|
|
28
|
+
<div class="w-2 h-2 bg-red-500 rounded-full animate-pulse" title="Recording"></div>
|
|
29
|
+
{% endif %}
|
|
30
|
+
<select
|
|
31
|
+
hx-put="."
|
|
32
|
+
hx-trigger="change"
|
|
33
|
+
hx-swap="morph:innerHTML"
|
|
34
|
+
hx-target="#main-content"
|
|
35
|
+
plain-hx-action="mode"
|
|
36
|
+
name="mode"
|
|
37
|
+
class="h-8 bg-white/10 text-white/70 rounded-sm px-2 py-0 text-xs border border-white/10 focus:border-white/20 focus:outline-none cursor-pointer">
|
|
38
|
+
<option value="summary" {% if observer.is_summarizing() %}selected{% endif %}>Summary</option>
|
|
39
|
+
<option value="persist" {% if observer.is_persisting() %}selected{% endif %}>Recording</option>
|
|
40
|
+
<option disabled>───────</option>
|
|
41
|
+
<option value="disable" {% if observer.is_disabled() %}selected{% endif %} class="text-white/50">Disabled</option>
|
|
42
|
+
</select>
|
|
20
43
|
<button
|
|
21
44
|
hx-get="."
|
|
22
45
|
hx-swap="morph:innerHTML"
|
|
23
46
|
hx-target="#main-content"
|
|
24
|
-
class="
|
|
47
|
+
class="h-8 w-8 flex items-center justify-center rounded-sm bg-white/10 text-white/70 hover:bg-white/20 cursor-pointer transition-colors"
|
|
25
48
|
title="Refresh traces">
|
|
26
|
-
<svg class="htmx-request:animate-spin
|
|
49
|
+
<svg class="htmx-request:animate-spin w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
27
50
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
|
28
51
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
|
29
52
|
</svg>
|
|
@@ -33,73 +56,73 @@
|
|
|
33
56
|
plain-hx-action="traces"
|
|
34
57
|
hx-swap="morph:innerHTML"
|
|
35
58
|
hx-target="#main-content"
|
|
36
|
-
class="
|
|
59
|
+
class="h-8 w-8 flex items-center justify-center rounded-sm bg-white/10 text-white/70 hover:bg-red-600 hover:text-white cursor-pointer transition-colors"
|
|
37
60
|
title="Clear all traces">
|
|
38
|
-
<svg
|
|
61
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
39
62
|
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
|
|
40
63
|
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
|
|
41
64
|
</svg>
|
|
42
65
|
</button>
|
|
43
66
|
</div>
|
|
44
67
|
</div>
|
|
45
|
-
<!-- Simplified mode control -->
|
|
46
|
-
<div class="flex items-center justify-between text-xs">
|
|
47
|
-
<div class="flex items-center space-x-2">
|
|
48
|
-
<span class="text-stone-400">Mode:</span>
|
|
49
|
-
<select
|
|
50
|
-
hx-post="."
|
|
51
|
-
hx-trigger="change"
|
|
52
|
-
hx-swap="morph:innerHTML"
|
|
53
|
-
hx-target="#main-content"
|
|
54
|
-
name="observe_action"
|
|
55
|
-
class="bg-stone-800 text-stone-300 rounded px-2 py-1 text-xs border border-stone-700 focus:border-stone-600 focus:outline-none cursor-pointer">
|
|
56
|
-
<option value="summary" {% if observer.is_summarizing() %}selected{% endif %}>Summary</option>
|
|
57
|
-
<option value="persist" {% if observer.is_persisting() %}selected{% endif %}>Recording</option>
|
|
58
|
-
<option disabled>───────</option>
|
|
59
|
-
<option value="disable" {% if observer.is_disabled() %}selected{% endif %} class="text-stone-500">Disabled</option>
|
|
60
|
-
</select>
|
|
61
|
-
</div>
|
|
62
|
-
<div class="text-stone-500">
|
|
63
|
-
{% if observer.is_persisting() %}
|
|
64
|
-
<div class="w-2 h-2 bg-red-500 rounded-full animate-pulse" title="Recording"></div>
|
|
65
|
-
{% endif %}
|
|
66
|
-
</div>
|
|
67
|
-
</div>
|
|
68
68
|
</div>
|
|
69
|
-
<div class="
|
|
70
|
-
<ul class="
|
|
69
|
+
<div class="flex-1 overflow-y-auto">
|
|
70
|
+
<ul class="divide-y divide-white/5">
|
|
71
71
|
{% for trace_item in traces %}
|
|
72
72
|
<li>
|
|
73
73
|
<a
|
|
74
|
-
href="
|
|
75
|
-
hx-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{{
|
|
74
|
+
href="{{ trace_item.get_absolute_url() }}"
|
|
75
|
+
hx-get="{{ trace_item.get_absolute_url() }}"
|
|
76
|
+
hx-target="#trace"
|
|
77
|
+
hx-swap="innerHTML"
|
|
78
|
+
class="block w-full text-left p-3 transition-colors hover:bg-white/5 focus:outline-none focus:bg-white/5"
|
|
79
|
+
data-trace-id="{{ trace_item.trace_id }}">
|
|
80
|
+
<div class="flex items-start justify-between gap-2">
|
|
81
|
+
<div class="flex-1 min-w-0">
|
|
82
|
+
{% if trace_item.root_span_name %}
|
|
83
|
+
<div class="text-sm font-medium text-white/90 truncate">{{ trace_item.root_span_name }}</div>
|
|
84
|
+
{% else %}
|
|
85
|
+
<div class="text-sm font-medium text-white/30 font-mono truncate">{{ trace_item.trace_id }}</div>
|
|
86
|
+
{% endif %}
|
|
87
|
+
{% if trace_item.summary %}
|
|
88
|
+
<div class="text-xs text-white/75 mt-1">{{ trace_item.summary }}</div>
|
|
89
|
+
{% endif %}
|
|
90
|
+
</div>
|
|
91
|
+
<div class="flex items-center gap-1.5 flex-shrink-0">
|
|
92
|
+
<span class="text-white/40 text-xs">{{ trace_item.start_time|localtime|strftime("%-I:%M %p") }}</span>
|
|
86
93
|
</div>
|
|
87
94
|
</div>
|
|
88
|
-
<div class="
|
|
89
|
-
{
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
<div class="mt-1 space-y-1">
|
|
96
|
+
{% if trace_item.root_span_name %}
|
|
97
|
+
<div class="flex items-center gap-1.5">
|
|
98
|
+
{% if trace_item.share_id %}
|
|
99
|
+
<div class="text-emerald-500 flex-shrink-0" title="Has active share link">
|
|
100
|
+
<svg class="w-3 h-3" fill="currentColor" viewBox="0 0 16 16">
|
|
101
|
+
<path d="M11 2.5a2.5 2.5 0 1 1 .603 1.628l-6.718 3.12a2.5 2.5 0 0 1 0 1.504l6.718 3.12a2.5 2.5 0 1 1-.488.876l-6.718-3.12a2.5 2.5 0 1 1 0-3.256l6.718-3.12A2.5 2.5 0 0 1 11 2.5"/>
|
|
102
|
+
</svg>
|
|
103
|
+
</div>
|
|
104
|
+
{% endif %}
|
|
105
|
+
<div class="text-xs text-white/30 font-mono truncate">{{ trace_item.trace_id }}</div>
|
|
99
106
|
</div>
|
|
100
|
-
{%
|
|
101
|
-
|
|
102
|
-
|
|
107
|
+
{% endif %}
|
|
108
|
+
{% if trace_item.user_id or trace_item.session_id %}
|
|
109
|
+
<div class="flex items-center gap-3 text-xs text-white/50">
|
|
110
|
+
{% if trace_item.user_id %}
|
|
111
|
+
<span class="flex items-center gap-1">
|
|
112
|
+
<svg class="w-3 h-3 text-white/70" fill="currentColor" viewBox="0 0 16 16">
|
|
113
|
+
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
|
|
114
|
+
</svg>
|
|
115
|
+
{{ trace_item.user_id }}
|
|
116
|
+
</span>
|
|
117
|
+
{% endif %}
|
|
118
|
+
{% if trace_item.session_id %}
|
|
119
|
+
<span class="flex items-center gap-1 min-w-0">
|
|
120
|
+
<svg class="w-3 h-3 text-white/70 flex-shrink-0" fill="currentColor" viewBox="0 0 16 16">
|
|
121
|
+
<path d="M13.5 3a.5.5 0 0 1 .5.5V11H2V3.5a.5.5 0 0 1 .5-.5zm-11-1A1.5 1.5 0 0 0 1 3.5V12h14V3.5A1.5 1.5 0 0 0 13.5 2zM0 12.5h16a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 12.5"/>
|
|
122
|
+
</svg>
|
|
123
|
+
<span class="truncate" title="{{ trace_item.session_id }}">{{ trace_item.session_id }}</span>
|
|
124
|
+
</span>
|
|
125
|
+
{% endif %}
|
|
103
126
|
</div>
|
|
104
127
|
{% endif %}
|
|
105
128
|
</div>
|
|
@@ -110,20 +133,20 @@
|
|
|
110
133
|
</div>
|
|
111
134
|
</aside>
|
|
112
135
|
|
|
113
|
-
<main id="
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
136
|
+
<main id="trace" class="flex-1 px-6 py-4 overflow-auto overscroll-contain ml-80">
|
|
137
|
+
<div hx-get="{{ traces.first().get_absolute_url() }}" hx-trigger="load" hx-swap="innerHTML">
|
|
138
|
+
<div class="flex items-center justify-center h-full">
|
|
139
|
+
<div class="text-white/50">Loading trace...</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
118
142
|
</main>
|
|
119
143
|
</div>
|
|
120
144
|
{% elif observer.is_enabled() %}
|
|
121
145
|
<div class="flex items-center justify-center min-h-screen p-6">
|
|
122
146
|
<div class="text-center max-w-2xl w-full">
|
|
123
147
|
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-6">
|
|
124
|
-
<!-- Icon and status -->
|
|
125
148
|
<div class="flex-shrink-0">
|
|
126
|
-
<div class="p-3 bg-
|
|
149
|
+
<div class="p-3 bg-white/10 rounded-full mb-3 sm:mb-0">
|
|
127
150
|
{% if observer.is_summarizing() %}
|
|
128
151
|
<svg width="32" height="32" fill="currentColor" class="text-yellow-500" viewBox="0 0 16 16">
|
|
129
152
|
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
|
@@ -137,16 +160,15 @@
|
|
|
137
160
|
</div>
|
|
138
161
|
</div>
|
|
139
162
|
|
|
140
|
-
<!-- Content -->
|
|
141
163
|
<div class="flex-1 text-center sm:text-left">
|
|
142
|
-
<h3 class="text-lg font-medium text-
|
|
164
|
+
<h3 class="text-lg font-medium text-white/80 mb-1">
|
|
143
165
|
{% if observer.is_summarizing() %}
|
|
144
166
|
Toolbar Summary Only
|
|
145
167
|
{% else %}
|
|
146
168
|
Recording Traces
|
|
147
169
|
{% endif %}
|
|
148
170
|
</h3>
|
|
149
|
-
<p class="text-sm text-
|
|
171
|
+
<p class="text-sm text-white/40 mb-4">
|
|
150
172
|
{% if observer.is_summarizing() %}
|
|
151
173
|
Performance summary is displayed in real-time. No traces are being stored.
|
|
152
174
|
{% else %}
|
|
@@ -154,53 +176,57 @@
|
|
|
154
176
|
{% endif %}
|
|
155
177
|
</p>
|
|
156
178
|
|
|
157
|
-
<!-- Actions -->
|
|
158
179
|
<div class="flex flex-col sm:flex-row items-center justify-center sm:justify-start gap-2">
|
|
159
180
|
{% if observer.is_summarizing() %}
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
181
|
+
<button
|
|
182
|
+
hx-put="."
|
|
183
|
+
plain-hx-action="mode"
|
|
184
|
+
hx-vals='{"mode": "persist"}'
|
|
185
|
+
hx-swap="morph:innerHTML"
|
|
186
|
+
hx-target="#main-content"
|
|
187
|
+
class="flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg bg-red-600 text-white hover:bg-red-700 cursor-pointer transition-colors">
|
|
188
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
189
|
+
<circle cx="8" cy="8" r="8"/>
|
|
190
|
+
</svg>
|
|
191
|
+
<span>Record Session Traces</span>
|
|
192
|
+
</button>
|
|
170
193
|
{% elif observer.is_persisting() %}
|
|
171
194
|
<button
|
|
172
195
|
hx-get="."
|
|
173
196
|
hx-swap="morph:innerHTML"
|
|
174
197
|
hx-target="#main-content"
|
|
175
|
-
class="flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg bg-
|
|
176
|
-
<svg class="htmx-request:animate-spin
|
|
198
|
+
class="flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg bg-white/10 text-white/70 hover:bg-white/20 cursor-pointer transition-colors">
|
|
199
|
+
<svg class="htmx-request:animate-spin w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
177
200
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
|
178
201
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
|
179
202
|
</svg>
|
|
180
203
|
<span class="htmx-request:hidden">Check for Traces</span>
|
|
181
204
|
<span class="hidden htmx-request:inline">Checking...</span>
|
|
182
205
|
</button>
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
<
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
<button
|
|
207
|
+
hx-put="."
|
|
208
|
+
plain-hx-action="mode"
|
|
209
|
+
hx-vals='{"mode": "summary"}'
|
|
210
|
+
hx-swap="morph:innerHTML"
|
|
211
|
+
hx-target="#main-content"
|
|
212
|
+
class="flex items-center space-x-2 px-3 py-1.5 text-sm rounded-lg bg-white/20 text-white/80 hover:bg-white/30 cursor-pointer transition-colors">
|
|
213
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
214
|
+
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
|
215
|
+
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
|
216
|
+
</svg>
|
|
217
|
+
<span>Stop Recording</span>
|
|
218
|
+
</button>
|
|
194
219
|
{% endif %}
|
|
195
220
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
221
|
+
<button
|
|
222
|
+
hx-put="."
|
|
223
|
+
plain-hx-action="mode"
|
|
224
|
+
hx-vals='{"mode": "disable"}'
|
|
225
|
+
hx-swap="morph:innerHTML"
|
|
226
|
+
hx-target="#main-content"
|
|
227
|
+
class="px-3 py-1.5 text-sm text-white/50 hover:text-white/40 transition-colors">
|
|
228
|
+
Disable Observer
|
|
229
|
+
</button>
|
|
204
230
|
</div>
|
|
205
231
|
</div>
|
|
206
232
|
</div>
|
|
@@ -210,10 +236,9 @@
|
|
|
210
236
|
<div class="flex items-center justify-center min-h-screen p-6">
|
|
211
237
|
<div class="text-center max-w-2xl w-full">
|
|
212
238
|
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-6">
|
|
213
|
-
<!-- Icon -->
|
|
214
239
|
<div class="flex-shrink-0">
|
|
215
|
-
<div class="p-3 bg-
|
|
216
|
-
<svg width="32" height="32" fill="currentColor" class="text-
|
|
240
|
+
<div class="p-3 bg-white/10 rounded-full mb-3 sm:mb-0">
|
|
241
|
+
<svg width="32" height="32" fill="currentColor" class="text-white/50" viewBox="0 0 16 16">
|
|
217
242
|
<path d="M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7.028 7.028 0 0 0-2.79.588l.77.771A5.944 5.944 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.134 13.134 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755-.165.165-.337.328-.517.486l.708.709z"/>
|
|
218
243
|
<path d="M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829l.822.822zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829z"/>
|
|
219
244
|
<path d="M3.35 5.47c-.18.16-.353.322-.518.487A13.134 13.134 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7.029 7.029 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.708zm10.296 8.884-12-12 .708-.708 12 12-.708.708z"/>
|
|
@@ -221,15 +246,13 @@
|
|
|
221
246
|
</div>
|
|
222
247
|
</div>
|
|
223
248
|
|
|
224
|
-
<!-- Content -->
|
|
225
249
|
<div class="flex-1 text-center sm:text-left">
|
|
226
|
-
<h3 class="text-lg font-medium text-
|
|
227
|
-
<p class="text-sm text-
|
|
250
|
+
<h3 class="text-lg font-medium text-white/80 mb-1">Observer is Disabled</h3>
|
|
251
|
+
<p class="text-sm text-white/40 mb-4">
|
|
228
252
|
Enable observer to start monitoring your application's performance and traces.
|
|
229
253
|
</p>
|
|
230
254
|
|
|
231
|
-
|
|
232
|
-
<div class="bg-stone-800/30 rounded-lg p-3 mb-4">
|
|
255
|
+
<div class="bg-white/10 rounded-lg p-3 mb-4">
|
|
233
256
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
|
234
257
|
<div class="text-left">
|
|
235
258
|
<h4 class="text-xs font-medium text-yellow-400 mb-1 flex items-center">
|
|
@@ -239,7 +262,7 @@
|
|
|
239
262
|
</svg>
|
|
240
263
|
Summary Mode
|
|
241
264
|
</h4>
|
|
242
|
-
<p class="text-xs text-
|
|
265
|
+
<p class="text-xs text-white/40">Monitor performance in real-time without saving traces.</p>
|
|
243
266
|
</div>
|
|
244
267
|
<div class="text-left">
|
|
245
268
|
<h4 class="text-xs font-medium text-red-400 mb-1 flex items-center">
|
|
@@ -248,34 +271,37 @@
|
|
|
248
271
|
</svg>
|
|
249
272
|
Recording Mode
|
|
250
273
|
</h4>
|
|
251
|
-
<p class="text-xs text-
|
|
274
|
+
<p class="text-xs text-white/40">Record and store traces for detailed analysis.</p>
|
|
252
275
|
</div>
|
|
253
276
|
</div>
|
|
254
277
|
</div>
|
|
255
278
|
|
|
256
|
-
<!-- Actions -->
|
|
257
279
|
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-2">
|
|
258
|
-
<
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
<
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
<button
|
|
281
|
+
hx-put="."
|
|
282
|
+
plain-hx-action="mode"
|
|
283
|
+
hx-vals='{"mode": "summary"}'
|
|
284
|
+
hx-swap="morph:innerHTML"
|
|
285
|
+
hx-target="#main-content"
|
|
286
|
+
class="flex items-center justify-center space-x-2 px-4 py-1.5 text-sm rounded-lg bg-white/20 text-white/80 hover:bg-white/30 cursor-pointer transition-colors w-full sm:w-auto">
|
|
287
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
288
|
+
<path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/>
|
|
289
|
+
<path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/>
|
|
290
|
+
</svg>
|
|
291
|
+
<span>Enable Summary</span>
|
|
292
|
+
</button>
|
|
293
|
+
<button
|
|
294
|
+
hx-put="."
|
|
295
|
+
plain-hx-action="mode"
|
|
296
|
+
hx-vals='{"mode": "persist"}'
|
|
297
|
+
hx-swap="morph:innerHTML"
|
|
298
|
+
hx-target="#main-content"
|
|
299
|
+
class="flex items-center justify-center space-x-2 px-4 py-1.5 text-sm rounded-lg bg-red-600 text-white hover:bg-red-700 cursor-pointer transition-colors w-full sm:w-auto">
|
|
300
|
+
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16">
|
|
301
|
+
<circle cx="8" cy="8" r="8"/>
|
|
302
|
+
</svg>
|
|
303
|
+
<span>Start Recording Session</span>
|
|
304
|
+
</button>
|
|
279
305
|
</div>
|
|
280
306
|
</div>
|
|
281
307
|
</div>
|
|
@@ -1,45 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
<button
|
|
2
|
+
type="button"
|
|
3
|
+
class="inline-flex items-center cursor-pointer text-xs rounded-full px-2.5 py-px bg-white/20 text-white/80 whitespace-nowrap hover:bg-white/30"
|
|
4
|
+
data-toolbar-tab="Observer">
|
|
5
|
+
{% if observer.is_persisting() %}
|
|
6
|
+
<span class="relative inline-flex size-2 mr-2.5">
|
|
7
|
+
<span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75"></span>
|
|
8
|
+
<span class="relative inline-flex size-2 rounded-full bg-red-500"></span>
|
|
9
|
+
</span>
|
|
10
|
+
{% elif observer.is_summarizing() %}
|
|
11
|
+
<span class="relative inline-flex size-2 mr-2">
|
|
12
|
+
<span class="relative inline-flex size-2 rounded-full bg-zinc-500"></span>
|
|
13
|
+
</span>
|
|
14
|
+
{% elif not observer.is_enabled() %}
|
|
15
|
+
<span class="rounded-full bg-zinc-500 w-2 h-2 inline-block mr-2"></span>
|
|
10
16
|
{% endif %}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{% elif observer.is_summarizing() %}
|
|
22
|
-
<span class="relative inline-flex size-2 mr-2.5">
|
|
23
|
-
<span class="relative inline-flex size-2 rounded-full bg-zinc-500"></span>
|
|
24
|
-
</span>
|
|
25
|
-
{% endif %}
|
|
26
|
-
{% if observer.get_current_trace_summary() %}
|
|
27
|
-
<span class="font-mono tracking-tight">{{ observer.get_current_trace_summary() }}</span>
|
|
28
|
-
{% else %}
|
|
29
|
-
{% if observer.is_persisting() %}Recording{% elif observer.is_summarizing() %}Summary{% else %}Observing{% endif %}
|
|
30
|
-
{% endif %}
|
|
31
|
-
</button>
|
|
32
|
-
</form>
|
|
33
|
-
{% else %}
|
|
34
|
-
<form method="post" action="{{ url('observer:traces') }}" style="display: inline;">
|
|
35
|
-
<input type="hidden" name="redirect_url" value="{{ request.get_full_path() }}">
|
|
36
|
-
<input type="hidden" name="observe_action" value="summary">
|
|
37
|
-
<button
|
|
38
|
-
type="submit"
|
|
39
|
-
class="cursor-pointer text-xs rounded-full px-2 py-px bg-white/20 text-white/80 whitespace-nowrap hover:bg-white/30"
|
|
40
|
-
title="Enable observer">
|
|
41
|
-
<span class="rounded-full bg-zinc-500 w-2 h-2 inline-block mr-1"></span>
|
|
17
|
+
|
|
18
|
+
{% if observer.get_current_trace_summary() %}
|
|
19
|
+
{{ observer.get_current_trace_summary() }}
|
|
20
|
+
{% elif observer.is_persisting() %}
|
|
21
|
+
Recording
|
|
22
|
+
{% elif observer.is_summarizing() %}
|
|
23
|
+
Summary
|
|
24
|
+
{% elif observer.is_enabled() %}
|
|
25
|
+
Observing
|
|
26
|
+
{% else %}
|
|
42
27
|
Observer disabled
|
|
43
|
-
|
|
44
|
-
</
|
|
45
|
-
{% endif %}
|
|
28
|
+
{% endif %}
|
|
29
|
+
</button>
|
plain/observer/urls.py
CHANGED
|
@@ -6,5 +6,7 @@ from . import views
|
|
|
6
6
|
class ObserverRouter(Router):
|
|
7
7
|
namespace = "observer"
|
|
8
8
|
urls = [
|
|
9
|
-
path("", views.ObserverTracesView, name="traces"),
|
|
9
|
+
path("traces/", views.ObserverTracesView, name="traces"),
|
|
10
|
+
path("traces/<trace_id>/", views.ObserverTraceDetailView, name="trace_detail"),
|
|
11
|
+
path("share/<share_id>/", views.ObserverTraceSharedView, name="trace_shared"),
|
|
10
12
|
]
|