bustapi 0.1.0__cp311-cp311-win_amd64.whl → 0.1.5__cp311-cp311-win_amd64.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 bustapi might be problematic. Click here for more details.
- bustapi/__init__.py +10 -5
- bustapi/app.py +338 -41
- bustapi/bustapi_core.cp311-win_amd64.pyd +0 -0
- bustapi/helpers.py +28 -8
- bustapi/logging.py +468 -0
- bustapi/openapi/__init__.py +33 -0
- bustapi/openapi/const.py +3 -0
- bustapi/openapi/docs.py +269 -0
- bustapi/openapi/models.py +128 -0
- bustapi/openapi/utils.py +158 -0
- bustapi/templating.py +30 -0
- bustapi/testing.py +2 -1
- bustapi-0.1.5.dist-info/METADATA +324 -0
- bustapi-0.1.5.dist-info/RECORD +23 -0
- {bustapi-0.1.0.dist-info → bustapi-0.1.5.dist-info}/licenses/LICENSE +1 -1
- bustapi-0.1.0.dist-info/METADATA +0 -233
- bustapi-0.1.0.dist-info/RECORD +0 -16
- {bustapi-0.1.0.dist-info → bustapi-0.1.5.dist-info}/WHEEL +0 -0
- {bustapi-0.1.0.dist-info → bustapi-0.1.5.dist-info}/entry_points.txt +0 -0
bustapi/logging.py
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BustAPI Smart Colorful Logging
|
|
3
|
+
|
|
4
|
+
FastAPI-style colorful logging with enhanced features for BustAPI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import colorama
|
|
14
|
+
from colorama import Back, Fore, Style
|
|
15
|
+
|
|
16
|
+
colorama.init(autoreset=True)
|
|
17
|
+
HAS_COLORAMA = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
# Fallback without colors
|
|
20
|
+
class _MockColor:
|
|
21
|
+
def __getattr__(self, name):
|
|
22
|
+
return ""
|
|
23
|
+
|
|
24
|
+
Fore = Back = Style = _MockColor()
|
|
25
|
+
HAS_COLORAMA = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ColoredFormatter(logging.Formatter):
|
|
29
|
+
"""Colorful log formatter similar to FastAPI."""
|
|
30
|
+
|
|
31
|
+
# Color mapping for different log levels
|
|
32
|
+
COLORS = {
|
|
33
|
+
"DEBUG": Fore.CYAN,
|
|
34
|
+
"INFO": Fore.GREEN,
|
|
35
|
+
"WARNING": Fore.YELLOW,
|
|
36
|
+
"ERROR": Fore.RED,
|
|
37
|
+
"CRITICAL": Fore.RED + Style.BRIGHT,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# HTTP status code colors
|
|
41
|
+
STATUS_COLORS = {
|
|
42
|
+
"1": Fore.CYAN, # 1xx Informational
|
|
43
|
+
"2": Fore.GREEN, # 2xx Success
|
|
44
|
+
"3": Fore.YELLOW, # 3xx Redirection
|
|
45
|
+
"4": Fore.RED, # 4xx Client Error
|
|
46
|
+
"5": Fore.MAGENTA, # 5xx Server Error
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
def __init__(self, use_colors: bool = True):
|
|
50
|
+
super().__init__()
|
|
51
|
+
self.use_colors = use_colors and HAS_COLORAMA
|
|
52
|
+
|
|
53
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
54
|
+
if not self.use_colors:
|
|
55
|
+
return self._format_plain(record)
|
|
56
|
+
|
|
57
|
+
# Get color for log level
|
|
58
|
+
level_color = self.COLORS.get(record.levelname, "")
|
|
59
|
+
|
|
60
|
+
# Format timestamp
|
|
61
|
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(record.created))
|
|
62
|
+
timestamp_colored = f"{Fore.BLUE}{timestamp}{Style.RESET_ALL}"
|
|
63
|
+
|
|
64
|
+
# Format log level
|
|
65
|
+
level_colored = f"{level_color}{record.levelname:8}{Style.RESET_ALL}"
|
|
66
|
+
|
|
67
|
+
# Format logger name
|
|
68
|
+
logger_name = record.name
|
|
69
|
+
if logger_name.startswith("bustapi"):
|
|
70
|
+
logger_name_colored = f"{Fore.MAGENTA}{logger_name}{Style.RESET_ALL}"
|
|
71
|
+
else:
|
|
72
|
+
logger_name_colored = f"{Fore.CYAN}{logger_name}{Style.RESET_ALL}"
|
|
73
|
+
|
|
74
|
+
# Format message
|
|
75
|
+
message = record.getMessage()
|
|
76
|
+
|
|
77
|
+
# Special formatting for HTTP requests
|
|
78
|
+
if hasattr(record, "method") and hasattr(record, "path"):
|
|
79
|
+
method = record.method
|
|
80
|
+
path = record.path
|
|
81
|
+
status_code = getattr(record, "status_code", "200")
|
|
82
|
+
duration_formatted = getattr(record, "duration_formatted", "N/A")
|
|
83
|
+
error = getattr(record, "error", None)
|
|
84
|
+
|
|
85
|
+
# Color method
|
|
86
|
+
method_colors = {
|
|
87
|
+
"GET": Fore.BLUE,
|
|
88
|
+
"POST": Fore.GREEN,
|
|
89
|
+
"PUT": Fore.YELLOW,
|
|
90
|
+
"DELETE": Fore.RED,
|
|
91
|
+
"PATCH": Fore.CYAN,
|
|
92
|
+
"HEAD": Fore.MAGENTA,
|
|
93
|
+
"OPTIONS": Fore.WHITE,
|
|
94
|
+
}
|
|
95
|
+
method_colored = f"{method_colors.get(method, '')}{method}{Style.RESET_ALL}"
|
|
96
|
+
|
|
97
|
+
# Color status code
|
|
98
|
+
status_first_digit = str(status_code)[0] if status_code else "2"
|
|
99
|
+
status_color = self.STATUS_COLORS.get(status_first_digit, Fore.WHITE)
|
|
100
|
+
status_colored = f"{status_color}{status_code}{Style.RESET_ALL}"
|
|
101
|
+
|
|
102
|
+
# Color path
|
|
103
|
+
path_colored = f"{Fore.WHITE}{path}{Style.RESET_ALL}"
|
|
104
|
+
|
|
105
|
+
# Color duration based on time
|
|
106
|
+
duration_color = Fore.GREEN # Default fast
|
|
107
|
+
if hasattr(record, "duration") and record.duration:
|
|
108
|
+
duration = record.duration
|
|
109
|
+
if duration >= 1.0:
|
|
110
|
+
duration_color = Fore.RED # >= 1s = Red (slow)
|
|
111
|
+
elif duration >= 0.5:
|
|
112
|
+
duration_color = Fore.YELLOW # >= 500ms = Yellow (medium)
|
|
113
|
+
elif duration >= 0.1:
|
|
114
|
+
duration_color = Fore.CYAN # >= 100ms = Cyan (ok)
|
|
115
|
+
else:
|
|
116
|
+
duration_color = Fore.GREEN # < 100ms = Green (fast)
|
|
117
|
+
|
|
118
|
+
duration_colored = f"{duration_color}{duration_formatted}{Style.RESET_ALL}"
|
|
119
|
+
|
|
120
|
+
# Build message
|
|
121
|
+
message = f"{method_colored} {path_colored} - {status_colored} - {duration_colored}"
|
|
122
|
+
|
|
123
|
+
# Add error if present
|
|
124
|
+
if error:
|
|
125
|
+
error_colored = f"{Fore.RED}ERROR: {error}{Style.RESET_ALL}"
|
|
126
|
+
message += f" - {error_colored}"
|
|
127
|
+
|
|
128
|
+
# Special formatting for startup messages
|
|
129
|
+
elif "starting" in message.lower() or "listening" in message.lower():
|
|
130
|
+
message = f"{Fore.GREEN}{Style.BRIGHT}{message}{Style.RESET_ALL}"
|
|
131
|
+
|
|
132
|
+
# Special formatting for error messages
|
|
133
|
+
elif record.levelname in ["ERROR", "CRITICAL"]:
|
|
134
|
+
message = f"{Fore.RED}{message}{Style.RESET_ALL}"
|
|
135
|
+
|
|
136
|
+
# Build final log line
|
|
137
|
+
log_line = (
|
|
138
|
+
f"{timestamp_colored} {level_colored} {logger_name_colored:20} {message}"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Add exception info if present
|
|
142
|
+
if record.exc_info:
|
|
143
|
+
log_line += f"\n{self.formatException(record.exc_info)}"
|
|
144
|
+
|
|
145
|
+
return log_line
|
|
146
|
+
|
|
147
|
+
def _format_plain(self, record: logging.LogRecord) -> str:
|
|
148
|
+
"""Plain formatting without colors."""
|
|
149
|
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(record.created))
|
|
150
|
+
level = record.levelname.ljust(8)
|
|
151
|
+
logger_name = record.name.ljust(20)
|
|
152
|
+
message = record.getMessage()
|
|
153
|
+
|
|
154
|
+
log_line = f"{timestamp} {level} {logger_name} {message}"
|
|
155
|
+
|
|
156
|
+
if record.exc_info:
|
|
157
|
+
log_line += f"\n{self.formatException(record.exc_info)}"
|
|
158
|
+
|
|
159
|
+
return log_line
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class BustAPILogger:
|
|
163
|
+
"""BustAPI logger with smart colorful output."""
|
|
164
|
+
|
|
165
|
+
def __init__(self, name: str = "bustapi", use_colors: bool = True):
|
|
166
|
+
self.logger = logging.getLogger(name)
|
|
167
|
+
self.use_colors = use_colors
|
|
168
|
+
self._setup_logger()
|
|
169
|
+
|
|
170
|
+
def _setup_logger(self):
|
|
171
|
+
"""Setup logger with colored formatter."""
|
|
172
|
+
if self.logger.handlers:
|
|
173
|
+
return # Already configured
|
|
174
|
+
|
|
175
|
+
# Create console handler
|
|
176
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
177
|
+
|
|
178
|
+
# Set colored formatter
|
|
179
|
+
formatter = ColoredFormatter(use_colors=self.use_colors)
|
|
180
|
+
handler.setFormatter(formatter)
|
|
181
|
+
|
|
182
|
+
# Add handler to logger
|
|
183
|
+
self.logger.addHandler(handler)
|
|
184
|
+
self.logger.setLevel(logging.INFO)
|
|
185
|
+
self.logger.propagate = False
|
|
186
|
+
|
|
187
|
+
def info(self, message: str, **kwargs):
|
|
188
|
+
"""Log info message with optional extra data."""
|
|
189
|
+
self.logger.info(message, extra=kwargs)
|
|
190
|
+
|
|
191
|
+
def debug(self, message: str, **kwargs):
|
|
192
|
+
"""Log debug message with optional extra data."""
|
|
193
|
+
self.logger.debug(message, extra=kwargs)
|
|
194
|
+
|
|
195
|
+
def warning(self, message: str, **kwargs):
|
|
196
|
+
"""Log warning message with optional extra data."""
|
|
197
|
+
self.logger.warning(message, extra=kwargs)
|
|
198
|
+
|
|
199
|
+
def error(self, message: str, **kwargs):
|
|
200
|
+
"""Log error message with optional extra data."""
|
|
201
|
+
self.logger.error(message, extra=kwargs)
|
|
202
|
+
|
|
203
|
+
def critical(self, message: str, **kwargs):
|
|
204
|
+
"""Log critical message with optional extra data."""
|
|
205
|
+
self.logger.critical(message, extra=kwargs)
|
|
206
|
+
|
|
207
|
+
def log_request(
|
|
208
|
+
self,
|
|
209
|
+
method: str,
|
|
210
|
+
path: str,
|
|
211
|
+
status_code: int,
|
|
212
|
+
duration: Optional[float] = None,
|
|
213
|
+
error: Optional[str] = None,
|
|
214
|
+
**kwargs,
|
|
215
|
+
):
|
|
216
|
+
"""Log HTTP request with colored formatting and smart time units."""
|
|
217
|
+
# Format duration with appropriate time unit
|
|
218
|
+
duration_str = (
|
|
219
|
+
self._format_duration(duration) if duration is not None else "N/A"
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Create log message
|
|
223
|
+
message = f"{method} {path} - {status_code} - {duration_str}"
|
|
224
|
+
if error:
|
|
225
|
+
message += f" - ERROR: {error}"
|
|
226
|
+
|
|
227
|
+
# Choose log level based on status code and error
|
|
228
|
+
if error or (status_code >= 500):
|
|
229
|
+
log_level = "error"
|
|
230
|
+
elif status_code >= 400:
|
|
231
|
+
log_level = "warning"
|
|
232
|
+
else:
|
|
233
|
+
log_level = "info"
|
|
234
|
+
|
|
235
|
+
# Log with appropriate level
|
|
236
|
+
getattr(self, log_level)(
|
|
237
|
+
message,
|
|
238
|
+
extra={
|
|
239
|
+
"method": method,
|
|
240
|
+
"path": path,
|
|
241
|
+
"status_code": status_code,
|
|
242
|
+
"duration": duration,
|
|
243
|
+
"duration_formatted": duration_str,
|
|
244
|
+
"error": error,
|
|
245
|
+
**kwargs,
|
|
246
|
+
},
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def _format_duration(self, duration: float) -> str:
|
|
250
|
+
"""Format duration with appropriate time unit (s, ms, μs, ns)."""
|
|
251
|
+
if duration >= 1.0:
|
|
252
|
+
return f"{duration:.3f}s"
|
|
253
|
+
elif duration >= 0.001:
|
|
254
|
+
return f"{duration * 1000:.3f}ms"
|
|
255
|
+
elif duration >= 0.000001:
|
|
256
|
+
return f"{duration * 1000000:.3f}μs"
|
|
257
|
+
else:
|
|
258
|
+
return f"{duration * 1000000000:.3f}ns"
|
|
259
|
+
|
|
260
|
+
def log_startup(self, message: str, **kwargs):
|
|
261
|
+
"""Log startup message with special formatting."""
|
|
262
|
+
self.info(f"🚀 {message}", **kwargs)
|
|
263
|
+
|
|
264
|
+
def log_shutdown(self, message: str, **kwargs):
|
|
265
|
+
"""Log shutdown message with special formatting."""
|
|
266
|
+
self.info(f"🛑 {message}", **kwargs)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Global logger instance
|
|
270
|
+
logger = BustAPILogger()
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# Simple interface - just import logging and use these
|
|
274
|
+
def setup(level: str = "INFO", use_colors: bool = True):
|
|
275
|
+
"""Simple setup function."""
|
|
276
|
+
return setup_logging(level, use_colors)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def info(message: str, **kwargs):
|
|
280
|
+
"""Simple info logging."""
|
|
281
|
+
logger.info(message, **kwargs)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def debug(message: str, **kwargs):
|
|
285
|
+
"""Simple debug logging."""
|
|
286
|
+
logger.debug(message, **kwargs)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def warning(message: str, **kwargs):
|
|
290
|
+
"""Simple warning logging."""
|
|
291
|
+
logger.warning(message, **kwargs)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def error(message: str, **kwargs):
|
|
295
|
+
"""Simple error logging."""
|
|
296
|
+
logger.error(message, **kwargs)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def request(
|
|
300
|
+
method: str, path: str, status_code: int, duration: float = None, error: str = None
|
|
301
|
+
):
|
|
302
|
+
"""Simple request logging."""
|
|
303
|
+
logger.log_request(method, path, status_code, duration, error)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def setup_logging(
|
|
307
|
+
level: str = "INFO", use_colors: bool = True, logger_name: str = "bustapi"
|
|
308
|
+
) -> BustAPILogger:
|
|
309
|
+
"""
|
|
310
|
+
Setup BustAPI logging with colorful output.
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
314
|
+
use_colors: Whether to use colored output
|
|
315
|
+
logger_name: Name of the logger
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
Configured BustAPILogger instance
|
|
319
|
+
"""
|
|
320
|
+
# Create logger
|
|
321
|
+
bustapi_logger = BustAPILogger(name=logger_name, use_colors=use_colors)
|
|
322
|
+
|
|
323
|
+
# Set log level
|
|
324
|
+
log_level = getattr(logging, level.upper(), logging.INFO)
|
|
325
|
+
bustapi_logger.logger.setLevel(log_level)
|
|
326
|
+
|
|
327
|
+
return bustapi_logger
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def get_logger(name: str = "bustapi") -> BustAPILogger:
|
|
331
|
+
"""Get a BustAPI logger instance."""
|
|
332
|
+
return BustAPILogger(name=name)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# Convenience functions
|
|
336
|
+
def log_info(message: str, **kwargs):
|
|
337
|
+
"""Log info message using global logger."""
|
|
338
|
+
logger.info(message, **kwargs)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def log_debug(message: str, **kwargs):
|
|
342
|
+
"""Log debug message using global logger."""
|
|
343
|
+
logger.debug(message, **kwargs)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def log_warning(message: str, **kwargs):
|
|
347
|
+
"""Log warning message using global logger."""
|
|
348
|
+
logger.warning(message, **kwargs)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def log_error(message: str, **kwargs):
|
|
352
|
+
"""Log error message using global logger."""
|
|
353
|
+
logger.error(message, **kwargs)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def log_request(
|
|
357
|
+
method: str,
|
|
358
|
+
path: str,
|
|
359
|
+
status_code: int,
|
|
360
|
+
duration: Optional[float] = None,
|
|
361
|
+
error: Optional[str] = None,
|
|
362
|
+
**kwargs,
|
|
363
|
+
):
|
|
364
|
+
"""Log HTTP request using global logger."""
|
|
365
|
+
logger.log_request(method, path, status_code, duration, error, **kwargs)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def log_startup(message: str, **kwargs):
|
|
369
|
+
"""Log startup message using global logger."""
|
|
370
|
+
logger.log_startup(message, **kwargs)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def log_shutdown(message: str, **kwargs):
|
|
374
|
+
"""Log shutdown message using global logger."""
|
|
375
|
+
logger.log_shutdown(message, **kwargs)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def request_logging_middleware(app_logger: Optional[BustAPILogger] = None):
|
|
379
|
+
"""
|
|
380
|
+
Decorator to add automatic request logging to route handlers.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
app_logger: Optional logger instance to use
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
Decorator function
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
def decorator(func):
|
|
390
|
+
def wrapper(*args, **kwargs):
|
|
391
|
+
import time
|
|
392
|
+
|
|
393
|
+
from .request import request
|
|
394
|
+
|
|
395
|
+
# Use provided logger or global logger
|
|
396
|
+
req_logger = app_logger or logger
|
|
397
|
+
|
|
398
|
+
# Get request info
|
|
399
|
+
method = getattr(request, "method", "UNKNOWN")
|
|
400
|
+
path = getattr(request, "path", "/unknown")
|
|
401
|
+
|
|
402
|
+
# Start timing
|
|
403
|
+
start_time = time.perf_counter()
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
# Execute the route handler
|
|
407
|
+
result = func(*args, **kwargs)
|
|
408
|
+
|
|
409
|
+
# Calculate duration
|
|
410
|
+
duration = time.perf_counter() - start_time
|
|
411
|
+
|
|
412
|
+
# Determine status code
|
|
413
|
+
if isinstance(result, tuple):
|
|
414
|
+
status_code = result[1] if len(result) > 1 else 200
|
|
415
|
+
else:
|
|
416
|
+
status_code = 200
|
|
417
|
+
|
|
418
|
+
# Log successful request
|
|
419
|
+
req_logger.log_request(
|
|
420
|
+
method=method, path=path, status_code=status_code, duration=duration
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
return result
|
|
424
|
+
|
|
425
|
+
except Exception as e:
|
|
426
|
+
# Calculate duration for error case
|
|
427
|
+
duration = time.perf_counter() - start_time
|
|
428
|
+
|
|
429
|
+
# Log error request
|
|
430
|
+
req_logger.log_request(
|
|
431
|
+
method=method,
|
|
432
|
+
path=path,
|
|
433
|
+
status_code=500,
|
|
434
|
+
duration=duration,
|
|
435
|
+
error=str(e),
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# Re-raise the exception
|
|
439
|
+
raise
|
|
440
|
+
|
|
441
|
+
return wrapper
|
|
442
|
+
|
|
443
|
+
return decorator
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
# Example usage
|
|
447
|
+
if __name__ == "__main__":
|
|
448
|
+
# Demo the colorful logging
|
|
449
|
+
demo_logger = setup_logging(level="DEBUG", use_colors=True)
|
|
450
|
+
|
|
451
|
+
demo_logger.log_startup("BustAPI server starting...")
|
|
452
|
+
demo_logger.info("Server configuration loaded")
|
|
453
|
+
demo_logger.debug("Debug information")
|
|
454
|
+
demo_logger.warning("This is a warning")
|
|
455
|
+
demo_logger.error("This is an error")
|
|
456
|
+
|
|
457
|
+
# Demo HTTP request logging with different time units
|
|
458
|
+
demo_logger.log_request("GET", "/api/fast", 200, 0.000045) # 45μs - very fast
|
|
459
|
+
demo_logger.log_request("GET", "/api/quick", 200, 0.0023) # 2.3ms - quick
|
|
460
|
+
demo_logger.log_request("POST", "/api/users", 201, 0.123) # 123ms - ok
|
|
461
|
+
demo_logger.log_request("GET", "/api/medium", 200, 0.456) # 456ms - medium
|
|
462
|
+
demo_logger.log_request("GET", "/api/slow", 200, 1.234) # 1.234s - slow
|
|
463
|
+
demo_logger.log_request("GET", "/api/users/999", 404, 0.012) # 12ms - not found
|
|
464
|
+
demo_logger.log_request(
|
|
465
|
+
"POST", "/api/error", 500, 0.089, "Database connection failed"
|
|
466
|
+
) # With error
|
|
467
|
+
|
|
468
|
+
demo_logger.log_shutdown("BustAPI server shutting down...")
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BustAPI OpenAPI Module
|
|
3
|
+
|
|
4
|
+
OpenAPI 3.1.0 specification support for BustAPI.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .models import (
|
|
8
|
+
OpenAPIInfo,
|
|
9
|
+
OpenAPIOperation,
|
|
10
|
+
OpenAPIPathItem,
|
|
11
|
+
OpenAPIResponse,
|
|
12
|
+
OpenAPIServer,
|
|
13
|
+
OpenAPISpec,
|
|
14
|
+
)
|
|
15
|
+
from .utils import (
|
|
16
|
+
create_operation_from_handler,
|
|
17
|
+
create_path_item_from_route,
|
|
18
|
+
extract_route_info,
|
|
19
|
+
get_openapi_spec,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"OpenAPIInfo",
|
|
24
|
+
"OpenAPIServer",
|
|
25
|
+
"OpenAPIResponse",
|
|
26
|
+
"OpenAPIOperation",
|
|
27
|
+
"OpenAPIPathItem",
|
|
28
|
+
"OpenAPISpec",
|
|
29
|
+
"get_openapi_spec",
|
|
30
|
+
"create_path_item_from_route",
|
|
31
|
+
"create_operation_from_handler",
|
|
32
|
+
"extract_route_info",
|
|
33
|
+
]
|
bustapi/openapi/const.py
ADDED