1717caching system to set a key and then triggering the actual event from middleware
1818"""
1919
20+ import json
2021import logging
22+ import uuid
2123from http import HTTPStatus
2224from threading import local
2325
3638
3739
3840class PromgenMiddleware :
41+ ACCESS_LOG_FIELD_LIMITS = {
42+ "body" : 8192 ,
43+ "description" : 512 ,
44+ "clause" : 8192 ,
45+ }
46+
3947 def __init__ (self , get_response ):
4048 self .get_response = get_response
4149
@@ -53,8 +61,46 @@ def __call__(self, request):
5361 if request .user .is_authenticated :
5462 _user .value = request .user
5563
64+ # Log all requests to our v2 API endpoints
65+ if settings .ENABLE_API_LOGGING and request .path .startswith ("/rest/v2/" ):
66+ try :
67+ # Generate a trace ID for each request
68+ trace_id = str (uuid .uuid4 ())
69+ request .trace_id = trace_id
70+ # Log the IP address of the request
71+ ip_address = request .META .get ("REMOTE_ADDR" )
72+ logger .info (f"[Trace ID: { trace_id } ] IP Address: { ip_address } " )
73+ # Log the user if authenticated
74+ if request .user .is_authenticated :
75+ logger .info (f"[Trace ID: { trace_id } ] User: { request .user .username } " )
76+ # Log the request details
77+ logger .info (
78+ f"[Trace ID: { request .trace_id } ] Request: { request .method } { request .get_full_path ()} - body size: { len (request .body ) if request .body else 0 } bytes"
79+ )
80+ if request .body and request .headers ["Content-Type" ] == "application/json" :
81+ # Only log first 512 characters of the request body to avoid flooding the logs
82+ logger .info (
83+ f"[Trace ID: { request .trace_id } ] Request body: { self .truncate_json_fields (json .loads (request .body ), self .ACCESS_LOG_FIELD_LIMITS )} "
84+ )
85+ except Exception as e :
86+ logger .exception (
87+ f"[Trace ID: { request .trace_id } ] An error occurred when parsing request: { str (e )} "
88+ )
89+
5690 response = self .get_response (request )
5791
92+ # Log all responses to our v2 API endpoints
93+ if settings .ENABLE_API_LOGGING and request .path .startswith ("/rest/v2/" ):
94+ try :
95+ # Log the response details
96+ logger .info (
97+ f"[Trace ID: { request .trace_id } ] Response status: { response .status_code } - content size: { len (response .content )} bytes"
98+ )
99+ except Exception as e :
100+ logger .exception (
101+ f"[Trace ID: { request .trace_id } ] An error occurred when logging response: { str (e )} "
102+ )
103+
58104 triggers = {
59105 "Config" : trigger_write_config .send ,
60106 "Rules" : trigger_write_rules .send ,
@@ -67,6 +113,36 @@ def __call__(self, request):
67113 messages .warning (request , "Error queueing %s " % msg )
68114 return response
69115
116+ @staticmethod
117+ def truncate_json_fields (data , field_limits , default_limits = 128 ):
118+ """
119+ Recursively truncate fields in a JSON object based on a given field-length mapping.
120+
121+ Args:
122+ data (dict): The JSON object to process.
123+ field_limits (dict): A mapping of field names to their maximum lengths.
124+ default_limits (int): Default maximum length for fields not specified in field_limits.
125+
126+ Returns:
127+ dict: A new JSON object with truncated fields.
128+ """
129+ truncated_data = {}
130+ for field , value in data .items ():
131+ if isinstance (value , dict ):
132+ # Recursively process nested dictionaries
133+ truncated_data [field ] = PromgenMiddleware .truncate_json_fields (value , field_limits )
134+ elif field in field_limits and isinstance (value , str ):
135+ truncated_data [field ] = value [: field_limits [field ]] + (
136+ "..." if len (value ) > field_limits [field ] else ""
137+ )
138+ elif isinstance (value , str ):
139+ truncated_data [field ] = value [:default_limits ] + (
140+ "..." if len (value ) > default_limits else ""
141+ )
142+ else :
143+ truncated_data [field ] = value
144+ return truncated_data
145+
70146
71147def get_current_user ():
72148 return getattr (_user , "value" , None )
0 commit comments