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.

Files changed (34) hide show
  1. {plain_observer-0.6.2 → plain_observer-0.7.0}/PKG-INFO +1 -1
  2. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/CHANGELOG.md +14 -0
  3. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/admin.py +4 -4
  4. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/cli.py +7 -7
  5. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/models.py +8 -7
  6. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/otel.py +7 -7
  7. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace.html +1 -1
  8. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/views.py +5 -5
  9. {plain_observer-0.6.2 → plain_observer-0.7.0}/pyproject.toml +1 -1
  10. {plain_observer-0.6.2 → plain_observer-0.7.0}/.gitignore +0 -0
  11. {plain_observer-0.6.2 → plain_observer-0.7.0}/LICENSE +0 -0
  12. {plain_observer-0.6.2 → plain_observer-0.7.0}/README.md +0 -0
  13. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/README.md +0 -0
  14. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/__init__.py +0 -0
  15. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/config.py +0 -0
  16. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/core.py +0 -0
  17. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/default_settings.py +0 -0
  18. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/logging.py +0 -0
  19. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0001_initial.py +0 -0
  20. {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
  21. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0003_span_plainobserv_span_id_e7ade3_idx.py +0 -0
  22. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0004_trace_app_name_trace_app_version.py +0 -0
  23. {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
  24. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/0006_remove_log_logger.py +0 -0
  25. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/migrations/__init__.py +0 -0
  26. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/log.html +0 -0
  27. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/partials/span.html +0 -0
  28. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_detail.html +0 -0
  29. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/trace_share.html +0 -0
  30. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/observer/traces.html +0 -0
  31. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer.html +0 -0
  32. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/templates/toolbar/observer_button.html +0 -0
  33. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/toolbar.py +0 -0
  34. {plain_observer-0.6.2 → plain_observer-0.7.0}/plain/observer/urls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plain.observer
3
- Version: 0.6.2
3
+ Version: 0.7.0
4
4
  Summary: On-page telemetry and observability tools for Plain.
5
5
  Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
6
  License-Expression: BSD-3-Clause
@@ -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.objects.filter(id__in=target_ids).delete()
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.objects.filter(id__in=target_ids).delete()
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.objects.filter(id__in=target_ids).delete()
104
+ Log.query.filter(id__in=target_ids).delete()
105
105
  elif action == "Delete all":
106
- Log.objects.all().delete()
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.objects.all()
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.objects.all()
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.objects.get(trace_id=trace_id)
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.objects.all()
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.objects.select_related("trace").get(span_id=span_id)
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.objects.get(trace_id=trace_id)
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 = [span.span_data for span in self.spans.all().order_by("start_time")]
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.objects.bulk_create(spans)
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.objects.bulk_create(log_models)
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.objects.count() > settings.OBSERVER_TRACE_LIMIT:
377
+ if Trace.query.count() > settings.OBSERVER_TRACE_LIMIT:
378
378
  excess_count = (
379
- Trace.objects.count() - settings.OBSERVER_TRACE_LIMIT
379
+ Trace.query.count() - settings.OBSERVER_TRACE_LIMIT
380
380
  )
381
- delete_ids = Trace.objects.order_by("start_time")[
381
+ delete_ids = Trace.query.order_by("start_time")[
382
382
  :excess_count
383
383
  ].values_list("id", flat=True)
384
- Trace.objects.filter(id__in=delete_ids).delete()
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.objects.filter(
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.objects.all()
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.objects.filter(share_id="").delete()
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.objects.get_or_none(trace_id=self.url_kwargs.get("trace_id"))
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.objects.get_or_none(share_id=self.url_kwargs["share_id"])
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()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "plain.observer"
3
- version = "0.6.2"
3
+ version = "0.7.0"
4
4
  description = "On-page telemetry and observability tools for Plain."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  license = "BSD-3-Clause"
File without changes
File without changes