Add CI/CD pipeline, logging enhancements, and release management
Lint Code / lint (push) Failing after 2m2s
CI/CD Pipeline / lint (push) Successful in 0s
Run Tests / test (3.12) (push) Successful in 54s
CI/CD Pipeline / build-and-release (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / notify (push) Has been cancelled
Run Tests / test (3.13) (push) Has been cancelled

- Create a GitHub Actions workflow for testing with Python 3.12 and 3.13.
- Update Makefile to include release management commands and pipeline checks.
- Document the CI/CD pipeline structure and usage in PIPELINE.md.
- Add structlog for structured logging and enhance logging utilities.
- Implement release management script for automated versioning and tagging.
- Modify logging configuration to support structured logging and improved formatting.
- Update dependencies in pyproject.toml and poetry.lock to include structlog.
- Enhance access logging in server and middleware to include structured data.
This commit is contained in:
Илья Глазунов
2025-09-03 00:13:21 +03:00
parent ff093b020f
commit 537b783726
16 changed files with 1054 additions and 241 deletions
+26 -10
View File
@@ -48,7 +48,15 @@ class PyServeMiddleware:
status_code = response.status_code
process_time = round((time.time() - start_time) * 1000, 2)
self.access_logger.info(f"{client_ip} - {method} {path} - {status_code} - {process_time}ms")
self.access_logger.info(
"HTTP request",
client_ip=client_ip,
method=method,
path=path,
status_code=status_code,
process_time_ms=process_time,
user_agent=request.headers.get("user-agent", "")
)
await response(scope, receive, send)
@@ -64,7 +72,7 @@ class PyServeServer:
def _setup_logging(self) -> None:
self.config.setup_logging()
logger.info("PyServe server initialized")
logger.info("PyServe server initialized", version=__version__)
def _load_extensions(self) -> None:
for ext_config in self.config.extensions:
@@ -106,7 +114,8 @@ class PyServeServer:
ext_metrics = getattr(extension, 'get_metrics')()
metrics.update(ext_metrics)
except Exception as e:
logger.error(f"Error getting metrics from {type(extension).__name__}: {e}")
logger.error("Error getting metrics from extension",
extension=type(extension).__name__, error=str(e))
import json
return Response(
@@ -122,11 +131,11 @@ class PyServeServer:
return None
if not Path(self.config.ssl.cert_file).exists():
logger.error(f"SSL certificate not found: {self.config.ssl.cert_file}")
logger.error("SSL certificate not found", cert_file=self.config.ssl.cert_file)
return None
if not Path(self.config.ssl.key_file).exists():
logger.error(f"SSL key not found: {self.config.ssl.key_file}")
logger.error("SSL key not found", key_file=self.config.ssl.key_file)
return None
try:
@@ -138,7 +147,7 @@ class PyServeServer:
logger.info("SSL context created successfully")
return context
except Exception as e:
logger.error(f"Error creating SSL context: {e}")
logger.error("Error creating SSL context", error=str(e), exc_info=True)
return None
def run(self) -> None:
@@ -167,7 +176,12 @@ class PyServeServer:
else:
protocol = "http"
logger.info(f"Starting PyServe server at {protocol}://{self.config.server.host}:{self.config.server.port}")
logger.info(
"Starting PyServe server",
protocol=protocol,
host=self.config.server.host,
port=self.config.server.port
)
try:
assert self.app is not None, "App not initialized"
@@ -175,7 +189,7 @@ class PyServeServer:
except KeyboardInterrupt:
logger.info("Received shutdown signal")
except Exception as e:
logger.error(f"Error starting server: {e}")
logger.error("Error starting server", error=str(e), exc_info=True)
finally:
self.shutdown()
@@ -193,6 +207,7 @@ class PyServeServer:
log_level="critical",
access_log=False,
use_colors=False,
backlog=self.config.server.backlog if self.config.server.backlog else 2048,
)
server = uvicorn.Server(config)
@@ -215,7 +230,7 @@ class PyServeServer:
for directory in directories:
Path(directory).mkdir(parents=True, exist_ok=True)
logger.debug(f"Created/checked directory: {directory}")
logger.debug("Created/checked directory", directory=directory)
def shutdown(self) -> None:
logger.info("Shutting down PyServe server")
@@ -238,7 +253,8 @@ class PyServeServer:
ext_metrics = getattr(extension, 'get_metrics')()
metrics.update(ext_metrics)
except Exception as e:
logger.error(f"Error getting metrics from {type(extension).__name__}: {e}")
logger.error("Error getting metrics from extension",
extension=type(extension).__name__, error=str(e))
return metrics