import http.server
import socketserver
import threading
import time
import logging
from datetime import datetime
import signal
import sys
class LoggingHandler(http.server.SimpleHTTPRequestHandler):
"""Custom HTTP handler that logs all requests with timestamp and client info"""
def log_message(self, format, *args):
"""Override to log messages to file and console with timestamp"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
client_ip = self.client_address[0]
message = f"[{timestamp}] {client_ip} - {format % args}"
logging.info(message)
print(message)
def do_GET(self):
"""Handle GET requests with custom logging"""
start_time = time.time()
# Call the parent method to handle the request
super().do_GET()
# Log request duration
duration = time.time() - start_time
self.log_message('"%s" %s %s (%.3fs)',
self.requestline, str(self.status), str(self.bytes_sent), duration)
class GracefulHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
"""HTTP server that can be shut down gracefully"""
def __init__(self, server_address, handler_class):
super().__init__(server_address, handler_class)
self.running = True
self.allow_reuse_address = True
def serve_forever(self):
"""Override serve_forever to check for running flag"""
while self.running:
self.handle_request()
def shutdown(self):
"""Gracefully shut down the server"""
self.running = False
self.server_close()
def setup_logging():
"""Setup logging configuration"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(message)s',
handlers=[
logging.FileHandler('server.log'),
logging.StreamHandler()
]
)
def signal_handler(signum, frame):
"""Handle shutdown signals"""
print(f"\nReceived signal {signum}. Shutting down server...")
server.shutdown()
sys.exit(0)
def run_server(host='localhost', port=8000):
"""Run the HTTP server with graceful shutdown"""
global server
# Setup logging
setup_logging()
# Create server
server = GracefulHTTPServer((host, port), LoggingHandler)
# Register signal handlers for graceful shutdown
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Start server
print(f"Server starting at http://{host}:{port}")
print("Press Ctrl+C to stop")
print(f"Server logs are written to 'server.log'")
try:
# Serve forever (or until interrupted)
while True:
server.handle_request()
except KeyboardInterrupt:
print("\nKeyboard interrupt received. Shutting down...")
finally:
server.shutdown()
# Run the server if this script is executed directly
if __name__ == "__main__":
run_server()
This is a simple but feature-rich HTTP server that provides a modern web server experience with:
server.log
)This snippet solves several common problems when working with local HTTP servers:
web_server.py
)python web_server.py
Ctrl+C
to stop the serverThe server will:
server.log
and the consoleYou can modify the server behavior by changing the parameters:
host
: The address to bind to (default: “localhost”)port
: The port to listen on (default: 8000)setup_logging()
functionThis is a simple but powerful tool for any Python developer working with static files or web content.