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,357 @@
|
|
1
|
+
#
|
2
|
+
# generate.py
|
3
|
+
#
|
4
|
+
"""
|
5
|
+
Command to generate logs for testing OpenObserve integration with Foundation's rate limiting.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import random
|
9
|
+
import time
|
10
|
+
from typing import Any
|
11
|
+
|
12
|
+
try:
|
13
|
+
import click
|
14
|
+
|
15
|
+
_HAS_CLICK = True
|
16
|
+
except ImportError:
|
17
|
+
click = None
|
18
|
+
_HAS_CLICK = False
|
19
|
+
|
20
|
+
import threading
|
21
|
+
|
22
|
+
from provide.foundation.logger import get_logger
|
23
|
+
|
24
|
+
log = get_logger(__name__)
|
25
|
+
|
26
|
+
# Cut-up phrases inspired by Burroughs
|
27
|
+
BURROUGHS_PHRASES = [
|
28
|
+
"mutated Soft Machine prescribed within data stream",
|
29
|
+
"pre-recorded talking asshole dissolved into under neon hum",
|
30
|
+
"the viral Word carrying a new strain of reality",
|
31
|
+
"memory banks spilling future-pasts onto the terminal floor",
|
32
|
+
"the soft typewriter of the Other Half",
|
33
|
+
"control mechanisms broadcast in reversed time signatures",
|
34
|
+
"equations of control flickering on a broken monitor",
|
35
|
+
"semantic disturbances in Sector 9",
|
36
|
+
"the Biologic Courts passing sentence in a dream",
|
37
|
+
"a thousand junk units screaming in unison",
|
38
|
+
"frequency shift reported by Sector 5",
|
39
|
+
"the algebra of need written in neural static",
|
40
|
+
]
|
41
|
+
|
42
|
+
# Service names
|
43
|
+
SERVICE_NAMES = [
|
44
|
+
"api-gateway",
|
45
|
+
"auth-service",
|
46
|
+
"user-service",
|
47
|
+
"payment-processor",
|
48
|
+
"notification-service",
|
49
|
+
"search-index",
|
50
|
+
"cache-layer",
|
51
|
+
"data-pipeline",
|
52
|
+
"ml-inference",
|
53
|
+
"report-generator",
|
54
|
+
"webhook-handler",
|
55
|
+
"queue-processor",
|
56
|
+
"stream-analyzer",
|
57
|
+
"batch-job",
|
58
|
+
"cron-scheduler",
|
59
|
+
"interzone-terminal",
|
60
|
+
"nova-police",
|
61
|
+
"reality-studio",
|
62
|
+
]
|
63
|
+
|
64
|
+
# Operations
|
65
|
+
OPERATIONS = [
|
66
|
+
"process_request",
|
67
|
+
"validate_input",
|
68
|
+
"execute_query",
|
69
|
+
"transform_data",
|
70
|
+
"send_notification",
|
71
|
+
"update_cache",
|
72
|
+
"sync_state",
|
73
|
+
"aggregate_metrics",
|
74
|
+
"encode_response",
|
75
|
+
"decode_request",
|
76
|
+
"authorize_access",
|
77
|
+
"refresh_token",
|
78
|
+
"persist_data",
|
79
|
+
"emit_event",
|
80
|
+
"handle_error",
|
81
|
+
"transmit_signal",
|
82
|
+
"intercept_word",
|
83
|
+
"decode_reality",
|
84
|
+
]
|
85
|
+
|
86
|
+
# Trace and span ID tracking
|
87
|
+
_trace_counter = 0
|
88
|
+
_span_counter = 0
|
89
|
+
_trace_lock = threading.Lock()
|
90
|
+
|
91
|
+
|
92
|
+
def generate_trace_id() -> str:
|
93
|
+
"""Generate a unique trace ID."""
|
94
|
+
global _trace_counter
|
95
|
+
with _trace_lock:
|
96
|
+
trace_id = f"trace_{_trace_counter:08d}"
|
97
|
+
_trace_counter += 1
|
98
|
+
return trace_id
|
99
|
+
|
100
|
+
|
101
|
+
def generate_span_id() -> str:
|
102
|
+
"""Generate a unique span ID."""
|
103
|
+
global _span_counter
|
104
|
+
with _trace_lock:
|
105
|
+
span_id = f"span_{_span_counter:08d}"
|
106
|
+
_span_counter += 1
|
107
|
+
return span_id
|
108
|
+
|
109
|
+
|
110
|
+
def generate_log_entry(
|
111
|
+
index: int, style: str = "normal", error_rate: float = 0.1
|
112
|
+
) -> dict[str, Any]:
|
113
|
+
"""
|
114
|
+
Generate a single log entry with optional error simulation.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
index: Log entry index
|
118
|
+
style: Style of log generation ("normal" or "burroughs")
|
119
|
+
error_rate: Probability of generating an error log (0.0 to 1.0)
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
Dict containing log entry data
|
123
|
+
"""
|
124
|
+
# Choose message based on style
|
125
|
+
if style == "burroughs":
|
126
|
+
message = random.choice(BURROUGHS_PHRASES)
|
127
|
+
else:
|
128
|
+
# Normal tech-style messages
|
129
|
+
operations = [
|
130
|
+
"processed",
|
131
|
+
"validated",
|
132
|
+
"executed",
|
133
|
+
"transformed",
|
134
|
+
"cached",
|
135
|
+
"synced",
|
136
|
+
]
|
137
|
+
objects = ["request", "query", "data", "event", "message", "transaction"]
|
138
|
+
message = f"Successfully {random.choice(operations)} {random.choice(objects)}"
|
139
|
+
|
140
|
+
# Generate error condition
|
141
|
+
is_error = random.random() < error_rate
|
142
|
+
|
143
|
+
# Base entry
|
144
|
+
entry = {
|
145
|
+
"message": message,
|
146
|
+
"service": random.choice(SERVICE_NAMES),
|
147
|
+
"operation": random.choice(OPERATIONS),
|
148
|
+
"iteration": index,
|
149
|
+
"trace_id": generate_trace_id()
|
150
|
+
if index % 10 == 0
|
151
|
+
else f"trace_{(_trace_counter - 1):08d}",
|
152
|
+
"span_id": generate_span_id(),
|
153
|
+
"duration_ms": random.randint(10, 5000),
|
154
|
+
}
|
155
|
+
|
156
|
+
# Add error fields if this is an error
|
157
|
+
if is_error:
|
158
|
+
entry["level"] = "error"
|
159
|
+
entry["error_code"] = random.choice([400, 404, 500, 503])
|
160
|
+
entry["error_type"] = random.choice(
|
161
|
+
[
|
162
|
+
"ValidationError",
|
163
|
+
"ServiceUnavailable",
|
164
|
+
"TimeoutError",
|
165
|
+
"DatabaseError",
|
166
|
+
"RateLimitExceeded",
|
167
|
+
]
|
168
|
+
)
|
169
|
+
else:
|
170
|
+
# Random log level for non-errors
|
171
|
+
entry["level"] = random.choice(["debug", "info", "warning"])
|
172
|
+
|
173
|
+
# Add domain/action/status for DAS emoji system
|
174
|
+
entry["domain"] = random.choice(["user", "system", "data", "api", None])
|
175
|
+
entry["action"] = random.choice(["create", "read", "update", "delete", None])
|
176
|
+
entry["status"] = (
|
177
|
+
"error" if is_error else random.choice(["success", "pending", None])
|
178
|
+
)
|
179
|
+
|
180
|
+
return entry
|
181
|
+
|
182
|
+
|
183
|
+
@click.command(name="generate")
|
184
|
+
@click.option(
|
185
|
+
"-n", "--count", default=100, help="Number of logs to generate (0 for continuous)"
|
186
|
+
)
|
187
|
+
@click.option("-r", "--rate", default=10.0, help="Logs per second rate")
|
188
|
+
@click.option("-s", "--stream", default="default", help="Target stream name")
|
189
|
+
@click.option(
|
190
|
+
"--style",
|
191
|
+
type=click.Choice(["normal", "burroughs"]),
|
192
|
+
default="normal",
|
193
|
+
help="Message generation style",
|
194
|
+
)
|
195
|
+
@click.option("-e", "--error-rate", default=0.1, help="Error rate (0.0 to 1.0)")
|
196
|
+
@click.option(
|
197
|
+
"--enable-rate-limit", is_flag=True, help="Enable Foundation's rate limiting"
|
198
|
+
)
|
199
|
+
@click.option("--rate-limit", default=100.0, help="Rate limit (logs/s) when enabled")
|
200
|
+
def generate_logs_command(
|
201
|
+
count: int,
|
202
|
+
rate: float,
|
203
|
+
stream: str,
|
204
|
+
style: str,
|
205
|
+
error_rate: float,
|
206
|
+
enable_rate_limit: bool,
|
207
|
+
rate_limit: float,
|
208
|
+
):
|
209
|
+
"""Generate logs to test OpenObserve integration with Foundation's rate limiting."""
|
210
|
+
|
211
|
+
click.echo("š Starting log generation...")
|
212
|
+
click.echo(f" Style: {style}")
|
213
|
+
click.echo(f" Error rate: {int(error_rate * 100)}%")
|
214
|
+
click.echo(f" Target stream: {stream}")
|
215
|
+
|
216
|
+
if count == 0:
|
217
|
+
click.echo(f" Mode: Continuous at {rate} logs/second")
|
218
|
+
else:
|
219
|
+
click.echo(f" Count: {count} logs at {rate} logs/second")
|
220
|
+
|
221
|
+
if enable_rate_limit:
|
222
|
+
click.echo(f" ā ļø Foundation rate limiting enabled: {rate_limit} logs/s max")
|
223
|
+
|
224
|
+
# Configure Foundation's rate limiting
|
225
|
+
from provide.foundation.logger.ratelimit import GlobalRateLimiter
|
226
|
+
|
227
|
+
limiter = GlobalRateLimiter()
|
228
|
+
limiter.configure(
|
229
|
+
global_rate=rate_limit,
|
230
|
+
global_capacity=rate_limit * 2, # Allow burst up to 2x the rate
|
231
|
+
)
|
232
|
+
|
233
|
+
click.echo(" Press Ctrl+C to stop\n")
|
234
|
+
|
235
|
+
# Track statistics
|
236
|
+
logs_sent = 0
|
237
|
+
logs_failed = 0
|
238
|
+
logs_rate_limited = 0
|
239
|
+
start_time = time.time()
|
240
|
+
last_stats_time = start_time
|
241
|
+
last_stats_sent = 0
|
242
|
+
|
243
|
+
try:
|
244
|
+
if count == 0:
|
245
|
+
# Continuous mode
|
246
|
+
index = 0
|
247
|
+
while True:
|
248
|
+
current_time = time.time()
|
249
|
+
|
250
|
+
# Generate log entry
|
251
|
+
entry = generate_log_entry(index, style, error_rate)
|
252
|
+
index += 1
|
253
|
+
|
254
|
+
# Send using Foundation's logger
|
255
|
+
try:
|
256
|
+
service_logger = get_logger(f"generated.{entry['service']}")
|
257
|
+
|
258
|
+
# Extract level and remove from entry
|
259
|
+
level = entry.pop("level", "info")
|
260
|
+
message = entry.pop("message")
|
261
|
+
|
262
|
+
# Log at appropriate level
|
263
|
+
getattr(service_logger, level)(message, **entry)
|
264
|
+
logs_sent += 1
|
265
|
+
|
266
|
+
except Exception as e:
|
267
|
+
logs_failed += 1
|
268
|
+
if "rate limit" in str(e).lower():
|
269
|
+
logs_rate_limited += 1
|
270
|
+
|
271
|
+
# Control rate
|
272
|
+
target_interval = 1.0 / rate
|
273
|
+
elapsed = current_time - start_time
|
274
|
+
expected_count = int(elapsed * rate)
|
275
|
+
|
276
|
+
if logs_sent < expected_count:
|
277
|
+
# We're behind, no sleep
|
278
|
+
pass
|
279
|
+
else:
|
280
|
+
# We're on track or ahead, sleep until next interval
|
281
|
+
next_time = start_time + (logs_sent / rate)
|
282
|
+
sleep_time = next_time - time.time()
|
283
|
+
if sleep_time > 0:
|
284
|
+
time.sleep(sleep_time)
|
285
|
+
|
286
|
+
# Print stats every second
|
287
|
+
if current_time - last_stats_time >= 1.0:
|
288
|
+
current_rate = (logs_sent - last_stats_sent) / (
|
289
|
+
current_time - last_stats_time
|
290
|
+
)
|
291
|
+
|
292
|
+
status = f"š Sent: {logs_sent:,} | Rate: {current_rate:.0f}/s"
|
293
|
+
if logs_failed > 0:
|
294
|
+
status += f" | Failed: {logs_failed:,}"
|
295
|
+
if enable_rate_limit and logs_rate_limited > 0:
|
296
|
+
status += f" | ā ļø Rate limited: {logs_rate_limited:,}"
|
297
|
+
|
298
|
+
click.echo(status)
|
299
|
+
last_stats_time = current_time
|
300
|
+
last_stats_sent = logs_sent
|
301
|
+
|
302
|
+
else:
|
303
|
+
# Fixed count mode
|
304
|
+
for i in range(count):
|
305
|
+
# Generate log entry
|
306
|
+
entry = generate_log_entry(i, style, error_rate)
|
307
|
+
|
308
|
+
# Send using Foundation's logger
|
309
|
+
try:
|
310
|
+
service_logger = get_logger(f"generated.{entry['service']}")
|
311
|
+
|
312
|
+
# Extract level and remove from entry
|
313
|
+
level = entry.pop("level", "info")
|
314
|
+
message = entry.pop("message")
|
315
|
+
|
316
|
+
# Log at appropriate level
|
317
|
+
getattr(service_logger, level)(message, **entry)
|
318
|
+
logs_sent += 1
|
319
|
+
|
320
|
+
except Exception as e:
|
321
|
+
logs_failed += 1
|
322
|
+
if "rate limit" in str(e).lower():
|
323
|
+
logs_rate_limited += 1
|
324
|
+
|
325
|
+
# Control rate
|
326
|
+
if rate > 0:
|
327
|
+
time.sleep(1.0 / rate)
|
328
|
+
|
329
|
+
# Print progress
|
330
|
+
if (i + 1) % max(1, count // 10) == 0:
|
331
|
+
progress = (i + 1) / count * 100
|
332
|
+
click.echo(f"Progress: {progress:.0f}% ({i + 1}/{count})")
|
333
|
+
|
334
|
+
except KeyboardInterrupt:
|
335
|
+
click.echo("\n\nā Generation interrupted by user")
|
336
|
+
|
337
|
+
finally:
|
338
|
+
# Print final statistics
|
339
|
+
total_time = time.time() - start_time
|
340
|
+
actual_rate = logs_sent / total_time if total_time > 0 else 0
|
341
|
+
|
342
|
+
click.echo("\nš Generation complete:")
|
343
|
+
click.echo(f" Total sent: {logs_sent} logs")
|
344
|
+
click.echo(f" Total failed: {logs_failed} logs")
|
345
|
+
if enable_rate_limit:
|
346
|
+
click.echo(f" ā ļø Rate limited: {logs_rate_limited} logs")
|
347
|
+
click.echo(f" Time: {total_time:.2f}s")
|
348
|
+
click.echo(f" Target rate: {rate} logs/second")
|
349
|
+
click.echo(f" Actual rate: {actual_rate:.1f} logs/second")
|
350
|
+
|
351
|
+
|
352
|
+
if not _HAS_CLICK:
|
353
|
+
|
354
|
+
def generate_logs_command(*args, **kwargs):
|
355
|
+
raise ImportError(
|
356
|
+
"Click is required for CLI commands. Install with: pip install click"
|
357
|
+
)
|