plain.observer 0.10.1__py3-none-any.whl → 0.11.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.
Potentially problematic release.
This version of plain.observer might be problematic. Click here for more details.
- plain/observer/AGENTS.md +6 -0
- plain/observer/CHANGELOG.md +25 -0
- plain/observer/cli.py +2 -2
- plain/observer/core.py +103 -5
- plain/observer/otel.py +18 -40
- plain/observer/templates/observer/traces.html +31 -30
- plain/observer/toolbar.py +1 -1
- plain/observer/views.py +3 -3
- {plain_observer-0.10.1.dist-info → plain_observer-0.11.1.dist-info}/METADATA +1 -1
- {plain_observer-0.10.1.dist-info → plain_observer-0.11.1.dist-info}/RECORD +12 -11
- {plain_observer-0.10.1.dist-info → plain_observer-0.11.1.dist-info}/WHEEL +0 -0
- {plain_observer-0.10.1.dist-info → plain_observer-0.11.1.dist-info}/licenses/LICENSE +0 -0
plain/observer/AGENTS.md
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Plain Observer AGENTS.md
|
|
2
|
+
|
|
3
|
+
- Send a request and record traces: `plain agent request /some/path --user 1 --header "Observer: persist"`
|
|
4
|
+
- Find traces by request ID: `plain observer traces --request-id abc-123-def`
|
|
5
|
+
- Diagnose trace: `plain observer diagnose [TRACE_ID]`
|
|
6
|
+
- Output raw trace data: `plain observer trace [TRACE_ID] --json`
|
plain/observer/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# plain-observer changelog
|
|
2
2
|
|
|
3
|
+
## [0.11.1](https://github.com/dropseed/plain/releases/plain-observer@0.11.1) (2025-10-10)
|
|
4
|
+
|
|
5
|
+
### What's changed
|
|
6
|
+
|
|
7
|
+
- Trace list items now update the URL when clicked, allowing direct linking to specific traces ([9f29b68](https://github.com/dropseed/plain/commit/9f29b68a87))
|
|
8
|
+
- Improved trace sidebar layout by moving the timestamp to the bottom right and creating better visual hierarchy ([9f29b68](https://github.com/dropseed/plain/commit/9f29b68a87))
|
|
9
|
+
- Updated diagnose command prompt text to be less personal in tone ([c82d67b](https://github.com/dropseed/plain/commit/c82d67bfcf))
|
|
10
|
+
|
|
11
|
+
### Upgrade instructions
|
|
12
|
+
|
|
13
|
+
- No changes required
|
|
14
|
+
|
|
15
|
+
## [0.11.0](https://github.com/dropseed/plain/releases/plain-observer@0.11.0) (2025-10-08)
|
|
16
|
+
|
|
17
|
+
### What's changed
|
|
18
|
+
|
|
19
|
+
- Observer can now be enabled in DEBUG mode using an `Observer` HTTP header (e.g., `Observer: persist` or `Observer: summary`), which takes precedence over cookies ([cba149a](https://github.com/dropseed/plain/commit/cba149a40e))
|
|
20
|
+
- Added validation for observer mode values that raises helpful errors in DEBUG mode when invalid values are provided ([cba149a](https://github.com/dropseed/plain/commit/cba149a40e))
|
|
21
|
+
- Refactored `Observer` class to accept cookies and headers as constructor parameters, with new `from_request()` and `from_otel_context()` factory methods for improved testability ([cba149a](https://github.com/dropseed/plain/commit/cba149a40e))
|
|
22
|
+
- Added AGENTS.md file with helpful commands for AI agents working with Plain Observer ([cba149a](https://github.com/dropseed/plain/commit/cba149a40e))
|
|
23
|
+
|
|
24
|
+
### Upgrade instructions
|
|
25
|
+
|
|
26
|
+
- No changes required
|
|
27
|
+
|
|
3
28
|
## [0.10.1](https://github.com/dropseed/plain/releases/plain-observer@0.10.1) (2025-10-08)
|
|
4
29
|
|
|
5
30
|
### What's changed
|
plain/observer/cli.py
CHANGED
|
@@ -564,9 +564,9 @@ def diagnose(
|
|
|
564
564
|
raise click.ClickException(f"Trace with ID '{trace_id}' not found")
|
|
565
565
|
|
|
566
566
|
prompt_lines: list[str] = [
|
|
567
|
-
"
|
|
567
|
+
"Below is OpenTelemetry trace data JSON from a Plain application. Analyze it for performance issues or improvements.",
|
|
568
568
|
"",
|
|
569
|
-
"Focus on easy and obvious wins first and foremost. You have access to the codebase, so make sure you look at it before suggesting anything! If there is nothing obvious, that's ok --
|
|
569
|
+
"Focus on easy and obvious wins first and foremost. You have access to the codebase, so make sure you look at it before suggesting anything! If there is nothing obvious, that's ok -- say that and ask whether there are specific things we should look deeper into.",
|
|
570
570
|
"",
|
|
571
571
|
"If potential code changes are found, briefly explain them and ask whether we should implement them.",
|
|
572
572
|
"",
|
plain/observer/core.py
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
4
|
+
from collections.abc import MutableMapping
|
|
3
5
|
from enum import Enum
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
4
7
|
|
|
5
|
-
from
|
|
8
|
+
from opentelemetry import baggage
|
|
9
|
+
|
|
10
|
+
from plain.http import Response
|
|
11
|
+
from plain.http.cookie import unsign_cookie_value
|
|
12
|
+
from plain.runtime import settings
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from plain.http import Request
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
6
18
|
|
|
7
19
|
|
|
8
20
|
class ObserverMode(Enum):
|
|
@@ -12,20 +24,106 @@ class ObserverMode(Enum):
|
|
|
12
24
|
PERSIST = "persist" # Real-time monitoring + DB export
|
|
13
25
|
DISABLED = "disabled" # Observer explicitly disabled
|
|
14
26
|
|
|
27
|
+
@classmethod
|
|
28
|
+
def validate(cls, mode: str | None, source: str = "value") -> str | None:
|
|
29
|
+
"""Validate observer mode value.
|
|
30
|
+
|
|
31
|
+
In DEBUG mode, raises ValueError for invalid values.
|
|
32
|
+
In production, logs debug message and returns None for invalid values.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
mode: The mode value to validate
|
|
36
|
+
source: Description of where the mode came from (for error messages)
|
|
37
|
+
|
|
38
|
+
Returns the mode if valid, None otherwise.
|
|
39
|
+
"""
|
|
40
|
+
if mode is None:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
valid_modes = (cls.SUMMARY.value, cls.PERSIST.value, cls.DISABLED.value)
|
|
44
|
+
|
|
45
|
+
if mode not in valid_modes:
|
|
46
|
+
if settings.DEBUG:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"Invalid Observer {source}: '{mode}'. "
|
|
49
|
+
f"Valid values are: {', '.join(valid_modes)}"
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
logger.debug(
|
|
53
|
+
"Invalid observer mode %s: '%s'. Expected one of: %s",
|
|
54
|
+
source,
|
|
55
|
+
mode,
|
|
56
|
+
valid_modes,
|
|
57
|
+
)
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
return mode
|
|
61
|
+
|
|
15
62
|
|
|
16
63
|
class Observer:
|
|
17
64
|
"""Central class for managing observer state and operations."""
|
|
18
65
|
|
|
19
66
|
COOKIE_NAME = "observer"
|
|
67
|
+
DEBUG_HEADER_NAME = "Observer"
|
|
20
68
|
SUMMARY_COOKIE_DURATION = 60 * 60 * 24 * 7 # 1 week in seconds
|
|
21
69
|
PERSIST_COOKIE_DURATION = 60 * 60 * 24 # 1 day in seconds
|
|
22
70
|
|
|
23
|
-
def __init__(self,
|
|
24
|
-
self.
|
|
71
|
+
def __init__(self, *, cookies: dict[str, str], headers: dict[str, str]) -> None:
|
|
72
|
+
self.cookies = cookies
|
|
73
|
+
self.headers = headers
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_request(cls, request: Request) -> Observer:
|
|
77
|
+
"""Create an Observer instance from a request object."""
|
|
78
|
+
return cls(cookies=request.cookies, headers=request.headers)
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_otel_context(cls, context: Any) -> Observer:
|
|
82
|
+
"""Create an Observer instance from an OpenTelemetry context.
|
|
83
|
+
|
|
84
|
+
This method extracts cookies and headers from the OTEL baggage.
|
|
85
|
+
"""
|
|
86
|
+
cookies = cast(
|
|
87
|
+
MutableMapping[str, str] | None,
|
|
88
|
+
baggage.get_baggage("http.request.cookies", context),
|
|
89
|
+
)
|
|
90
|
+
if not cookies:
|
|
91
|
+
cookies = {}
|
|
92
|
+
|
|
93
|
+
headers = cast(
|
|
94
|
+
MutableMapping[str, str] | None,
|
|
95
|
+
baggage.get_baggage("http.request.headers", context),
|
|
96
|
+
)
|
|
97
|
+
if not headers:
|
|
98
|
+
headers = {}
|
|
99
|
+
|
|
100
|
+
return cls(cookies=cookies, headers=headers)
|
|
25
101
|
|
|
26
102
|
def mode(self) -> str | None:
|
|
27
|
-
"""Get the current observer mode from
|
|
28
|
-
|
|
103
|
+
"""Get the current observer mode from header (DEBUG only) or cookie.
|
|
104
|
+
|
|
105
|
+
In DEBUG mode, the Observer header takes precedence over the cookie.
|
|
106
|
+
Returns 'summary', 'persist', 'disabled', or None.
|
|
107
|
+
|
|
108
|
+
Raises ValueError if invalid value is found (DEBUG only).
|
|
109
|
+
"""
|
|
110
|
+
# Check Observer header in DEBUG mode (takes precedence)
|
|
111
|
+
if settings.DEBUG:
|
|
112
|
+
if observer_header := self.headers.get(self.DEBUG_HEADER_NAME):
|
|
113
|
+
observer_mode = observer_header.lower()
|
|
114
|
+
return ObserverMode.validate(observer_mode, source="header value")
|
|
115
|
+
|
|
116
|
+
# Check cookie
|
|
117
|
+
observer_cookie = self.cookies.get(self.COOKIE_NAME)
|
|
118
|
+
if not observer_cookie:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
mode = unsign_cookie_value(self.COOKIE_NAME, observer_cookie, default=None)
|
|
123
|
+
return ObserverMode.validate(mode, source="cookie value")
|
|
124
|
+
except Exception as e:
|
|
125
|
+
logger.debug("Failed to unsign observer cookie: %s", e)
|
|
126
|
+
return None
|
|
29
127
|
|
|
30
128
|
def is_enabled(self) -> bool:
|
|
31
129
|
"""Check if observer is enabled (either summary or persist mode)."""
|
plain/observer/otel.py
CHANGED
|
@@ -8,7 +8,7 @@ from collections.abc import MutableMapping, Sequence
|
|
|
8
8
|
from typing import TYPE_CHECKING, Any, cast
|
|
9
9
|
|
|
10
10
|
import opentelemetry.context as context_api
|
|
11
|
-
from opentelemetry import
|
|
11
|
+
from opentelemetry import trace
|
|
12
12
|
from opentelemetry.context import Context
|
|
13
13
|
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, sampling
|
|
14
14
|
from opentelemetry.semconv.attributes import url_attributes
|
|
@@ -20,7 +20,6 @@ from opentelemetry.trace import (
|
|
|
20
20
|
format_trace_id,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
-
from plain.http.cookie import unsign_cookie_value
|
|
24
23
|
from plain.logs import app_logger
|
|
25
24
|
from plain.models.otel import suppress_db_tracing
|
|
26
25
|
from plain.runtime import settings
|
|
@@ -105,28 +104,20 @@ class ObserverSampler(sampling.Sampler):
|
|
|
105
104
|
attributes=attributes,
|
|
106
105
|
)
|
|
107
106
|
|
|
108
|
-
# If no processor decision, check cookies directly for root spans
|
|
107
|
+
# If no processor decision, check headers and cookies directly for root spans
|
|
109
108
|
decision: sampling.Decision | None = None
|
|
110
109
|
if parent_context:
|
|
111
|
-
# Check
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
ObserverMode.PERSIST.value,
|
|
123
|
-
ObserverMode.SUMMARY.value,
|
|
124
|
-
):
|
|
125
|
-
# Always use RECORD_AND_SAMPLE so ParentBased works correctly
|
|
126
|
-
# The processor will check the mode to decide whether to export
|
|
127
|
-
decision = sampling.Decision.RECORD_AND_SAMPLE
|
|
128
|
-
else:
|
|
129
|
-
decision = sampling.Decision.DROP
|
|
110
|
+
# Check Observer header (DEBUG only) and cookies
|
|
111
|
+
mode = Observer.from_otel_context(parent_context).mode()
|
|
112
|
+
|
|
113
|
+
# Set decision based on mode
|
|
114
|
+
if mode in (ObserverMode.PERSIST.value, ObserverMode.SUMMARY.value):
|
|
115
|
+
# Always use RECORD_AND_SAMPLE so ParentBased works correctly
|
|
116
|
+
# The processor will check the mode to decide whether to export
|
|
117
|
+
decision = sampling.Decision.RECORD_AND_SAMPLE
|
|
118
|
+
elif mode == ObserverMode.DISABLED.value:
|
|
119
|
+
# Explicitly disabled - never sample even with remote parent
|
|
120
|
+
decision = sampling.Decision.DROP
|
|
130
121
|
|
|
131
122
|
# If there are links, assume it is to another trace/span that we are keeping
|
|
132
123
|
if links:
|
|
@@ -441,25 +432,12 @@ class ObserverSpanProcessor(SpanProcessor):
|
|
|
441
432
|
if not (context := parent_context or context_api.get_current()):
|
|
442
433
|
return None
|
|
443
434
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
baggage.get_baggage("http.request.cookies", context),
|
|
447
|
-
)
|
|
448
|
-
if not cookies:
|
|
449
|
-
return None
|
|
450
|
-
|
|
451
|
-
observer_cookie = cookies.get(Observer.COOKIE_NAME)
|
|
452
|
-
if not observer_cookie:
|
|
453
|
-
return None
|
|
435
|
+
# Check Observer header (DEBUG only) and cookies
|
|
436
|
+
mode = Observer.from_otel_context(context).mode()
|
|
454
437
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
)
|
|
459
|
-
if mode in (ObserverMode.SUMMARY.value, ObserverMode.PERSIST.value):
|
|
460
|
-
return mode
|
|
461
|
-
except Exception as e:
|
|
462
|
-
logger.warning("Failed to unsign observer cookie: %s", e)
|
|
438
|
+
# Only return valid recording modes (summary/persist), not disabled
|
|
439
|
+
if mode in (ObserverMode.SUMMARY.value, ObserverMode.PERSIST.value):
|
|
440
|
+
return mode
|
|
463
441
|
|
|
464
442
|
return None
|
|
465
443
|
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
hx-get="{{ trace_item.get_absolute_url() }}"
|
|
89
89
|
hx-target="#trace"
|
|
90
90
|
hx-swap="innerHTML"
|
|
91
|
+
hx-push-url="true"
|
|
91
92
|
class="block w-full text-left p-3 transition-colors hover:bg-white/5 focus:outline-none focus:bg-white/5"
|
|
92
93
|
data-trace-id="{{ trace_item.trace_id }}">
|
|
93
94
|
<div class="flex items-center justify-between gap-2">
|
|
@@ -98,9 +99,6 @@
|
|
|
98
99
|
<div class="text-xs font-medium text-white/30 font-mono truncate">{{ trace_item.trace_id }}</div>
|
|
99
100
|
{% endif %}
|
|
100
101
|
</div>
|
|
101
|
-
<div class="flex items-center gap-1.5 flex-shrink-0">
|
|
102
|
-
<span class="text-white/40 text-xs" title="{{ trace_item.start_time|localtime }}">{{ trace_item.start_time|timesince }} ago</span>
|
|
103
|
-
</div>
|
|
104
102
|
</div>
|
|
105
103
|
<div class="mt-1 space-y-1">
|
|
106
104
|
{% if trace_item.summary %}<div class="text-xs text-white/60">{{ trace_item.summary }}</div>{% endif %}
|
|
@@ -114,35 +112,38 @@
|
|
|
114
112
|
<div class="text-xs text-white/30 font-mono truncate">{{ trace_item.trace_id }}</div>
|
|
115
113
|
</div>
|
|
116
114
|
{% endif %}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
115
|
+
<div class="flex items-center justify-between gap-3 text-xs text-white/50">
|
|
116
|
+
{% if trace_item.user_id or trace_item.session_id or trace_item.app_version %}
|
|
117
|
+
<div class="flex items-center gap-3">
|
|
118
|
+
{% if trace_item.user_id %}
|
|
119
|
+
<span class="flex items-center gap-1 flex-shrink-0">
|
|
120
|
+
<svg class="w-3 h-3 text-white/70" fill="currentColor" viewBox="0 0 16 16">
|
|
121
|
+
<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"/>
|
|
122
|
+
</svg>
|
|
123
|
+
{{ trace_item.user_id }}
|
|
124
|
+
</span>
|
|
125
|
+
{% endif %}
|
|
126
|
+
{% if trace_item.session_id %}
|
|
127
|
+
<span class="flex items-center gap-1 min-w-0 flex-shrink">
|
|
128
|
+
<svg class="w-3 h-3 text-white/70 flex-shrink-0" fill="currentColor" viewBox="0 0 16 16">
|
|
129
|
+
<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"/>
|
|
130
|
+
</svg>
|
|
131
|
+
<span class="truncate" title="{{ trace_item.session_id }}">{{ trace_item.session_id }}</span>
|
|
132
|
+
</span>
|
|
133
|
+
{% endif %}
|
|
134
|
+
{% if trace_item.app_version %}
|
|
135
|
+
<span class="flex items-center gap-1 flex-shrink-0">
|
|
136
|
+
<svg class="w-3 h-3 text-white/70" fill="currentColor" viewBox="0 0 16 16">
|
|
137
|
+
<path d="M6 4.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0 .5.5 0 0 0 1 0z"/>
|
|
138
|
+
<path d="M2 1h4.586a1 1 0 0 1 .707.293l7 7a1 1 0 0 1 0 1.414l-4.586 4.586a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 1 6.586V2a1 1 0 0 1 1-1zm0 5.586 7 7L13.586 9l-7-7H2v4.586z"/>
|
|
139
|
+
</svg>
|
|
140
|
+
{{ trace_item.app_version }}
|
|
141
|
+
</span>
|
|
142
|
+
{% endif %}
|
|
143
|
+
</div>
|
|
143
144
|
{% endif %}
|
|
145
|
+
<span class="flex-shrink-0 text-white/40" title="{{ trace_item.start_time|localtime }}">{{ trace_item.start_time|timesince }} ago</span>
|
|
144
146
|
</div>
|
|
145
|
-
{% endif %}
|
|
146
147
|
</div>
|
|
147
148
|
</a>
|
|
148
149
|
</li>
|
plain/observer/toolbar.py
CHANGED
|
@@ -17,7 +17,7 @@ class ObserverToolbarItem(ToolbarItem):
|
|
|
17
17
|
@cached_property
|
|
18
18
|
def observer(self) -> Observer:
|
|
19
19
|
"""Get the Observer instance for this request."""
|
|
20
|
-
return Observer(self.request)
|
|
20
|
+
return Observer.from_request(self.request)
|
|
21
21
|
|
|
22
22
|
def get_template_context(self) -> dict[str, Any]:
|
|
23
23
|
context = super().get_template_context()
|
plain/observer/views.py
CHANGED
|
@@ -37,13 +37,13 @@ class ObserverTracesView(AuthViewMixin, HTMXViewMixin, ListView):
|
|
|
37
37
|
|
|
38
38
|
def get_template_context(self) -> dict[str, Any]:
|
|
39
39
|
context = super().get_template_context()
|
|
40
|
-
context["observer"] = Observer(self.request)
|
|
40
|
+
context["observer"] = Observer.from_request(self.request)
|
|
41
41
|
return context
|
|
42
42
|
|
|
43
43
|
def htmx_put_mode(self) -> Response:
|
|
44
44
|
"""Set observer mode via HTMX PUT."""
|
|
45
45
|
mode = self.request.data.get("mode")
|
|
46
|
-
observer = Observer(self.request)
|
|
46
|
+
observer = Observer.from_request(self.request)
|
|
47
47
|
|
|
48
48
|
response = Response(status_code=204)
|
|
49
49
|
response.headers["HX-Refresh"] = "true"
|
|
@@ -70,7 +70,7 @@ class ObserverTracesView(AuthViewMixin, HTMXViewMixin, ListView):
|
|
|
70
70
|
"""Handle POST requests to set observer mode."""
|
|
71
71
|
action = self.request.data.get("observe_action")
|
|
72
72
|
if action == "summary":
|
|
73
|
-
observer = Observer(self.request)
|
|
73
|
+
observer = Observer.from_request(self.request)
|
|
74
74
|
response = Response(status_code=204)
|
|
75
75
|
observer.enable_summary_mode(response)
|
|
76
76
|
return response
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
plain/observer/
|
|
1
|
+
plain/observer/AGENTS.md,sha256=w3vqd2VWGliBQl8ohEBOsHyo1KROoUuK10uDr_C5Xeo,334
|
|
2
|
+
plain/observer/CHANGELOG.md,sha256=DdqLFtPkmytx7ds8ZTIGjjPDQ5iWcx5CpwY0ynqxc9w,14781
|
|
2
3
|
plain/observer/README.md,sha256=39RA17fgcyOkqeIWBOAg4Be6YZjoiDzK5PVOG-nseuY,988
|
|
3
4
|
plain/observer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
5
|
plain/observer/admin.py,sha256=mSwJWfA9svhjZCzwqjD82Fv_iV9iENUtUiefV1s3lSE,3590
|
|
5
|
-
plain/observer/cli.py,sha256=
|
|
6
|
+
plain/observer/cli.py,sha256=rzESdG8rYH6ILZ_5jA6oR362kCjTlimgzNqng89s2yg,21131
|
|
6
7
|
plain/observer/config.py,sha256=BJKsKNbfNSYmaxpUAkuR6gDMgNvD9tx19wpo3-1sksA,2542
|
|
7
|
-
plain/observer/core.py,sha256=
|
|
8
|
+
plain/observer/core.py,sha256=9AQFe4pwLpz4w64GG7599rNxuTkMN3eyqsqFNehy8A0,5950
|
|
8
9
|
plain/observer/default_settings.py,sha256=JN2jT2wfa6f80EqU0p4Ox_47xyxL-Ym5-_pftY7xj2U,197
|
|
9
10
|
plain/observer/logging.py,sha256=amv5i06ghszoJ192vrZ0zFdnTajazMxei40ltf0kI50,3061
|
|
10
11
|
plain/observer/models.py,sha256=6hyPAjK-Uq24n-2TkN0eHW8xcDIMUprMAFMY6rlLXiI,18365
|
|
11
|
-
plain/observer/otel.py,sha256=
|
|
12
|
-
plain/observer/toolbar.py,sha256=
|
|
12
|
+
plain/observer/otel.py,sha256=QbWoSc2KxDDhGfBQeww_uKBy3DL1SCOomvmxzU_yAVM,16902
|
|
13
|
+
plain/observer/toolbar.py,sha256=btFUZIOQu0dkXOz7dWay79S0eBpadtSdfGyHF0yRVY4,720
|
|
13
14
|
plain/observer/urls.py,sha256=oLJoDjB7YMUx8z-MGql_xXSXkfacVKFp-aHNXaKzs38,376
|
|
14
|
-
plain/observer/views.py,sha256=
|
|
15
|
+
plain/observer/views.py,sha256=johyO4MaLvROCexelrPDw0O-mIylzstJS7bzN94NOBg,5424
|
|
15
16
|
plain/observer/migrations/0001_initial.py,sha256=HVoSrd5V-IOqD1adADIAzqMH8xMlPwyLOFH6JcGFniI,3312
|
|
16
17
|
plain/observer/migrations/0002_trace_share_created_at_trace_share_id_trace_summary_and_more.py,sha256=lgFqdn66zq7AoCvkC3MIvLXiE_Omup5EEMDSe1jpKOg,1765
|
|
17
18
|
plain/observer/migrations/0003_span_plainobserv_span_id_e7ade3_idx.py,sha256=54G8GXi-PiFbcTU-HrXO14UN9PxhjDCY05TA9HHkkmk,527
|
|
@@ -22,12 +23,12 @@ plain/observer/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
22
23
|
plain/observer/templates/observer/trace.html,sha256=zHf6GIdNDYBD7pqWxVvfm0R3fMTS6ZiFrOuq8eFxwmI,11374
|
|
23
24
|
plain/observer/templates/observer/trace_detail.html,sha256=86-YGCpg9gy31Pd2vDHB3LYRkJile-DsGD1Enjx9s5Y,943
|
|
24
25
|
plain/observer/templates/observer/trace_share.html,sha256=HrYLti5BpX96-6Bm_37OObFvAJSYbZ3GZD-MwCi9hgA,525
|
|
25
|
-
plain/observer/templates/observer/traces.html,sha256=
|
|
26
|
+
plain/observer/templates/observer/traces.html,sha256=48MFsDjNCrouAiIQoSD-CqeeyHIN8-24k7DuqAmiiVY,24531
|
|
26
27
|
plain/observer/templates/observer/partials/log.html,sha256=Vpp0j-GLBfwTBFyZp_cv0jkOLpgxfd-8-RMBe6V4HbU,964
|
|
27
28
|
plain/observer/templates/observer/partials/span.html,sha256=PaWZONsABwlS7mdKsJlYbhMSOzidq_WyOp6ifvFUgmM,18672
|
|
28
29
|
plain/observer/templates/toolbar/observer.html,sha256=uaDKiWR7EYqC1kEXE-uHDlE7nfFEMR_zmOgvlKwQHJ4,1365
|
|
29
30
|
plain/observer/templates/toolbar/observer_button.html,sha256=FMBJHKMGqpHWs-3Ei2PBCdVYZ_6vFw6-eKH_i4jQu-Q,1215
|
|
30
|
-
plain_observer-0.
|
|
31
|
-
plain_observer-0.
|
|
32
|
-
plain_observer-0.
|
|
33
|
-
plain_observer-0.
|
|
31
|
+
plain_observer-0.11.1.dist-info/METADATA,sha256=11ezQixaEebjSiKXZY8ZVdP9nrgVQ5N9Z-yoQqpf7SA,1387
|
|
32
|
+
plain_observer-0.11.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
33
|
+
plain_observer-0.11.1.dist-info/licenses/LICENSE,sha256=YZdq6Pz8ivjs97eSVLRmoGDI1hjEikX6N49DfM0DWio,1500
|
|
34
|
+
plain_observer-0.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|