forked from Shifty/pyserveX
feat: Enhance logging configuration with support for multiple log files and formats; improve CLI help output
This commit is contained in:
parent
79c8f127ca
commit
fb38853427
50
Makefile
50
Makefile
@ -7,11 +7,55 @@ PACKAGE_NAME = pyserve
|
|||||||
GREEN = \033[0;32m
|
GREEN = \033[0;32m
|
||||||
YELLOW = \033[1;33m
|
YELLOW = \033[1;33m
|
||||||
RED = \033[0;31m
|
RED = \033[0;31m
|
||||||
|
CYAN = \033[0;36m
|
||||||
NC = \033[0m
|
NC = \033[0m
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "$(GREEN)Commands:$(NC)"
|
@echo "$(GREEN)╔══════════════════════════════════════════════════════════════════════════════╗$(NC)"
|
||||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(YELLOW)%-20s$(NC) %s\n", $$1, $$2}'
|
@echo "$(GREEN)║$(NC) $(GREEN)Available Commands:$(NC) $(GREEN)║$(NC)"
|
||||||
|
@echo "$(GREEN)╠══════════════════════════════════════════════════════════════════════════════╣$(NC)"
|
||||||
|
@echo "$(YELLOW)Installation:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "install" "Installing dependencies"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "dev-install" "Installing development dependencies"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "dev-deps" "Installing additional tools"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "install-package" "Installing package locally"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Building:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "build" "Building package"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "clean" "Cleaning temporary files"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Testing:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "test" "Running tests"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "test-cov" "Running tests with coverage"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "lint" "Checking code with linters"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "format" "Formatting code"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "check" "Lint and test"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Running:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "run" "Starting server in development mode"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "run-prod" "Starting server in production mode"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Publishing:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "publish-test" "Publishing to Test PyPI"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "publish" "Publishing to PyPI"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Versioning:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "version" "Show current version"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "version-patch" "Increase patch version"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "version-minor" "Increase minor version"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "version-major" "Increase major version"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Environment:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "shell" "Opening Poetry shell"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "env-info" "Environment information"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "deps-update" "Updating dependencies"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "deps-show" "Dependency tree"
|
||||||
|
@echo ""
|
||||||
|
@echo "$(YELLOW)Configuration:$(NC)"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "config-create" "Creating config.yaml"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "watch-logs" "Last server logs"
|
||||||
|
@printf " $(YELLOW)%-20s$(CYAN) %s$(NC)\n" "init" "Project initialized for development"
|
||||||
|
@echo "$(GREEN)╚══════════════════════════════════════════════════════════════════════════════╝$(NC)"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@echo "$(GREEN)Installing dependencies...$(NC)"
|
@echo "$(GREEN)Installing dependencies...$(NC)"
|
||||||
@ -118,7 +162,7 @@ config-create:
|
|||||||
echo "$(YELLOW)config.yaml already exists$(NC)"; \
|
echo "$(YELLOW)config.yaml already exists$(NC)"; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
logs:
|
watch-logs:
|
||||||
@echo "$(GREEN)Last server logs:$(NC)"
|
@echo "$(GREEN)Last server logs:$(NC)"
|
||||||
@if [ -f logs/pyserve.log ]; then tail -f logs/pyserve.log; else echo "$(RED)Log file not found$(NC)"; fi
|
@if [ -f logs/pyserve.log ]; then tail -f logs/pyserve.log; else echo "$(RED)Log file not found$(NC)"; fi
|
||||||
|
|
||||||
|
|||||||
66
config.yaml
66
config.yaml
@ -18,29 +18,73 @@ ssl:
|
|||||||
logging:
|
logging:
|
||||||
level: DEBUG
|
level: DEBUG
|
||||||
console_output: true
|
console_output: true
|
||||||
log_file: ./logs/pyserve.log
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: true
|
||||||
|
show_module: true
|
||||||
|
timestamp_format: "%Y-%m-%d %H:%M:%S"
|
||||||
|
console:
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: true
|
||||||
|
show_module: true
|
||||||
|
level: DEBUG
|
||||||
|
files:
|
||||||
|
- path: "./logs/pyserve.log"
|
||||||
|
level: DEBUG
|
||||||
|
loggers: []
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
- path: "./logs/pyserve.json"
|
||||||
|
level: INFO
|
||||||
|
loggers: []
|
||||||
|
format:
|
||||||
|
type: json
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
- path: "./logs/server.log"
|
||||||
|
level: DEBUG
|
||||||
|
loggers: ["pyserve.server"]
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
- path: "./logs/access.log"
|
||||||
|
level: INFO
|
||||||
|
loggers: ["pyserve.access"]
|
||||||
|
max_bytes: 5242880 # 5MB
|
||||||
|
backup_count: 10
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: false
|
||||||
|
|
||||||
# НОВОЕ: Расширяемые модули
|
# NEW: Extendable modules
|
||||||
extensions:
|
extensions:
|
||||||
# Встроенное расширение для продвинутой маршрутизации
|
# Built-in extension for advanced routing
|
||||||
- type: routing
|
- type: routing
|
||||||
config:
|
config:
|
||||||
regex_locations:
|
regex_locations:
|
||||||
# API маршруты с захватом версии
|
# API routes with version capture
|
||||||
"~^/api/v(?P<version>\\d+)/":
|
"~^/api/v(?P<version>\\d+)/":
|
||||||
proxy_pass: "http://localhost:9001"
|
proxy_pass: "http://localhost:9001"
|
||||||
headers:
|
headers:
|
||||||
- "API-Version: {version}"
|
- "API-Version: {version}"
|
||||||
- "X-Forwarded-For: $remote_addr"
|
- "X-Forwarded-For: $remote_addr"
|
||||||
|
|
||||||
# Статические файлы с долгим кэшем
|
# Static files with long cache
|
||||||
"~*\\.(js|css|png|jpg|gif|ico|svg|woff2?)$":
|
"~*\\.(js|css|png|jpg|gif|ico|svg|woff2?)$":
|
||||||
root: "./static"
|
root: "./static"
|
||||||
cache_control: "public, max-age=31536000"
|
cache_control: "public, max-age=31536000"
|
||||||
headers:
|
headers:
|
||||||
- "Access-Control-Allow-Origin: *"
|
- "Access-Control-Allow-Origin: *"
|
||||||
|
|
||||||
# Exact match для health check
|
# Exact match for health check
|
||||||
"=/health":
|
"=/health":
|
||||||
return: "200 OK"
|
return: "200 OK"
|
||||||
content_type: "text/plain"
|
content_type: "text/plain"
|
||||||
@ -48,13 +92,13 @@ extensions:
|
|||||||
"=/":
|
"=/":
|
||||||
root: "./static"
|
root: "./static"
|
||||||
index_file: "index.html"
|
index_file: "index.html"
|
||||||
|
|
||||||
# SPA fallback для всех остальных маршрутов
|
# SPA fallback for all other routes
|
||||||
"__default__":
|
"__default__":
|
||||||
spa_fallback: true
|
spa_fallback: true
|
||||||
root: "./static"
|
root: "./static"
|
||||||
index_file: "docs.html"
|
index_file: "docs.html"
|
||||||
# Исключения для SPA (не попадают в fallback)
|
# Exceptions for SPA (do not hit fallback)
|
||||||
exclude_patterns:
|
exclude_patterns:
|
||||||
- "/api/"
|
- "/api/"
|
||||||
- "/admin/"
|
- "/admin/"
|
||||||
|
|||||||
131
examples/config.example.logging.yaml
Normal file
131
examples/config.example.logging.yaml
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# Example extended logging configuration for PyServe
|
||||||
|
# Support for multiple files and logger-based separation
|
||||||
|
|
||||||
|
logging:
|
||||||
|
# Main logging level (applies by default)
|
||||||
|
level: DEBUG # DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
|
|
||||||
|
# Enable/disable console output
|
||||||
|
console_output: true
|
||||||
|
|
||||||
|
# Global formatting settings (applied to console and files unless overridden)
|
||||||
|
format:
|
||||||
|
type: standard # standard or json
|
||||||
|
use_colors: true # Use colors (recommended false for files)
|
||||||
|
show_module: true # Show module name
|
||||||
|
timestamp_format: "%Y-%m-%d %H:%M:%S" # Timestamp format
|
||||||
|
|
||||||
|
# Specific settings for console output
|
||||||
|
console:
|
||||||
|
level: INFO # You can specify a separate level for the console
|
||||||
|
format:
|
||||||
|
type: standard # standard or json
|
||||||
|
use_colors: true # Colors in the console are usually helpful
|
||||||
|
show_module: true
|
||||||
|
timestamp_format: "%H:%M:%S" # Short timestamp format for console
|
||||||
|
|
||||||
|
# Logging file settings (you can specify multiple files)
|
||||||
|
files:
|
||||||
|
# Main log file - all logs
|
||||||
|
- path: "./logs/pyserve.log"
|
||||||
|
level: DEBUG
|
||||||
|
loggers: [] # Empty list means all loggers
|
||||||
|
max_bytes: 10485760 # 10MB (default)
|
||||||
|
backup_count: 5 # Number of backup files (default)
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
# JSON log for automated analysis
|
||||||
|
- path: "./logs/pyserve.json"
|
||||||
|
level: INFO
|
||||||
|
loggers: []
|
||||||
|
format:
|
||||||
|
type: json
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
# Separate file for server logs
|
||||||
|
- path: "./logs/server.log"
|
||||||
|
level: DEBUG
|
||||||
|
loggers: ["pyserve.server"] # Only logs from pyserve.server
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
# Separate file for access logs
|
||||||
|
- path: "./logs/access.log"
|
||||||
|
level: INFO
|
||||||
|
loggers: ["pyserve.access"] # Only access logs
|
||||||
|
max_bytes: 5242880 # 5MB
|
||||||
|
backup_count: 10
|
||||||
|
format:
|
||||||
|
type: standard
|
||||||
|
use_colors: false
|
||||||
|
show_module: false # For access logs module is not needed
|
||||||
|
|
||||||
|
# Separate file for error logs
|
||||||
|
- path: "./logs/errors.log"
|
||||||
|
level: ERROR
|
||||||
|
loggers: [] # All errors from all loggers
|
||||||
|
format:
|
||||||
|
type: json # JSON for easy error parsing
|
||||||
|
use_colors: false
|
||||||
|
show_module: true
|
||||||
|
|
||||||
|
# Examples of various configurations:
|
||||||
|
|
||||||
|
# 1. Minimal configuration (backward compatibility):
|
||||||
|
# logging:
|
||||||
|
# level: INFO
|
||||||
|
# console_output: true
|
||||||
|
# files:
|
||||||
|
# - path: "./logs/pyserve.log"
|
||||||
|
# level: INFO
|
||||||
|
# loggers: []
|
||||||
|
|
||||||
|
# 2. Only JSON logs:
|
||||||
|
# logging:
|
||||||
|
# level: INFO
|
||||||
|
# console_output: false
|
||||||
|
# files:
|
||||||
|
# - path: "./logs/pyserve.json"
|
||||||
|
# format:
|
||||||
|
# type: json
|
||||||
|
|
||||||
|
# 3. Separation by log type:
|
||||||
|
# logging:
|
||||||
|
# level: DEBUG
|
||||||
|
# console_output: true
|
||||||
|
# files:
|
||||||
|
# - path: "./logs/general.log"
|
||||||
|
# level: INFO
|
||||||
|
# loggers: []
|
||||||
|
# - path: "./logs/server.log"
|
||||||
|
# level: DEBUG
|
||||||
|
# loggers: ["pyserve.server", "pyserve.routing"]
|
||||||
|
# - path: "./logs/access.log"
|
||||||
|
# level: INFO
|
||||||
|
# loggers: ["pyserve.access"]
|
||||||
|
# - path: "./logs/errors.json"
|
||||||
|
# level: ERROR
|
||||||
|
# loggers: []
|
||||||
|
# format:
|
||||||
|
# type: json
|
||||||
|
|
||||||
|
# 4. Console without colors, different formats for files:
|
||||||
|
# logging:
|
||||||
|
# level: DEBUG
|
||||||
|
# console_output: true
|
||||||
|
# console:
|
||||||
|
# format:
|
||||||
|
# use_colors: false
|
||||||
|
# files:
|
||||||
|
# - path: "./logs/standard.log"
|
||||||
|
# format:
|
||||||
|
# type: standard
|
||||||
|
# - path: "./logs/structured.json"
|
||||||
|
# format:
|
||||||
|
# type: json
|
||||||
@ -1,9 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
PyServe - HTTP веб-сервер с функционалом nginx
|
PyServe - HTTP web server written on Python
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "0.6.0"
|
__version__ = "0.6.0"
|
||||||
__author__ = "Илья Глазунов"
|
__author__ = "Ilya Glazunov"
|
||||||
|
|
||||||
from .server import PyServeServer
|
from .server import PyServeServer
|
||||||
from .config import Config
|
from .config import Config
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from . import PyServeServer, Config, __version__
|
|||||||
def main() -> None:
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="PyServe - HTTP web server",
|
description="PyServe - HTTP web server",
|
||||||
prog="pyserve"
|
prog="pyserve",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-c", "--config",
|
"-c", "--config",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import os
|
import os
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List, cast
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
import logging
|
import logging
|
||||||
from .logging_utils import setup_logging
|
from .logging_utils import setup_logging
|
||||||
@ -28,11 +28,37 @@ class SSLConfig:
|
|||||||
key_file: str = "./ssl/key.pem"
|
key_file: str = "./ssl/key.pem"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LogFormatConfig:
|
||||||
|
type: str = "standard"
|
||||||
|
use_colors: bool = True
|
||||||
|
show_module: bool = True
|
||||||
|
timestamp_format: str = "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LogFileConfig:
|
||||||
|
path: str
|
||||||
|
level: str = "INFO"
|
||||||
|
format: LogFormatConfig = field(default_factory=LogFormatConfig)
|
||||||
|
loggers: List[str] = field(default_factory=list) # Список logger'ов для этого файла
|
||||||
|
max_bytes: int = 10 * 1024 * 1024 # 10MB
|
||||||
|
backup_count: int = 5
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LogHandlerConfig:
|
||||||
|
level: str = "INFO"
|
||||||
|
format: LogFormatConfig = field(default_factory=LogFormatConfig)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LoggingConfig:
|
class LoggingConfig:
|
||||||
level: str = "INFO"
|
level: str = "INFO"
|
||||||
console_output: bool = True
|
console_output: bool = True
|
||||||
log_file: str = "./logs/pyserve.log"
|
format: LogFormatConfig = field(default_factory=LogFormatConfig)
|
||||||
|
console: LogHandlerConfig = field(default_factory=LogHandlerConfig)
|
||||||
|
files: List[LogFileConfig] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -99,10 +125,85 @@ class Config:
|
|||||||
|
|
||||||
if 'logging' in data:
|
if 'logging' in data:
|
||||||
log_data = data['logging']
|
log_data = data['logging']
|
||||||
|
format_data = log_data.get('format', {})
|
||||||
|
global_format = LogFormatConfig(
|
||||||
|
type=format_data.get('type', 'standard'),
|
||||||
|
use_colors=format_data.get('use_colors', True),
|
||||||
|
show_module=format_data.get('show_module', True),
|
||||||
|
timestamp_format=format_data.get('timestamp_format', '%Y-%m-%d %H:%M:%S')
|
||||||
|
)
|
||||||
|
console_data = log_data.get('console', {})
|
||||||
|
console_format_data = console_data.get('format', {})
|
||||||
|
console_format = LogFormatConfig(
|
||||||
|
type=console_format_data.get('type', global_format.type),
|
||||||
|
use_colors=console_format_data.get('use_colors', global_format.use_colors),
|
||||||
|
show_module=console_format_data.get('show_module', global_format.show_module),
|
||||||
|
timestamp_format=console_format_data.get('timestamp_format', global_format.timestamp_format)
|
||||||
|
)
|
||||||
|
console_config = LogHandlerConfig(
|
||||||
|
level=console_data.get('level', log_data.get('level', 'INFO')),
|
||||||
|
format=console_format
|
||||||
|
)
|
||||||
|
files_config = []
|
||||||
|
if 'log_file' in log_data:
|
||||||
|
default_file_format = LogFormatConfig(
|
||||||
|
type=global_format.type,
|
||||||
|
use_colors=False,
|
||||||
|
show_module=global_format.show_module,
|
||||||
|
timestamp_format=global_format.timestamp_format
|
||||||
|
)
|
||||||
|
default_file = LogFileConfig(
|
||||||
|
path=log_data['log_file'],
|
||||||
|
level=log_data.get('level', 'INFO'),
|
||||||
|
format=default_file_format,
|
||||||
|
loggers=[], # Empty list means including all loggers
|
||||||
|
max_bytes=10 * 1024 * 1024,
|
||||||
|
backup_count=5
|
||||||
|
)
|
||||||
|
files_config.append(default_file)
|
||||||
|
|
||||||
|
if 'files' in log_data:
|
||||||
|
for file_data in log_data['files']:
|
||||||
|
file_format_data = file_data.get('format', {})
|
||||||
|
file_format = LogFormatConfig(
|
||||||
|
type=file_format_data.get('type', global_format.type),
|
||||||
|
use_colors=file_format_data.get('use_colors', False),
|
||||||
|
show_module=file_format_data.get('show_module', global_format.show_module),
|
||||||
|
timestamp_format=file_format_data.get('timestamp_format', global_format.timestamp_format)
|
||||||
|
)
|
||||||
|
file_config = LogFileConfig(
|
||||||
|
path=file_data.get('path', './logs/pyserve.log'),
|
||||||
|
level=file_data.get('level', log_data.get('level', 'INFO')),
|
||||||
|
format=file_format,
|
||||||
|
loggers=file_data.get('loggers', []),
|
||||||
|
max_bytes=file_data.get('max_bytes', 10 * 1024 * 1024),
|
||||||
|
backup_count=file_data.get('backup_count', 5)
|
||||||
|
)
|
||||||
|
files_config.append(file_config)
|
||||||
|
|
||||||
|
if not files_config:
|
||||||
|
default_file_format = LogFormatConfig(
|
||||||
|
type=global_format.type,
|
||||||
|
use_colors=False,
|
||||||
|
show_module=global_format.show_module,
|
||||||
|
timestamp_format=global_format.timestamp_format
|
||||||
|
)
|
||||||
|
default_file = LogFileConfig(
|
||||||
|
path='./logs/pyserve.log',
|
||||||
|
level=log_data.get('level', 'INFO'),
|
||||||
|
format=default_file_format,
|
||||||
|
loggers=[],
|
||||||
|
max_bytes=10 * 1024 * 1024,
|
||||||
|
backup_count=5
|
||||||
|
)
|
||||||
|
files_config.append(default_file)
|
||||||
|
|
||||||
config.logging = LoggingConfig(
|
config.logging = LoggingConfig(
|
||||||
level=log_data.get('level', config.logging.level),
|
level=log_data.get('level', 'INFO'),
|
||||||
console_output=log_data.get('console_output', config.logging.console_output),
|
console_output=log_data.get('console_output', True),
|
||||||
log_file=log_data.get('log_file', config.logging.log_file)
|
format=global_format,
|
||||||
|
console=console_config,
|
||||||
|
files=files_config
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'extensions' in data:
|
if 'extensions' in data:
|
||||||
@ -130,12 +231,34 @@ class Config:
|
|||||||
if not (1 <= self.server.port <= 65535):
|
if not (1 <= self.server.port <= 65535):
|
||||||
errors.append(f"Invalid port: {self.server.port}")
|
errors.append(f"Invalid port: {self.server.port}")
|
||||||
|
|
||||||
log_dir = os.path.dirname(self.logging.log_file)
|
valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
|
||||||
if log_dir and not os.path.exists(log_dir):
|
if self.logging.level.upper() not in valid_log_levels:
|
||||||
try:
|
errors.append(f"Invalid logging level: {self.logging.level}")
|
||||||
os.makedirs(log_dir, exist_ok=True)
|
|
||||||
except OSError as e:
|
if self.logging.console.level.upper() not in valid_log_levels:
|
||||||
errors.append(f"Unable to create log directory: {e}")
|
errors.append(f"Invalid console logging level: {self.logging.console.level}")
|
||||||
|
|
||||||
|
valid_format_types = ['standard', 'json']
|
||||||
|
|
||||||
|
if self.logging.format.type not in valid_format_types:
|
||||||
|
errors.append(f"Invalid logging format type: {self.logging.format.type}")
|
||||||
|
|
||||||
|
if self.logging.console.format.type not in valid_format_types:
|
||||||
|
errors.append(f"Invalid console format type: {self.logging.console.format.type}")
|
||||||
|
|
||||||
|
for i, file_config in enumerate(self.logging.files):
|
||||||
|
if file_config.level.upper() not in valid_log_levels:
|
||||||
|
errors.append(f"Invalid file[{i}] logging level: {file_config.level}")
|
||||||
|
|
||||||
|
if file_config.format.type not in valid_format_types:
|
||||||
|
errors.append(f"Invalid file[{i}] format type: {file_config.format.type}")
|
||||||
|
|
||||||
|
log_dir = os.path.dirname(file_config.path)
|
||||||
|
if log_dir and not os.path.exists(log_dir):
|
||||||
|
try:
|
||||||
|
os.makedirs(log_dir, exist_ok=True)
|
||||||
|
except OSError as e:
|
||||||
|
errors.append(f"Unable to create log directory for {file_config.path}: {e}")
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
for error in errors:
|
for error in errors:
|
||||||
@ -148,6 +271,38 @@ class Config:
|
|||||||
config_dict = {
|
config_dict = {
|
||||||
'level': self.logging.level,
|
'level': self.logging.level,
|
||||||
'console_output': self.logging.console_output,
|
'console_output': self.logging.console_output,
|
||||||
'log_file': self.logging.log_file
|
'format': {
|
||||||
|
'type': self.logging.format.type,
|
||||||
|
'use_colors': self.logging.format.use_colors,
|
||||||
|
'show_module': self.logging.format.show_module,
|
||||||
|
'timestamp_format': self.logging.format.timestamp_format
|
||||||
|
},
|
||||||
|
'console': {
|
||||||
|
'level': self.logging.console.level,
|
||||||
|
'format': {
|
||||||
|
'type': self.logging.console.format.type,
|
||||||
|
'use_colors': self.logging.console.format.use_colors,
|
||||||
|
'show_module': self.logging.console.format.show_module,
|
||||||
|
'timestamp_format': self.logging.console.format.timestamp_format
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'files': []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for file_config in self.logging.files:
|
||||||
|
file_dict = {
|
||||||
|
'path': file_config.path,
|
||||||
|
'level': file_config.level,
|
||||||
|
'loggers': file_config.loggers,
|
||||||
|
'max_bytes': file_config.max_bytes,
|
||||||
|
'backup_count': file_config.backup_count,
|
||||||
|
'format': {
|
||||||
|
'type': file_config.format.type,
|
||||||
|
'use_colors': file_config.format.use_colors,
|
||||||
|
'show_module': file_config.format.show_module,
|
||||||
|
'timestamp_format': file_config.format.timestamp_format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cast(List[Dict[str, Any]], config_dict['files']).append(file_dict)
|
||||||
|
|
||||||
setup_logging(config_dict)
|
setup_logging(config_dict)
|
||||||
|
|||||||
@ -152,7 +152,7 @@ class ExtensionManager:
|
|||||||
|
|
||||||
def load_extension(self, extension_type: str, config: Dict[str, Any]) -> None:
|
def load_extension(self, extension_type: str, config: Dict[str, Any]) -> None:
|
||||||
if extension_type not in self.extension_registry:
|
if extension_type not in self.extension_registry:
|
||||||
logger.error(f"Неизвестный тип расширения: {extension_type}")
|
logger.error(f"Unknown extension type: {extension_type}")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -160,9 +160,9 @@ class ExtensionManager:
|
|||||||
extension = extension_class(config)
|
extension = extension_class(config)
|
||||||
extension.initialize()
|
extension.initialize()
|
||||||
self.extensions.append(extension)
|
self.extensions.append(extension)
|
||||||
logger.info(f"Загружено расширение: {extension_type}")
|
logger.info(f"Loaded extension: {extension_type}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка загрузки расширения {extension_type}: {e}")
|
logger.error(f"Error loading extension {extension_type}: {e}")
|
||||||
|
|
||||||
async def process_request(self, request: Request) -> Optional[Response]:
|
async def process_request(self, request: Request) -> Optional[Response]:
|
||||||
for extension in self.extensions:
|
for extension in self.extensions:
|
||||||
@ -174,7 +174,7 @@ class ExtensionManager:
|
|||||||
if response is not None:
|
if response is not None:
|
||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в расширении {type(extension).__name__}: {e}")
|
logger.error(f"Error in extension {type(extension).__name__}: {e}")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ class ExtensionManager:
|
|||||||
try:
|
try:
|
||||||
response = await extension.process_response(request, response)
|
response = await extension.process_response(request, response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка в расширении {type(extension).__name__}: {e}")
|
logger.error(f"Error in extension {type(extension).__name__}: {e}")
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@ -195,6 +195,6 @@ class ExtensionManager:
|
|||||||
try:
|
try:
|
||||||
extension.cleanup()
|
extension.cleanup()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Ошибка при очистке расширения {type(extension).__name__}: {e}")
|
logger.error(f"Error cleaning up extension {type(extension).__name__}: {e}")
|
||||||
|
|
||||||
self.extensions.clear()
|
self.extensions.clear()
|
||||||
|
|||||||
@ -2,12 +2,30 @@ import logging
|
|||||||
import logging.handlers
|
import logging.handlers
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any, List
|
from typing import Dict, Any, List
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
|
|
||||||
|
class LoggerFilter(logging.Filter):
|
||||||
|
def __init__(self, logger_names: List[str]):
|
||||||
|
super().__init__()
|
||||||
|
self.logger_names = logger_names
|
||||||
|
self.accept_all = len(logger_names) == 0
|
||||||
|
|
||||||
|
def filter(self, record: logging.LogRecord) -> bool:
|
||||||
|
if self.accept_all:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for logger_name in self.logger_names:
|
||||||
|
if record.name == logger_name or record.name.startswith(logger_name + '.'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class UvicornLogFilter(logging.Filter):
|
class UvicornLogFilter(logging.Filter):
|
||||||
def filter(self, record: logging.LogRecord) -> bool:
|
def filter(self, record: logging.LogRecord) -> bool:
|
||||||
if hasattr(record, 'name') and 'uvicorn.access' in record.name:
|
if hasattr(record, 'name') and 'uvicorn.access' in record.name:
|
||||||
@ -36,10 +54,12 @@ class PyServeFormatter(logging.Formatter):
|
|||||||
'RESET': '\033[0m' # Reset
|
'RESET': '\033[0m' # Reset
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, use_colors: bool = True, show_module: bool = True, *args: Any, **kwargs: Any):
|
def __init__(self, use_colors: bool = True, show_module: bool = True,
|
||||||
|
timestamp_format: str = "%Y-%m-%d %H:%M:%S", *args: Any, **kwargs: Any):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.use_colors = use_colors and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty()
|
self.use_colors = use_colors and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty()
|
||||||
self.show_module = show_module
|
self.show_module = show_module
|
||||||
|
self.timestamp_format = timestamp_format
|
||||||
|
|
||||||
def format(self, record: logging.LogRecord) -> str:
|
def format(self, record: logging.LogRecord) -> str:
|
||||||
if self.use_colors:
|
if self.use_colors:
|
||||||
@ -59,6 +79,37 @@ class PyServeFormatter(logging.Formatter):
|
|||||||
return super().format(record)
|
return super().format(record)
|
||||||
|
|
||||||
|
|
||||||
|
class PyServeJSONFormatter(logging.Formatter):
|
||||||
|
def __init__(self, timestamp_format: str = "%Y-%m-%d %H:%M:%S", *args: Any, **kwargs: Any):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.timestamp_format = timestamp_format
|
||||||
|
|
||||||
|
def format(self, record: logging.LogRecord) -> str:
|
||||||
|
log_entry = {
|
||||||
|
'timestamp': time.strftime(self.timestamp_format, time.localtime(record.created)),
|
||||||
|
'level': record.levelname,
|
||||||
|
'logger': record.name,
|
||||||
|
'message': record.getMessage(),
|
||||||
|
'module': record.module,
|
||||||
|
'function': record.funcName,
|
||||||
|
'line': record.lineno,
|
||||||
|
'thread': record.thread,
|
||||||
|
'thread_name': record.threadName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.exc_info:
|
||||||
|
log_entry['exception'] = self.formatException(record.exc_info)
|
||||||
|
|
||||||
|
for key, value in record.__dict__.items():
|
||||||
|
if key not in ['name', 'msg', 'args', 'levelname', 'levelno', 'pathname',
|
||||||
|
'filename', 'module', 'lineno', 'funcName', 'created',
|
||||||
|
'msecs', 'relativeCreated', 'thread', 'threadName',
|
||||||
|
'processName', 'process', 'getMessage', 'exc_info', 'exc_text', 'stack_info']:
|
||||||
|
log_entry[key] = value
|
||||||
|
|
||||||
|
return json.dumps(log_entry, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
|
|
||||||
class AccessLogHandler(logging.Handler):
|
class AccessLogHandler(logging.Handler):
|
||||||
def __init__(self, logger_name: str = 'pyserve.access'):
|
def __init__(self, logger_name: str = 'pyserve.access'):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -75,68 +126,154 @@ class PyServeLogManager:
|
|||||||
self.loggers: Dict[str, logging.Logger] = {}
|
self.loggers: Dict[str, logging.Logger] = {}
|
||||||
self.original_handlers: Dict[str, List[logging.Handler]] = {}
|
self.original_handlers: Dict[str, List[logging.Handler]] = {}
|
||||||
|
|
||||||
|
def _create_formatter(self, format_config: Dict[str, Any]) -> logging.Formatter:
|
||||||
|
format_type = format_config.get('type', 'standard').lower()
|
||||||
|
use_colors = format_config.get('use_colors', True)
|
||||||
|
show_module = format_config.get('show_module', True)
|
||||||
|
timestamp_format = format_config.get('timestamp_format', '%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
if format_type == 'json':
|
||||||
|
return PyServeJSONFormatter(timestamp_format=timestamp_format)
|
||||||
|
else:
|
||||||
|
if format_type == 'json':
|
||||||
|
fmt = None
|
||||||
|
else:
|
||||||
|
fmt = '%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
|
||||||
|
|
||||||
|
return PyServeFormatter(
|
||||||
|
use_colors=use_colors,
|
||||||
|
show_module=show_module,
|
||||||
|
timestamp_format=timestamp_format,
|
||||||
|
fmt=fmt
|
||||||
|
)
|
||||||
|
|
||||||
def setup_logging(self, config: Dict[str, Any]) -> None:
|
def setup_logging(self, config: Dict[str, Any]) -> None:
|
||||||
if self.configured:
|
if self.configured:
|
||||||
return
|
return
|
||||||
|
|
||||||
level = config.get('level', 'INFO').upper()
|
if 'format' not in config and 'console' not in config and 'files' not in config:
|
||||||
|
level = config.get('level', 'INFO').upper()
|
||||||
|
console_output = config.get('console_output', True)
|
||||||
|
log_file = config.get('log_file', './logs/pyserve.log')
|
||||||
|
config = {
|
||||||
|
'level': level,
|
||||||
|
'console_output': console_output,
|
||||||
|
'format': {
|
||||||
|
'type': 'standard',
|
||||||
|
'use_colors': True,
|
||||||
|
'show_module': True,
|
||||||
|
'timestamp_format': '%Y-%m-%d %H:%M:%S'
|
||||||
|
},
|
||||||
|
'files': [{
|
||||||
|
'path': log_file,
|
||||||
|
'level': level,
|
||||||
|
'loggers': [],
|
||||||
|
'max_bytes': 10 * 1024 * 1024,
|
||||||
|
'backup_count': 5,
|
||||||
|
'format': {
|
||||||
|
'type': 'standard',
|
||||||
|
'use_colors': False,
|
||||||
|
'show_module': True,
|
||||||
|
'timestamp_format': '%Y-%m-%d %H:%M:%S'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
main_level = config.get('level', 'INFO').upper()
|
||||||
console_output = config.get('console_output', True)
|
console_output = config.get('console_output', True)
|
||||||
log_file = config.get('log_file', './logs/pyserve.log')
|
|
||||||
|
global_format = config.get('format', {})
|
||||||
|
console_config = config.get('console', {})
|
||||||
|
files_config = config.get('files', [])
|
||||||
|
|
||||||
|
console_format = {**global_format, **console_config.get('format', {})}
|
||||||
|
console_level = console_config.get('level', main_level)
|
||||||
|
|
||||||
self._save_original_handlers()
|
self._save_original_handlers()
|
||||||
self._clear_all_handlers()
|
self._clear_all_handlers()
|
||||||
|
|
||||||
root_logger = logging.getLogger()
|
root_logger = logging.getLogger()
|
||||||
root_logger.setLevel(logging.DEBUG)
|
root_logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
detailed_formatter = PyServeFormatter(
|
|
||||||
use_colors=False,
|
|
||||||
show_module=True,
|
|
||||||
fmt='%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
|
|
||||||
)
|
|
||||||
|
|
||||||
console_formatter = PyServeFormatter(
|
|
||||||
use_colors=True,
|
|
||||||
show_module=True,
|
|
||||||
fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
||||||
)
|
|
||||||
|
|
||||||
if console_output:
|
if console_output:
|
||||||
console_handler = logging.StreamHandler(sys.stdout)
|
console_handler = logging.StreamHandler(sys.stdout)
|
||||||
console_handler.setLevel(getattr(logging, level))
|
console_handler.setLevel(getattr(logging, console_level))
|
||||||
|
|
||||||
|
if console_format.get('type') == 'json':
|
||||||
|
console_formatter = self._create_formatter(console_format)
|
||||||
|
else:
|
||||||
|
console_formatter = PyServeFormatter(
|
||||||
|
use_colors=console_format.get('use_colors', True),
|
||||||
|
show_module=console_format.get('show_module', True),
|
||||||
|
timestamp_format=console_format.get('timestamp_format', '%Y-%m-%d %H:%M:%S'),
|
||||||
|
fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
console_handler.setFormatter(console_formatter)
|
console_handler.setFormatter(console_formatter)
|
||||||
|
|
||||||
console_handler.addFilter(UvicornLogFilter())
|
console_handler.addFilter(UvicornLogFilter())
|
||||||
|
|
||||||
root_logger.addHandler(console_handler)
|
root_logger.addHandler(console_handler)
|
||||||
self.handlers['console'] = console_handler
|
self.handlers['console'] = console_handler
|
||||||
|
|
||||||
if log_file:
|
for i, file_config in enumerate(files_config):
|
||||||
self._ensure_log_directory(log_file)
|
file_path = file_config.get('path', './logs/pyserve.log')
|
||||||
|
file_level = file_config.get('level', main_level)
|
||||||
|
file_loggers = file_config.get('loggers', [])
|
||||||
|
max_bytes = file_config.get('max_bytes', 10 * 1024 * 1024)
|
||||||
|
backup_count = file_config.get('backup_count', 5)
|
||||||
|
file_format = {**global_format, **file_config.get('format', {})}
|
||||||
|
|
||||||
|
self._ensure_log_directory(file_path)
|
||||||
|
|
||||||
file_handler = logging.handlers.RotatingFileHandler(
|
file_handler = logging.handlers.RotatingFileHandler(
|
||||||
log_file,
|
file_path,
|
||||||
maxBytes=10 * 1024 * 1024, # 10MB
|
maxBytes=max_bytes,
|
||||||
backupCount=5,
|
backupCount=backup_count,
|
||||||
encoding='utf-8'
|
encoding='utf-8'
|
||||||
)
|
)
|
||||||
file_handler.setLevel(logging.DEBUG)
|
file_handler.setLevel(getattr(logging, file_level))
|
||||||
file_handler.setFormatter(detailed_formatter)
|
|
||||||
|
if file_format.get('type') == 'json':
|
||||||
|
file_formatter = self._create_formatter(file_format)
|
||||||
|
else:
|
||||||
|
file_formatter = PyServeFormatter(
|
||||||
|
use_colors=file_format.get('use_colors', False),
|
||||||
|
show_module=file_format.get('show_module', True),
|
||||||
|
timestamp_format=file_format.get('timestamp_format', '%Y-%m-%d %H:%M:%S'),
|
||||||
|
fmt='%(asctime)s - %(name)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s'
|
||||||
|
)
|
||||||
|
|
||||||
|
file_handler.setFormatter(file_formatter)
|
||||||
file_handler.addFilter(UvicornLogFilter())
|
file_handler.addFilter(UvicornLogFilter())
|
||||||
|
if file_loggers:
|
||||||
|
logger_filter = LoggerFilter(file_loggers)
|
||||||
|
file_handler.addFilter(logger_filter)
|
||||||
|
|
||||||
root_logger.addHandler(file_handler)
|
root_logger.addHandler(file_handler)
|
||||||
self.handlers['file'] = file_handler
|
self.handlers[f'file_{i}'] = file_handler
|
||||||
|
|
||||||
self._configure_library_loggers(level)
|
|
||||||
|
|
||||||
|
self._configure_library_loggers(main_level)
|
||||||
self._intercept_uvicorn_logging()
|
self._intercept_uvicorn_logging()
|
||||||
|
|
||||||
pyserve_logger = logging.getLogger('pyserve')
|
pyserve_logger = logging.getLogger('pyserve')
|
||||||
pyserve_logger.setLevel(getattr(logging, level))
|
pyserve_logger.setLevel(getattr(logging, main_level))
|
||||||
self.loggers['pyserve'] = pyserve_logger
|
self.loggers['pyserve'] = pyserve_logger
|
||||||
|
|
||||||
pyserve_logger.info(f"PyServe v{__version__} - Logger initialized")
|
pyserve_logger.info(f"PyServe v{__version__} - Logger initialized")
|
||||||
pyserve_logger.info(f"Logging level: {level}")
|
pyserve_logger.info(f"Logging level: {main_level}")
|
||||||
pyserve_logger.info(f"Console output: {'enabled' if console_output else 'disabled'}")
|
pyserve_logger.info(f"Console output: {'enabled' if console_output else 'disabled'}")
|
||||||
pyserve_logger.info(f"Log file: {log_file if log_file else 'disabled'}")
|
pyserve_logger.info(f"Console format: {console_format.get('type', 'standard')}")
|
||||||
|
|
||||||
|
for i, file_config in enumerate(files_config):
|
||||||
|
file_path = file_config.get('path', './logs/pyserve.log')
|
||||||
|
file_loggers = file_config.get('loggers', [])
|
||||||
|
file_format = file_config.get('format', {})
|
||||||
|
|
||||||
|
pyserve_logger.info(f"Log file[{i}]: {file_path}")
|
||||||
|
pyserve_logger.info(f"File format[{i}]: {file_format.get('type', 'standard')}")
|
||||||
|
if file_loggers:
|
||||||
|
pyserve_logger.info(f"File loggers[{i}]: {', '.join(file_loggers)}")
|
||||||
|
else:
|
||||||
|
pyserve_logger.info(f"File loggers[{i}]: all loggers")
|
||||||
|
|
||||||
self.configured = True
|
self.configured = True
|
||||||
|
|
||||||
@ -168,13 +305,13 @@ class PyServeLogManager:
|
|||||||
|
|
||||||
def _configure_library_loggers(self, main_level: str) -> None:
|
def _configure_library_loggers(self, main_level: str) -> None:
|
||||||
library_configs = {
|
library_configs = {
|
||||||
# Uvicorn и связанные - только в DEBUG режиме
|
# Uvicorn and related - only in DEBUG mode
|
||||||
'uvicorn': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
'uvicorn': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
||||||
'uvicorn.access': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
'uvicorn.access': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
||||||
'uvicorn.error': 'DEBUG' if main_level == 'DEBUG' else 'ERROR',
|
'uvicorn.error': 'DEBUG' if main_level == 'DEBUG' else 'ERROR',
|
||||||
'uvicorn.asgi': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
'uvicorn.asgi': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
||||||
|
|
||||||
# Starlette - только в DEBUG режиме
|
# Starlette - only in DEBUG mode
|
||||||
'starlette': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
'starlette': 'DEBUG' if main_level == 'DEBUG' else 'WARNING',
|
||||||
|
|
||||||
'asyncio': 'WARNING',
|
'asyncio': 'WARNING',
|
||||||
|
|||||||
@ -166,7 +166,7 @@ class RequestHandler:
|
|||||||
|
|
||||||
async def _handle_proxy(self, request: Request, config: Dict[str, Any],
|
async def _handle_proxy(self, request: Request, config: Dict[str, Any],
|
||||||
params: Dict[str, str]) -> Response:
|
params: Dict[str, str]) -> Response:
|
||||||
# TODO: Реализовать полноценное проксирование
|
# TODO: implement real proxying
|
||||||
proxy_url = config["proxy_pass"]
|
proxy_url = config["proxy_pass"]
|
||||||
|
|
||||||
for key, value in params.items():
|
for key, value in params.items():
|
||||||
|
|||||||
@ -208,9 +208,10 @@ class PyServeServer:
|
|||||||
self.config.http.templates_dir,
|
self.config.http.templates_dir,
|
||||||
]
|
]
|
||||||
|
|
||||||
log_dir = Path(self.config.logging.log_file).parent
|
for file_config in self.config.logging.files:
|
||||||
if log_dir != Path("."):
|
log_dir = Path(file_config.path).parent
|
||||||
directories.append(str(log_dir))
|
if log_dir != Path("."):
|
||||||
|
directories.append(str(log_dir))
|
||||||
|
|
||||||
for directory in directories:
|
for directory in directories:
|
||||||
Path(directory).mkdir(parents=True, exist_ok=True)
|
Path(directory).mkdir(parents=True, exist_ok=True)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user