11"""
2- DevOps Info Service
3- Веб-сервис для предоставления информации о системе и состоянии сервиса
2+ DevOps Info Service with JSON Logging
43"""
54
65import os
76import socket
8- import platform
7+ import logging
98from datetime import datetime , timezone
109from flask import Flask , jsonify , request
10+ from pythonjsonlogger import jsonlogger
1111
12- # Создаем приложение Flask
12+ # Flask app
1313app = Flask (__name__ )
1414
15- # Настройки из переменных окружения
15+ # ENV config
1616HOST = os .getenv ('HOST' , '0.0.0.0' )
1717PORT = int (os .getenv ('PORT' , 5000 ))
1818DEBUG = os .getenv ('DEBUG' , 'False' ).lower () == 'true'
1919
20- # Время запуска сервиса
2120START_TIME = datetime .now (timezone .utc )
2221
23- # Добавляем Docker информацию
22+ # Docker info
2423IS_DOCKER = os .path .exists ('/.dockerenv' )
2524CONTAINER_ID = socket .gethostname () if IS_DOCKER else None
2625
26+ # ----------------------------
27+ # JSON LOGGING CONFIG
28+ # ----------------------------
29+ logger = logging .getLogger ()
30+ logger .setLevel (logging .INFO )
31+
32+ handler = logging .StreamHandler ()
33+ formatter = jsonlogger .JsonFormatter (
34+ '%(asctime)s %(levelname)s %(message)s %(method)s %(path)s %(status)s %(client_ip)s %(duration)s'
35+ )
36+ handler .setFormatter (formatter )
37+ logger .addHandler (handler )
38+
39+ # ----------------------------
40+ # HELPERS
41+ # ----------------------------
2742def get_uptime ():
28- """Рассчитывает время работы сервиса"""
2943 delta = datetime .now (timezone .utc ) - START_TIME
3044 seconds = int (delta .total_seconds ())
31-
32- hours = seconds // 3600
33- minutes = (seconds % 3600 ) // 60
34- secs = seconds % 60
35-
36- parts = []
37- if hours > 0 :
38- parts .append (f"{ hours } hour{ 's' if hours != 1 else '' } " )
39- if minutes > 0 :
40- parts .append (f"{ minutes } minute{ 's' if minutes != 1 else '' } " )
41- if secs > 0 or not parts :
42- parts .append (f"{ secs } second{ 's' if secs != 1 else '' } " )
43-
44- return {
45- 'seconds' : seconds ,
46- 'human' : ', ' .join (parts )
47- }
45+ return {'seconds' : seconds , 'human' : f"{ seconds } seconds" }
46+
47+ # ----------------------------
48+ # LOGGING MIDDLEWARE
49+ # ----------------------------
50+ @app .before_request
51+ def log_request ():
52+ request .start_time = datetime .now (timezone .utc )
53+ logging .info (
54+ "request_received" ,
55+ extra = {
56+ "method" : request .method ,
57+ "path" : request .path ,
58+ "client_ip" : request .remote_addr
59+ }
60+ )
61+
62+ @app .after_request
63+ def log_response (response ):
64+ duration = (datetime .now (timezone .utc ) - request .start_time ).total_seconds ()
65+ logging .info (
66+ "request_completed" ,
67+ extra = {
68+ "method" : request .method ,
69+ "path" : request .path ,
70+ "status" : response .status_code ,
71+ "client_ip" : request .remote_addr ,
72+ "duration" : duration
73+ }
74+ )
75+ return response
4876
77+ # ----------------------------
78+ # ROUTES
79+ # ----------------------------
4980@app .route ('/' )
5081def main_endpoint ():
51- """Основной эндпоинт - информация о сервисе и системе"""
52-
53- # Получаем информацию о системе
54- system_info = {
55- 'hostname' : socket .gethostname (),
56- 'platform' : platform .system (),
57- 'platform_version' : platform .version (),
58- 'architecture' : platform .machine (),
59- 'cpu_count' : os .cpu_count (),
60- 'python_version' : platform .python_version (),
61- 'is_docker_container' : IS_DOCKER ,
62- 'container_id' : CONTAINER_ID
63- }
64-
65- # Информация о времени
66- runtime_info = {
67- 'uptime_seconds' : get_uptime ()['seconds' ],
68- 'uptime_human' : get_uptime ()['human' ],
69- 'current_time' : datetime .now (timezone .utc ).isoformat (),
70- 'timezone' : 'UTC' ,
71- 'start_time' : START_TIME .isoformat ()
72- }
73-
74- # Информация о запросе
75- request_info = {
76- 'client_ip' : request .remote_addr ,
77- 'user_agent' : request .headers .get ('User-Agent' , 'Unknown' ),
78- 'method' : request .method ,
79- 'path' : request .path
80- }
81-
8282 return jsonify ({
83- 'service' : {
84- 'name' : 'devops-info-service' ,
85- 'version' : '2.0.0' ,
86- 'description' : 'DevOps course info service (Dockerized)' ,
87- 'framework' : 'Flask' ,
88- 'environment' : 'docker' if IS_DOCKER else 'local'
89- },
90- 'system' : system_info ,
91- 'runtime' : runtime_info ,
92- 'request' : request_info ,
93- 'endpoints' : [
94- {'path' : '/' , 'method' : 'GET' , 'description' : 'Service information' },
95- {'path' : '/health' , 'method' : 'GET' , 'description' : 'Health check' },
96- {'path' : '/docker' , 'method' : 'GET' , 'description' : 'Docker information' }
97- ]
83+ 'service' : 'devops-info-service' ,
84+ 'status' : 'running' ,
85+ 'time' : datetime .now (timezone .utc ).isoformat ()
9886 })
9987
10088@app .route ('/health' )
10189def health_check ():
102- """Эндпоинт проверки состояния сервиса"""
10390 return jsonify ({
10491 'status' : 'healthy' ,
105- 'timestamp' : datetime .now (timezone .utc ).isoformat (),
106- 'uptime_seconds' : get_uptime ()['seconds' ],
107- 'environment' : 'docker' if IS_DOCKER else 'local' ,
108- 'container_id' : CONTAINER_ID
92+ 'uptime' : get_uptime ()['seconds' ]
10993 })
11094
11195@app .route ('/docker' )
11296def docker_info ():
113- """Эндпоинт информации о Docker окружении"""
11497 return jsonify ({
11598 'is_docker' : IS_DOCKER ,
116- 'container_id' : CONTAINER_ID ,
117- 'docker_env' : dict (os .environ ) if IS_DOCKER else None ,
118- 'message' : 'Running in Docker container' if IS_DOCKER else 'Running locally'
99+ 'container_id' : CONTAINER_ID
119100 })
120101
102+ # ----------------------------
103+ # ERRORS
104+ # ----------------------------
121105@app .errorhandler (404 )
122106def not_found (error ):
123- """Обработка ошибки 404"""
124- return jsonify ({
125- 'error' : 'Not Found' ,
126- 'message' : 'Endpoint does not exist' ,
127- 'available_endpoints' : [
128- { 'path' : '/' , 'method' : 'GET' } ,
129- { 'path' : '/health' , 'method' : 'GET' },
130- { 'path' : '/docker' , 'method' : 'GET' }
131- ]
132- }), 404
107+ logging . error (
108+ "not_found" ,
109+ extra = {
110+ "method" : request . method ,
111+ "path" : request . path ,
112+ "status" : 404 ,
113+ "client_ip" : request . remote_addr
114+ }
115+ )
116+ return jsonify ({ 'error' : 'Not Found' }), 404
133117
118+ # ----------------------------
119+ # START
120+ # ----------------------------
134121if __name__ == '__main__' :
135- print (f"Starting DevOps Info Service on { HOST } :{ PORT } " )
136- print (f"Debug mode: { DEBUG } " )
137- print (f"Docker environment: { IS_DOCKER } " )
138- print (f"Container ID: { CONTAINER_ID } " )
139-
122+ logging .info (
123+ "service_started" ,
124+ extra = {
125+ "host" : HOST ,
126+ "port" : PORT ,
127+ "debug" : DEBUG ,
128+ "docker" : IS_DOCKER ,
129+ "container_id" : CONTAINER_ID
130+ }
131+ )
140132 app .run (host = HOST , port = PORT , debug = DEBUG )
0 commit comments