provide-foundation 0.0.0.dev0__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.
- provide/__init__.py +15 -0
- provide/foundation/__init__.py +155 -0
- provide/foundation/_version.py +58 -0
- provide/foundation/cli/__init__.py +67 -0
- provide/foundation/cli/commands/__init__.py +3 -0
- provide/foundation/cli/commands/deps.py +71 -0
- provide/foundation/cli/commands/logs/__init__.py +63 -0
- provide/foundation/cli/commands/logs/generate.py +357 -0
- provide/foundation/cli/commands/logs/generate_old.py +569 -0
- provide/foundation/cli/commands/logs/query.py +174 -0
- provide/foundation/cli/commands/logs/send.py +166 -0
- provide/foundation/cli/commands/logs/tail.py +112 -0
- provide/foundation/cli/decorators.py +262 -0
- provide/foundation/cli/main.py +65 -0
- provide/foundation/cli/testing.py +220 -0
- provide/foundation/cli/utils.py +210 -0
- provide/foundation/config/__init__.py +106 -0
- provide/foundation/config/base.py +295 -0
- provide/foundation/config/env.py +369 -0
- provide/foundation/config/loader.py +311 -0
- provide/foundation/config/manager.py +387 -0
- provide/foundation/config/schema.py +284 -0
- provide/foundation/config/sync.py +281 -0
- provide/foundation/config/types.py +78 -0
- provide/foundation/config/validators.py +80 -0
- provide/foundation/console/__init__.py +29 -0
- provide/foundation/console/input.py +364 -0
- provide/foundation/console/output.py +178 -0
- provide/foundation/context/__init__.py +12 -0
- provide/foundation/context/core.py +356 -0
- provide/foundation/core.py +20 -0
- provide/foundation/crypto/__init__.py +182 -0
- provide/foundation/crypto/algorithms.py +111 -0
- provide/foundation/crypto/certificates.py +896 -0
- provide/foundation/crypto/checksums.py +301 -0
- provide/foundation/crypto/constants.py +57 -0
- provide/foundation/crypto/hashing.py +265 -0
- provide/foundation/crypto/keys.py +188 -0
- provide/foundation/crypto/signatures.py +144 -0
- provide/foundation/crypto/utils.py +164 -0
- provide/foundation/errors/__init__.py +96 -0
- provide/foundation/errors/auth.py +73 -0
- provide/foundation/errors/base.py +81 -0
- provide/foundation/errors/config.py +103 -0
- provide/foundation/errors/context.py +299 -0
- provide/foundation/errors/decorators.py +484 -0
- provide/foundation/errors/handlers.py +360 -0
- provide/foundation/errors/integration.py +105 -0
- provide/foundation/errors/platform.py +37 -0
- provide/foundation/errors/process.py +140 -0
- provide/foundation/errors/resources.py +133 -0
- provide/foundation/errors/runtime.py +160 -0
- provide/foundation/errors/safe_decorators.py +133 -0
- provide/foundation/errors/types.py +276 -0
- provide/foundation/file/__init__.py +79 -0
- provide/foundation/file/atomic.py +157 -0
- provide/foundation/file/directory.py +134 -0
- provide/foundation/file/formats.py +236 -0
- provide/foundation/file/lock.py +175 -0
- provide/foundation/file/safe.py +179 -0
- provide/foundation/file/utils.py +170 -0
- provide/foundation/hub/__init__.py +88 -0
- provide/foundation/hub/click_builder.py +310 -0
- provide/foundation/hub/commands.py +42 -0
- provide/foundation/hub/components.py +640 -0
- provide/foundation/hub/decorators.py +244 -0
- provide/foundation/hub/info.py +32 -0
- provide/foundation/hub/manager.py +446 -0
- provide/foundation/hub/registry.py +279 -0
- provide/foundation/hub/type_mapping.py +54 -0
- provide/foundation/hub/types.py +28 -0
- provide/foundation/logger/__init__.py +41 -0
- provide/foundation/logger/base.py +22 -0
- provide/foundation/logger/config/__init__.py +16 -0
- provide/foundation/logger/config/base.py +40 -0
- provide/foundation/logger/config/logging.py +394 -0
- provide/foundation/logger/config/telemetry.py +188 -0
- provide/foundation/logger/core.py +239 -0
- provide/foundation/logger/custom_processors.py +172 -0
- provide/foundation/logger/emoji/__init__.py +44 -0
- provide/foundation/logger/emoji/matrix.py +209 -0
- provide/foundation/logger/emoji/sets.py +458 -0
- provide/foundation/logger/emoji/types.py +56 -0
- provide/foundation/logger/factories.py +56 -0
- provide/foundation/logger/processors/__init__.py +13 -0
- provide/foundation/logger/processors/main.py +254 -0
- provide/foundation/logger/processors/trace.py +113 -0
- provide/foundation/logger/ratelimit/__init__.py +31 -0
- provide/foundation/logger/ratelimit/limiters.py +294 -0
- provide/foundation/logger/ratelimit/processor.py +203 -0
- provide/foundation/logger/ratelimit/queue_limiter.py +305 -0
- provide/foundation/logger/setup/__init__.py +29 -0
- provide/foundation/logger/setup/coordinator.py +138 -0
- provide/foundation/logger/setup/emoji_resolver.py +64 -0
- provide/foundation/logger/setup/processors.py +85 -0
- provide/foundation/logger/setup/testing.py +39 -0
- provide/foundation/logger/trace.py +38 -0
- provide/foundation/metrics/__init__.py +119 -0
- provide/foundation/metrics/otel.py +122 -0
- provide/foundation/metrics/simple.py +165 -0
- provide/foundation/observability/__init__.py +53 -0
- provide/foundation/observability/openobserve/__init__.py +79 -0
- provide/foundation/observability/openobserve/auth.py +72 -0
- provide/foundation/observability/openobserve/client.py +307 -0
- provide/foundation/observability/openobserve/commands.py +357 -0
- provide/foundation/observability/openobserve/exceptions.py +41 -0
- provide/foundation/observability/openobserve/formatters.py +298 -0
- provide/foundation/observability/openobserve/models.py +134 -0
- provide/foundation/observability/openobserve/otlp.py +320 -0
- provide/foundation/observability/openobserve/search.py +222 -0
- provide/foundation/observability/openobserve/streaming.py +235 -0
- provide/foundation/platform/__init__.py +44 -0
- provide/foundation/platform/detection.py +193 -0
- provide/foundation/platform/info.py +157 -0
- provide/foundation/process/__init__.py +39 -0
- provide/foundation/process/async_runner.py +373 -0
- provide/foundation/process/lifecycle.py +406 -0
- provide/foundation/process/runner.py +390 -0
- provide/foundation/setup/__init__.py +101 -0
- provide/foundation/streams/__init__.py +44 -0
- provide/foundation/streams/console.py +57 -0
- provide/foundation/streams/core.py +65 -0
- provide/foundation/streams/file.py +104 -0
- provide/foundation/testing/__init__.py +166 -0
- provide/foundation/testing/cli.py +227 -0
- provide/foundation/testing/crypto.py +163 -0
- provide/foundation/testing/fixtures.py +49 -0
- provide/foundation/testing/hub.py +23 -0
- provide/foundation/testing/logger.py +106 -0
- provide/foundation/testing/streams.py +54 -0
- provide/foundation/tracer/__init__.py +49 -0
- provide/foundation/tracer/context.py +115 -0
- provide/foundation/tracer/otel.py +135 -0
- provide/foundation/tracer/spans.py +174 -0
- provide/foundation/types.py +32 -0
- provide/foundation/utils/__init__.py +97 -0
- provide/foundation/utils/deps.py +195 -0
- provide/foundation/utils/env.py +491 -0
- provide/foundation/utils/formatting.py +483 -0
- provide/foundation/utils/parsing.py +235 -0
- provide/foundation/utils/rate_limiting.py +112 -0
- provide/foundation/utils/streams.py +67 -0
- provide/foundation/utils/timing.py +93 -0
- provide_foundation-0.0.0.dev0.dist-info/METADATA +469 -0
- provide_foundation-0.0.0.dev0.dist-info/RECORD +149 -0
- provide_foundation-0.0.0.dev0.dist-info/WHEEL +5 -0
- provide_foundation-0.0.0.dev0.dist-info/entry_points.txt +2 -0
- provide_foundation-0.0.0.dev0.dist-info/licenses/LICENSE +201 -0
- provide_foundation-0.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
"""
|
2
|
+
OpenObserve integration for Foundation.
|
3
|
+
|
4
|
+
Provides log querying and streaming capabilities when OpenTelemetry is available.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from provide.foundation.observability.openobserve.client import OpenObserveClient
|
8
|
+
from provide.foundation.observability.openobserve.exceptions import (
|
9
|
+
OpenObserveAuthenticationError,
|
10
|
+
OpenObserveConfigError,
|
11
|
+
OpenObserveConnectionError,
|
12
|
+
OpenObserveError,
|
13
|
+
OpenObserveQueryError,
|
14
|
+
OpenObserveStreamingError,
|
15
|
+
)
|
16
|
+
from provide.foundation.observability.openobserve.formatters import (
|
17
|
+
format_csv,
|
18
|
+
format_json,
|
19
|
+
format_log_line,
|
20
|
+
format_output,
|
21
|
+
format_summary,
|
22
|
+
format_table,
|
23
|
+
)
|
24
|
+
from provide.foundation.observability.openobserve.models import (
|
25
|
+
SearchQuery,
|
26
|
+
SearchResponse,
|
27
|
+
StreamInfo,
|
28
|
+
parse_relative_time,
|
29
|
+
)
|
30
|
+
from provide.foundation.observability.openobserve.search import (
|
31
|
+
aggregate_by_level,
|
32
|
+
get_current_trace_logs,
|
33
|
+
search_by_level,
|
34
|
+
search_by_service,
|
35
|
+
search_by_trace_id,
|
36
|
+
search_errors,
|
37
|
+
search_logs,
|
38
|
+
)
|
39
|
+
from provide.foundation.observability.openobserve.streaming import (
|
40
|
+
stream_logs,
|
41
|
+
stream_search_http2,
|
42
|
+
tail_logs,
|
43
|
+
)
|
44
|
+
|
45
|
+
__all__ = [
|
46
|
+
# Client
|
47
|
+
"OpenObserveClient",
|
48
|
+
# Search functions
|
49
|
+
"search_logs",
|
50
|
+
"search_by_trace_id",
|
51
|
+
"search_by_level",
|
52
|
+
"search_errors",
|
53
|
+
"search_by_service",
|
54
|
+
"aggregate_by_level",
|
55
|
+
"get_current_trace_logs",
|
56
|
+
# Streaming functions
|
57
|
+
"stream_logs",
|
58
|
+
"stream_search_http2",
|
59
|
+
"tail_logs",
|
60
|
+
# Models
|
61
|
+
"SearchQuery",
|
62
|
+
"SearchResponse",
|
63
|
+
"StreamInfo",
|
64
|
+
"parse_relative_time",
|
65
|
+
# Formatters
|
66
|
+
"format_json",
|
67
|
+
"format_log_line",
|
68
|
+
"format_table",
|
69
|
+
"format_csv",
|
70
|
+
"format_summary",
|
71
|
+
"format_output",
|
72
|
+
# Exceptions
|
73
|
+
"OpenObserveError",
|
74
|
+
"OpenObserveConnectionError",
|
75
|
+
"OpenObserveAuthenticationError",
|
76
|
+
"OpenObserveQueryError",
|
77
|
+
"OpenObserveStreamingError",
|
78
|
+
"OpenObserveConfigError",
|
79
|
+
]
|
@@ -0,0 +1,72 @@
|
|
1
|
+
"""
|
2
|
+
Authentication handling for OpenObserve.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import base64
|
6
|
+
|
7
|
+
from provide.foundation.observability.openobserve.exceptions import (
|
8
|
+
OpenObserveAuthenticationError,
|
9
|
+
)
|
10
|
+
|
11
|
+
|
12
|
+
def encode_basic_auth(username: str, password: str) -> str:
|
13
|
+
"""Encode username and password for Basic authentication.
|
14
|
+
|
15
|
+
Args:
|
16
|
+
username: OpenObserve username
|
17
|
+
password: OpenObserve password
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
Base64 encoded auth string
|
21
|
+
"""
|
22
|
+
credentials = f"{username}:{password}"
|
23
|
+
encoded = base64.b64encode(credentials.encode()).decode()
|
24
|
+
return encoded
|
25
|
+
|
26
|
+
|
27
|
+
def get_auth_headers(username: str | None, password: str | None) -> dict[str, str]:
|
28
|
+
"""Get authentication headers for OpenObserve API.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
username: OpenObserve username
|
32
|
+
password: OpenObserve password
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Dictionary with Authorization header
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
OpenObserveAuthenticationError: If credentials are missing
|
39
|
+
"""
|
40
|
+
if not username or not password:
|
41
|
+
raise OpenObserveAuthenticationError(
|
42
|
+
"OpenObserve credentials not configured. "
|
43
|
+
"Set OPENOBSERVE_USER and OPENOBSERVE_PASSWORD environment variables."
|
44
|
+
)
|
45
|
+
|
46
|
+
auth_token = encode_basic_auth(username, password)
|
47
|
+
return {
|
48
|
+
"Authorization": f"Basic {auth_token}",
|
49
|
+
"Content-Type": "application/json",
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
def validate_credentials(username: str | None, password: str | None) -> tuple[str, str]:
|
54
|
+
"""Validate and return OpenObserve credentials.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
username: OpenObserve username
|
58
|
+
password: OpenObserve password
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Tuple of (username, password)
|
62
|
+
|
63
|
+
Raises:
|
64
|
+
OpenObserveAuthenticationError: If credentials are invalid
|
65
|
+
"""
|
66
|
+
if not username:
|
67
|
+
raise OpenObserveAuthenticationError("OpenObserve username is required")
|
68
|
+
|
69
|
+
if not password:
|
70
|
+
raise OpenObserveAuthenticationError("OpenObserve password is required")
|
71
|
+
|
72
|
+
return username, password
|
@@ -0,0 +1,307 @@
|
|
1
|
+
"""
|
2
|
+
OpenObserve API client.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from datetime import datetime
|
6
|
+
import json
|
7
|
+
from typing import Any
|
8
|
+
from urllib.parse import urljoin
|
9
|
+
|
10
|
+
import requests
|
11
|
+
from requests.adapters import HTTPAdapter
|
12
|
+
from urllib3.util.retry import Retry
|
13
|
+
|
14
|
+
from provide.foundation.logger import get_logger
|
15
|
+
from provide.foundation.observability.openobserve.auth import (
|
16
|
+
get_auth_headers,
|
17
|
+
validate_credentials,
|
18
|
+
)
|
19
|
+
from provide.foundation.observability.openobserve.exceptions import (
|
20
|
+
OpenObserveConfigError,
|
21
|
+
OpenObserveConnectionError,
|
22
|
+
OpenObserveQueryError,
|
23
|
+
)
|
24
|
+
from provide.foundation.observability.openobserve.models import (
|
25
|
+
SearchQuery,
|
26
|
+
SearchResponse,
|
27
|
+
StreamInfo,
|
28
|
+
parse_relative_time,
|
29
|
+
)
|
30
|
+
|
31
|
+
log = get_logger(__name__)
|
32
|
+
|
33
|
+
|
34
|
+
class OpenObserveClient:
|
35
|
+
"""Client for interacting with OpenObserve API."""
|
36
|
+
|
37
|
+
def __init__(
|
38
|
+
self,
|
39
|
+
url: str,
|
40
|
+
username: str,
|
41
|
+
password: str,
|
42
|
+
organization: str = "default",
|
43
|
+
timeout: int = 30,
|
44
|
+
max_retries: int = 3,
|
45
|
+
):
|
46
|
+
"""Initialize OpenObserve client.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
url: Base URL for OpenObserve API
|
50
|
+
username: Username for authentication
|
51
|
+
password: Password for authentication
|
52
|
+
organization: Organization name (default: "default")
|
53
|
+
timeout: Request timeout in seconds
|
54
|
+
max_retries: Maximum number of retry attempts
|
55
|
+
"""
|
56
|
+
self.url = url.rstrip("/")
|
57
|
+
self.username, self.password = validate_credentials(username, password)
|
58
|
+
self.organization = organization
|
59
|
+
self.timeout = timeout
|
60
|
+
|
61
|
+
# Setup session with retry logic
|
62
|
+
self.session = requests.Session()
|
63
|
+
retry = Retry(
|
64
|
+
total=max_retries,
|
65
|
+
backoff_factor=0.3,
|
66
|
+
status_forcelist=[500, 502, 503, 504],
|
67
|
+
)
|
68
|
+
adapter = HTTPAdapter(max_retries=retry)
|
69
|
+
self.session.mount("http://", adapter)
|
70
|
+
self.session.mount("https://", adapter)
|
71
|
+
|
72
|
+
# Set default headers
|
73
|
+
self.session.headers.update(get_auth_headers(self.username, self.password))
|
74
|
+
|
75
|
+
@classmethod
|
76
|
+
def from_config(cls) -> "OpenObserveClient":
|
77
|
+
"""Create client from TelemetryConfig.
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
Configured OpenObserveClient instance
|
81
|
+
|
82
|
+
Raises:
|
83
|
+
OpenObserveConfigError: If configuration is missing
|
84
|
+
"""
|
85
|
+
from provide.foundation.logger.config import TelemetryConfig
|
86
|
+
|
87
|
+
config = TelemetryConfig.from_env()
|
88
|
+
|
89
|
+
if not config.openobserve_url:
|
90
|
+
raise OpenObserveConfigError(
|
91
|
+
"OpenObserve URL not configured. Set OPENOBSERVE_URL environment variable."
|
92
|
+
)
|
93
|
+
|
94
|
+
if not config.openobserve_user or not config.openobserve_password:
|
95
|
+
raise OpenObserveConfigError(
|
96
|
+
"OpenObserve credentials not configured. "
|
97
|
+
"Set OPENOBSERVE_USER and OPENOBSERVE_PASSWORD environment variables."
|
98
|
+
)
|
99
|
+
|
100
|
+
return cls(
|
101
|
+
url=config.openobserve_url,
|
102
|
+
username=config.openobserve_user,
|
103
|
+
password=config.openobserve_password,
|
104
|
+
organization=config.openobserve_org,
|
105
|
+
)
|
106
|
+
|
107
|
+
def _make_request(
|
108
|
+
self,
|
109
|
+
method: str,
|
110
|
+
endpoint: str,
|
111
|
+
params: dict[str, Any] | None = None,
|
112
|
+
json_data: dict[str, Any] | None = None,
|
113
|
+
) -> dict[str, Any]:
|
114
|
+
"""Make HTTP request to OpenObserve API.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
method: HTTP method
|
118
|
+
endpoint: API endpoint path
|
119
|
+
params: Query parameters
|
120
|
+
json_data: JSON body data
|
121
|
+
|
122
|
+
Returns:
|
123
|
+
Response data as dictionary
|
124
|
+
|
125
|
+
Raises:
|
126
|
+
OpenObserveConnectionError: On connection errors
|
127
|
+
OpenObserveQueryError: On API errors
|
128
|
+
"""
|
129
|
+
url = urljoin(self.url, f"/api/{self.organization}/{endpoint}")
|
130
|
+
|
131
|
+
try:
|
132
|
+
response = self.session.request(
|
133
|
+
method=method,
|
134
|
+
url=url,
|
135
|
+
params=params,
|
136
|
+
json=json_data,
|
137
|
+
timeout=self.timeout,
|
138
|
+
)
|
139
|
+
|
140
|
+
if response.status_code == 401:
|
141
|
+
raise OpenObserveConnectionError(
|
142
|
+
"Authentication failed. Check credentials."
|
143
|
+
)
|
144
|
+
|
145
|
+
response.raise_for_status()
|
146
|
+
|
147
|
+
# Handle empty responses
|
148
|
+
if not response.content:
|
149
|
+
return {}
|
150
|
+
|
151
|
+
return response.json()
|
152
|
+
|
153
|
+
except requests.exceptions.ConnectionError as e:
|
154
|
+
raise OpenObserveConnectionError(f"Failed to connect to OpenObserve: {e}")
|
155
|
+
except requests.exceptions.Timeout as e:
|
156
|
+
raise OpenObserveConnectionError(f"Request timed out: {e}")
|
157
|
+
except requests.exceptions.HTTPError as e:
|
158
|
+
# Try to extract error message from response
|
159
|
+
error_msg = str(e)
|
160
|
+
try:
|
161
|
+
error_data = e.response.json()
|
162
|
+
if "message" in error_data:
|
163
|
+
error_msg = error_data["message"]
|
164
|
+
except (json.JSONDecodeError, AttributeError):
|
165
|
+
pass
|
166
|
+
raise OpenObserveQueryError(f"API error: {error_msg}")
|
167
|
+
except Exception as e:
|
168
|
+
raise OpenObserveQueryError(f"Unexpected error: {e}")
|
169
|
+
|
170
|
+
def search(
|
171
|
+
self,
|
172
|
+
sql: str,
|
173
|
+
start_time: str | int | None = None,
|
174
|
+
end_time: str | int | None = None,
|
175
|
+
size: int = 100,
|
176
|
+
from_offset: int = 0,
|
177
|
+
) -> SearchResponse:
|
178
|
+
"""Execute a search query.
|
179
|
+
|
180
|
+
Args:
|
181
|
+
sql: SQL query to execute
|
182
|
+
start_time: Start time (relative like "-1h" or microseconds)
|
183
|
+
end_time: End time (relative like "now" or microseconds)
|
184
|
+
size: Number of results to return
|
185
|
+
from_offset: Offset for pagination
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
SearchResponse with results
|
189
|
+
"""
|
190
|
+
# Parse time parameters
|
191
|
+
now = datetime.now()
|
192
|
+
|
193
|
+
if start_time is None:
|
194
|
+
start_time = "-1h"
|
195
|
+
if end_time is None:
|
196
|
+
end_time = "now"
|
197
|
+
|
198
|
+
start_ts = (
|
199
|
+
parse_relative_time(str(start_time), now)
|
200
|
+
if isinstance(start_time, str)
|
201
|
+
else start_time
|
202
|
+
)
|
203
|
+
end_ts = (
|
204
|
+
parse_relative_time(str(end_time), now)
|
205
|
+
if isinstance(end_time, str)
|
206
|
+
else end_time
|
207
|
+
)
|
208
|
+
|
209
|
+
# Create query
|
210
|
+
query = SearchQuery(
|
211
|
+
sql=sql,
|
212
|
+
start_time=start_ts,
|
213
|
+
end_time=end_ts,
|
214
|
+
size=size,
|
215
|
+
from_offset=from_offset,
|
216
|
+
)
|
217
|
+
|
218
|
+
log.debug(f"Executing search query: {sql}")
|
219
|
+
|
220
|
+
# Make request
|
221
|
+
response = self._make_request(
|
222
|
+
method="POST",
|
223
|
+
endpoint="_search",
|
224
|
+
params={"is_ui_histogram": "false", "is_multi_stream_search": "false"},
|
225
|
+
json_data=query.to_dict(),
|
226
|
+
)
|
227
|
+
|
228
|
+
# Handle errors in response
|
229
|
+
if "error" in response:
|
230
|
+
raise OpenObserveQueryError(f"Query error: {response['error']}")
|
231
|
+
|
232
|
+
result = SearchResponse.from_dict(response)
|
233
|
+
|
234
|
+
# Log any function errors
|
235
|
+
if result.function_error:
|
236
|
+
for error in result.function_error:
|
237
|
+
log.warning(f"Query warning: {error}")
|
238
|
+
|
239
|
+
log.info(f"Search completed: {len(result.hits)} hits, took {result.took}ms")
|
240
|
+
|
241
|
+
return result
|
242
|
+
|
243
|
+
def list_streams(self) -> list[StreamInfo]:
|
244
|
+
"""List available streams.
|
245
|
+
|
246
|
+
Returns:
|
247
|
+
List of StreamInfo objects
|
248
|
+
"""
|
249
|
+
response = self._make_request(
|
250
|
+
method="GET",
|
251
|
+
endpoint="streams",
|
252
|
+
)
|
253
|
+
|
254
|
+
streams = []
|
255
|
+
if isinstance(response, dict):
|
256
|
+
# Response is a dict of stream types to stream lists
|
257
|
+
for stream_type, stream_list in response.items():
|
258
|
+
if isinstance(stream_list, list):
|
259
|
+
for stream_data in stream_list:
|
260
|
+
if isinstance(stream_data, dict):
|
261
|
+
stream_info = StreamInfo.from_dict(stream_data)
|
262
|
+
streams.append(stream_info)
|
263
|
+
|
264
|
+
return streams
|
265
|
+
|
266
|
+
def get_search_history(
|
267
|
+
self,
|
268
|
+
stream_name: str | None = None,
|
269
|
+
size: int = 100,
|
270
|
+
) -> SearchResponse:
|
271
|
+
"""Get search history.
|
272
|
+
|
273
|
+
Args:
|
274
|
+
stream_name: Filter by stream name
|
275
|
+
size: Number of history entries to return
|
276
|
+
|
277
|
+
Returns:
|
278
|
+
SearchResponse with history entries
|
279
|
+
"""
|
280
|
+
request_data = {
|
281
|
+
"size": size,
|
282
|
+
}
|
283
|
+
|
284
|
+
if stream_name:
|
285
|
+
request_data["stream_name"] = stream_name
|
286
|
+
|
287
|
+
response = self._make_request(
|
288
|
+
method="POST",
|
289
|
+
endpoint="_search_history",
|
290
|
+
json_data=request_data,
|
291
|
+
)
|
292
|
+
|
293
|
+
return SearchResponse.from_dict(response)
|
294
|
+
|
295
|
+
def test_connection(self) -> bool:
|
296
|
+
"""Test connection to OpenObserve.
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
True if connection successful
|
300
|
+
"""
|
301
|
+
try:
|
302
|
+
# Try to list streams as a simple test
|
303
|
+
self.list_streams()
|
304
|
+
return True
|
305
|
+
except Exception as e:
|
306
|
+
log.error(f"Connection test failed: {e}")
|
307
|
+
return False
|