plain.observer 0.6.2__tar.gz → 0.7.0__tar.gz
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-0.6.2 → plain_observer-0.7.0}/PKG-INFO +1 -1
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/CHANGELOG.md +14 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/admin.py +4 -4
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/cli.py +7 -7
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/models.py +8 -7
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/otel.py +7 -7
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace.html +1 -1
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/views.py +5 -5
- {plain_observer-0.6.2 → plain_observer-0.7.0}/pyproject.toml +1 -1
- {plain_observer-0.6.2 → plain_observer-0.7.0}/.gitignore +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/LICENSE +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/README.md +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/README.md +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/__init__.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/config.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/core.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/default_settings.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/logging.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0001_initial.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0002_trace_share_created_at_trace_share_id_trace_summary_and_more.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0003_span_plainobserv_span_id_e7ade3_idx.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0004_trace_app_name_trace_app_version.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0005_log_log_plainobserv_trace_i_fcfb7d_idx_and_more.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0006_remove_log_logger.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/__init__.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/log.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/span.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_detail.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_share.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/traces.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer_button.html +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/toolbar.py +0 -0
- {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/urls.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# plain-observer changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/dropseed/plain/releases/plain-observer@0.7.0) (2025-09-12)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- Model manager renamed from `objects` to `query` throughout the codebase for consistency with Plain framework conventions ([037a239](https://github.com/dropseed/plain/commit/037a239ef4))
|
|
8
|
+
- Updated internal QuerySet configuration to use `queryset_class` instead of `manager_class` in model Meta ([bbaee93](https://github.com/dropseed/plain/commit/bbaee93839))
|
|
9
|
+
- Simplified manager initialization by removing explicit `objects` assignment in favor of Meta configuration ([6b60a00](https://github.com/dropseed/plain/commit/6b60a00731))
|
|
10
|
+
|
|
11
|
+
### Upgrade instructions
|
|
12
|
+
|
|
13
|
+
- Replace any direct usage of `Trace.objects` with `Trace.query` in your code
|
|
14
|
+
- Replace any direct usage of `Span.objects` with `Span.query` in your code
|
|
15
|
+
- Replace any direct usage of `Log.objects` with `Log.query` in your code
|
|
16
|
+
|
|
3
17
|
## [0.6.2](https://github.com/dropseed/plain/releases/plain-observer@0.6.2) (2025-09-09)
|
|
4
18
|
|
|
5
19
|
### What's changed
|
|
@@ -26,7 +26,7 @@ class TraceViewset(AdminViewset):
|
|
|
26
26
|
|
|
27
27
|
def perform_action(self, action: str, target_ids: list):
|
|
28
28
|
if action == "Delete":
|
|
29
|
-
Trace.
|
|
29
|
+
Trace.query.filter(id__in=target_ids).delete()
|
|
30
30
|
|
|
31
31
|
class DetailView(AdminModelDetailView):
|
|
32
32
|
model = Trace
|
|
@@ -54,7 +54,7 @@ class SpanViewset(AdminViewset):
|
|
|
54
54
|
|
|
55
55
|
def perform_action(self, action: str, target_ids: list):
|
|
56
56
|
if action == "Delete":
|
|
57
|
-
Span.
|
|
57
|
+
Span.query.filter(id__in=target_ids).delete()
|
|
58
58
|
|
|
59
59
|
def get_objects(self):
|
|
60
60
|
return (
|
|
@@ -101,9 +101,9 @@ class LogViewset(AdminViewset):
|
|
|
101
101
|
|
|
102
102
|
def perform_action(self, action: str, target_ids: list):
|
|
103
103
|
if action == "Delete selected":
|
|
104
|
-
Log.
|
|
104
|
+
Log.query.filter(id__in=target_ids).delete()
|
|
105
105
|
elif action == "Delete all":
|
|
106
|
-
Log.
|
|
106
|
+
Log.query.all().delete()
|
|
107
107
|
|
|
108
108
|
def get_objects(self):
|
|
109
109
|
return (
|
|
@@ -19,7 +19,7 @@ def observer_cli():
|
|
|
19
19
|
@click.option("--force", is_flag=True, help="Skip confirmation prompt.")
|
|
20
20
|
def clear(force: bool):
|
|
21
21
|
"""Clear all observer data."""
|
|
22
|
-
query = Trace.
|
|
22
|
+
query = Trace.query.all()
|
|
23
23
|
trace_count = query.count()
|
|
24
24
|
|
|
25
25
|
if trace_count == 0:
|
|
@@ -43,7 +43,7 @@ def clear(force: bool):
|
|
|
43
43
|
def trace_list(limit, user_id, request_id, session_id, output_json):
|
|
44
44
|
"""List recent traces."""
|
|
45
45
|
# Build query
|
|
46
|
-
query = Trace.
|
|
46
|
+
query = Trace.query.all()
|
|
47
47
|
|
|
48
48
|
if user_id:
|
|
49
49
|
query = query.filter(user_id=user_id)
|
|
@@ -139,7 +139,7 @@ def trace_list(limit, user_id, request_id, session_id, output_json):
|
|
|
139
139
|
def trace_detail(trace_id, output_json):
|
|
140
140
|
"""Display detailed information about a specific trace."""
|
|
141
141
|
try:
|
|
142
|
-
trace = Trace.
|
|
142
|
+
trace = Trace.query.get(trace_id=trace_id)
|
|
143
143
|
except Trace.DoesNotExist:
|
|
144
144
|
click.secho(f"Error: Trace with ID '{trace_id}' not found", fg="red", err=True)
|
|
145
145
|
raise click.Abort()
|
|
@@ -157,7 +157,7 @@ def trace_detail(trace_id, output_json):
|
|
|
157
157
|
def span_list(trace_id, limit, output_json):
|
|
158
158
|
"""List recent spans."""
|
|
159
159
|
# Build query
|
|
160
|
-
query = Span.
|
|
160
|
+
query = Span.query.all()
|
|
161
161
|
|
|
162
162
|
if trace_id:
|
|
163
163
|
query = query.filter(trace__trace_id=trace_id)
|
|
@@ -259,7 +259,7 @@ def span_list(trace_id, limit, output_json):
|
|
|
259
259
|
def span_detail(span_id, output_json):
|
|
260
260
|
"""Display detailed information about a specific span."""
|
|
261
261
|
try:
|
|
262
|
-
span = Span.
|
|
262
|
+
span = Span.query.select_related("trace").get(span_id=span_id)
|
|
263
263
|
except Span.DoesNotExist:
|
|
264
264
|
click.secho(f"Error: Span with ID '{span_id}' not found", fg="red", err=True)
|
|
265
265
|
raise click.Abort()
|
|
@@ -387,7 +387,7 @@ def format_trace_output(trace):
|
|
|
387
387
|
output_lines.append(click.style("Spans:", fg="bright_blue", bold=True))
|
|
388
388
|
|
|
389
389
|
# Get annotated spans with nesting levels
|
|
390
|
-
spans = trace.spans.all().annotate_spans()
|
|
390
|
+
spans = trace.spans.query.all().annotate_spans()
|
|
391
391
|
|
|
392
392
|
# Build parent-child relationships
|
|
393
393
|
span_dict = {span.span_id: span for span in spans}
|
|
@@ -540,7 +540,7 @@ def diagnose(trace_id, url, json_input, agent_command, print_only):
|
|
|
540
540
|
raise click.ClickException(f"Error fetching trace from URL: {e}")
|
|
541
541
|
else:
|
|
542
542
|
try:
|
|
543
|
-
trace = Trace.
|
|
543
|
+
trace = Trace.query.get(trace_id=trace_id)
|
|
544
544
|
trace_data = trace.as_dict()
|
|
545
545
|
except Trace.DoesNotExist:
|
|
546
546
|
raise click.ClickException(f"Trace with ID '{trace_id}' not found")
|
|
@@ -188,7 +188,9 @@ class Trace(models.Model):
|
|
|
188
188
|
)
|
|
189
189
|
|
|
190
190
|
def as_dict(self):
|
|
191
|
-
spans = [
|
|
191
|
+
spans = [
|
|
192
|
+
span.span_data for span in self.spans.query.all().order_by("start_time")
|
|
193
|
+
]
|
|
192
194
|
logs = [
|
|
193
195
|
{
|
|
194
196
|
"timestamp": log.timestamp.isoformat(),
|
|
@@ -196,7 +198,7 @@ class Trace(models.Model):
|
|
|
196
198
|
"message": log.message,
|
|
197
199
|
"span_id": log.span_id,
|
|
198
200
|
}
|
|
199
|
-
for log in self.logs.all().order_by("timestamp")
|
|
201
|
+
for log in self.logs.query.all().order_by("timestamp")
|
|
200
202
|
]
|
|
201
203
|
|
|
202
204
|
return {
|
|
@@ -219,7 +221,7 @@ class Trace(models.Model):
|
|
|
219
221
|
"""Get chronological list of spans and logs for unified timeline display."""
|
|
220
222
|
events = []
|
|
221
223
|
|
|
222
|
-
for span in self.spans.all().annotate_spans():
|
|
224
|
+
for span in self.spans.query.all().annotate_spans():
|
|
223
225
|
events.append(
|
|
224
226
|
{
|
|
225
227
|
"type": "span",
|
|
@@ -230,7 +232,7 @@ class Trace(models.Model):
|
|
|
230
232
|
)
|
|
231
233
|
|
|
232
234
|
# Add logs for this span
|
|
233
|
-
for log in self.logs.filter(span=span):
|
|
235
|
+
for log in self.logs.query.filter(span=span):
|
|
234
236
|
events.append(
|
|
235
237
|
{
|
|
236
238
|
"type": "log",
|
|
@@ -241,7 +243,7 @@ class Trace(models.Model):
|
|
|
241
243
|
)
|
|
242
244
|
|
|
243
245
|
# Add unlinked logs (logs without span)
|
|
244
|
-
for log in self.logs.filter(span__isnull=True):
|
|
246
|
+
for log in self.logs.query.filter(span__isnull=True):
|
|
245
247
|
events.append(
|
|
246
248
|
{
|
|
247
249
|
"type": "log",
|
|
@@ -316,9 +318,8 @@ class Span(models.Model):
|
|
|
316
318
|
status = models.CharField(max_length=50, default="", required=False)
|
|
317
319
|
span_data = models.JSONField(default=dict, required=False)
|
|
318
320
|
|
|
319
|
-
objects = SpanQuerySet.as_manager()
|
|
320
|
-
|
|
321
321
|
class Meta:
|
|
322
|
+
queryset_class = SpanQuerySet
|
|
322
323
|
ordering = ["-start_time"]
|
|
323
324
|
constraints = [
|
|
324
325
|
models.UniqueConstraint(
|
|
@@ -342,7 +342,7 @@ class ObserverSpanProcessor(SpanProcessor):
|
|
|
342
342
|
span.trace = trace
|
|
343
343
|
|
|
344
344
|
# Bulk create spans
|
|
345
|
-
Span.
|
|
345
|
+
Span.query.bulk_create(spans)
|
|
346
346
|
|
|
347
347
|
# Create log models if we have logs
|
|
348
348
|
if logs:
|
|
@@ -362,7 +362,7 @@ class ObserverSpanProcessor(SpanProcessor):
|
|
|
362
362
|
)
|
|
363
363
|
log_models.append(log_model)
|
|
364
364
|
|
|
365
|
-
Log.
|
|
365
|
+
Log.query.bulk_create(log_models)
|
|
366
366
|
|
|
367
367
|
except Exception as e:
|
|
368
368
|
logger.warning(
|
|
@@ -374,14 +374,14 @@ class ObserverSpanProcessor(SpanProcessor):
|
|
|
374
374
|
# Delete oldest traces if we exceed the limit
|
|
375
375
|
if settings.OBSERVER_TRACE_LIMIT > 0:
|
|
376
376
|
try:
|
|
377
|
-
if Trace.
|
|
377
|
+
if Trace.query.count() > settings.OBSERVER_TRACE_LIMIT:
|
|
378
378
|
excess_count = (
|
|
379
|
-
Trace.
|
|
379
|
+
Trace.query.count() - settings.OBSERVER_TRACE_LIMIT
|
|
380
380
|
)
|
|
381
|
-
delete_ids = Trace.
|
|
381
|
+
delete_ids = Trace.query.order_by("start_time")[
|
|
382
382
|
:excess_count
|
|
383
383
|
].values_list("id", flat=True)
|
|
384
|
-
Trace.
|
|
384
|
+
Trace.query.filter(id__in=delete_ids).delete()
|
|
385
385
|
except Exception as e:
|
|
386
386
|
logger.warning(
|
|
387
387
|
"Failed to clean up old observer traces: %s", e, exc_info=True
|
|
@@ -401,7 +401,7 @@ class ObserverSpanProcessor(SpanProcessor):
|
|
|
401
401
|
from .models import Span
|
|
402
402
|
|
|
403
403
|
with suppress_db_tracing():
|
|
404
|
-
if Span.
|
|
404
|
+
if Span.query.filter(
|
|
405
405
|
span_id=f"0x{format_span_id(link.context.span_id)}"
|
|
406
406
|
).exists():
|
|
407
407
|
return ObserverMode.PERSIST.value
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
|
|
127
127
|
<div class="mt-6">
|
|
128
128
|
<div class="flex items-center justify-between mb-3">
|
|
129
|
-
<h3 class="text-sm font-medium text-white/80">Spans ({{ trace.spans.count() }}) + Logs ({{ trace.logs.count() }})</h3>
|
|
129
|
+
<h3 class="text-sm font-medium text-white/80">Spans ({{ trace.spans.query.count() }}) + Logs ({{ trace.logs.query.count() }})</h3>
|
|
130
130
|
<div class="text-xs text-white/60">
|
|
131
131
|
<span class="text-white/50">Started:</span> {{ trace.start_time|localtime|strftime("%b %-d, %-I:%M %p") }}
|
|
132
132
|
</div>
|
|
@@ -15,7 +15,7 @@ class ObserverTracesView(AuthViewMixin, HTMXViewMixin, ListView):
|
|
|
15
15
|
admin_required = True
|
|
16
16
|
|
|
17
17
|
def get_objects(self):
|
|
18
|
-
return Trace.
|
|
18
|
+
return Trace.query.all()
|
|
19
19
|
|
|
20
20
|
def check_auth(self):
|
|
21
21
|
# Allow the view if we're in DEBUG
|
|
@@ -56,7 +56,7 @@ class ObserverTracesView(AuthViewMixin, HTMXViewMixin, ListView):
|
|
|
56
56
|
|
|
57
57
|
def htmx_delete_traces(self):
|
|
58
58
|
"""Clear all traces via HTMX DELETE."""
|
|
59
|
-
Trace.
|
|
59
|
+
Trace.query.filter(share_id="").delete()
|
|
60
60
|
response = Response(status_code=204)
|
|
61
61
|
response.headers["HX-Refresh"] = "true"
|
|
62
62
|
return response
|
|
@@ -80,7 +80,7 @@ class ObserverTraceDetailView(AuthViewMixin, HTMXViewMixin, DetailView):
|
|
|
80
80
|
admin_required = True
|
|
81
81
|
|
|
82
82
|
def get_object(self):
|
|
83
|
-
return Trace.
|
|
83
|
+
return Trace.query.get_or_none(trace_id=self.url_kwargs.get("trace_id"))
|
|
84
84
|
|
|
85
85
|
def check_auth(self):
|
|
86
86
|
# Allow the view if we're in DEBUG
|
|
@@ -97,7 +97,7 @@ class ObserverTraceDetailView(AuthViewMixin, HTMXViewMixin, DetailView):
|
|
|
97
97
|
return self.object.as_dict()
|
|
98
98
|
|
|
99
99
|
if self.request.query_params.get("logs") == "true":
|
|
100
|
-
logs = self.object.logs.all().order_by("timestamp")
|
|
100
|
+
logs = self.object.logs.query.all().order_by("timestamp")
|
|
101
101
|
log_lines = []
|
|
102
102
|
for log in logs:
|
|
103
103
|
timestamp = log.timestamp.strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
@@ -137,7 +137,7 @@ class ObserverTraceSharedView(DetailView):
|
|
|
137
137
|
context_object_name = "trace"
|
|
138
138
|
|
|
139
139
|
def get_object(self):
|
|
140
|
-
return Trace.
|
|
140
|
+
return Trace.query.get_or_none(share_id=self.url_kwargs["share_id"])
|
|
141
141
|
|
|
142
142
|
def get_template_context(self):
|
|
143
143
|
context = super().get_template_context()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0006_remove_log_logger.py
RENAMED
|
File without changes
|
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/log.html
RENAMED
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/span.html
RENAMED
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_detail.html
RENAMED
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_share.html
RENAMED
|
File without changes
|
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer.html
RENAMED
|
File without changes
|
{plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer_button.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|