Skip to content

Commit 6a95341

Browse files
feat: implement logging functionality in logger_task for API activity
- Added a new logger_task module to handle logging of API activity to MongoDB and PostgreSQL. - Introduced functions for safely decoding request/response bodies and processing logs based on MongoDB availability. - Refactored APITokenLogMiddleware to utilize the new logging functions, improving code organization and maintainability.
1 parent 415f6e3 commit 6a95341

File tree

2 files changed

+95
-56
lines changed

2 files changed

+95
-56
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# Python imports
2+
import logging
3+
from typing import Optional, Dict, Any
4+
5+
# Third party imports
6+
from pymongo.collection import Collection
7+
8+
# Django imports
9+
from plane.settings.mongo import MongoConnection
10+
from plane.utils.exception_logger import log_exception
11+
from plane.db.models import APIActivityLog
12+
13+
14+
logger = logging.getLogger("plane.worker")
15+
16+
17+
def get_mongo_collection() -> Optional[Collection]:
18+
"""
19+
Returns the MongoDB collection for external API activity logs.
20+
"""
21+
if not MongoConnection.is_configured():
22+
logger.info("MongoDB not configured")
23+
return None
24+
25+
try:
26+
return MongoConnection.get_collection("api_activity_logs")
27+
except Exception as e:
28+
logger.error(f"Error getting MongoDB collection: {str(e)}")
29+
log_exception(e)
30+
return None
31+
32+
33+
def safe_decode_body(content: bytes) -> Optional[str]:
34+
"""
35+
Safely decodes request/response body content, handling binary data.
36+
Returns "[Binary Content]" if the content is binary, or a string representation of the content.
37+
Returns None if the content is None or empty.
38+
"""
39+
# If the content is None, return None
40+
if content is None:
41+
return None
42+
43+
# If the content is an empty bytes object, return None
44+
if content == b"":
45+
return None
46+
47+
# Check if content is binary by looking for common binary file signatures
48+
if content.startswith(b"\x89PNG") or content.startswith(b"\xff\xd8\xff") or content.startswith(b"%PDF"):
49+
return "[Binary Content]"
50+
51+
try:
52+
return content.decode("utf-8")
53+
except UnicodeDecodeError:
54+
return "[Could not decode content]"
55+
56+
57+
def log_to_mongo(mongo_collection: Optional[Collection], log_document: Dict[str, Any]) -> bool:
58+
"""
59+
Logs the request to MongoDB if available.
60+
"""
61+
62+
if mongo_collection is None:
63+
return False
64+
65+
try:
66+
mongo_collection.insert_one(log_document)
67+
return True
68+
except Exception as e:
69+
log_exception(e)
70+
return False
71+
72+
73+
def log_to_postgres(log_data: Dict[str, Any]) -> bool:
74+
"""
75+
Fallback to logging to PostgreSQL if MongoDB is unavailable.
76+
"""
77+
try:
78+
APIActivityLog.objects.create(**log_data)
79+
return True
80+
except Exception as e:
81+
log_exception(e)
82+
return False
83+
84+
85+
def process_logs(log_data: Dict[str, Any], mongo_log: Dict[str, Any]):
86+
"""
87+
Process logs to save to MongoDB or Postgres based on the configuration
88+
"""
89+
mongo_collection = get_mongo_collection()
90+
if mongo_collection is not None:
91+
log_to_mongo(mongo_collection, mongo_log)
92+
else:
93+
log_to_postgres(log_data)

apps/api/plane/middleware/logger.py

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from plane.db.models import APIActivityLog
1515
from plane.settings.mongo import MongoConnection
1616
from plane.utils.exception_logger import log_exception
17+
from plane.bgtasks.logger_task import process_logs
1718

1819
api_logger = logging.getLogger("plane.api.request")
1920

@@ -79,30 +80,6 @@ class APITokenLogMiddleware:
7980

8081
def __init__(self, get_response):
8182
self.get_response = get_response
82-
self.mongo_available = False
83-
84-
# Initialize MongoDB collection
85-
try:
86-
self.mongo_collection = self.get_mongo_collection()
87-
self.mongo_available = True if self.mongo_collection is not None else False
88-
except Exception as e:
89-
api_logger.error(f"Error getting MongoDB collection: {str(e)}")
90-
log_exception(e)
91-
92-
def get_mongo_collection(self):
93-
"""
94-
Returns the MongoDB collection for API activity logs.
95-
"""
96-
if not MongoConnection.is_configured():
97-
api_logger.info("MongoDB not configured")
98-
return None
99-
100-
try:
101-
return MongoConnection.get_collection("api_activity_logs")
102-
except Exception as e:
103-
api_logger.error(f"Error getting MongoDB collection: {str(e)}")
104-
log_exception(e)
105-
return None
10683

10784
def __call__(self, request):
10885
request_body = request.body
@@ -132,34 +109,6 @@ def _safe_decode_body(self, content):
132109
except UnicodeDecodeError:
133110
return "[Could not decode content]"
134111

135-
def log_to_mongo(self, log_document):
136-
"""
137-
Logs the request to MongoDB if available.
138-
"""
139-
140-
if not self.mongo_available or self.mongo_collection is None:
141-
return False
142-
143-
try:
144-
self.mongo_collection.insert_one(log_document)
145-
return True
146-
except Exception as e:
147-
log_exception(e)
148-
self.mongo_collection = self.get_mongo_collection()
149-
self.mongo_available = True if self.mongo_collection is not None else False
150-
return False
151-
152-
def log_to_postgres(self, log_data):
153-
"""
154-
Fallback to logging to PostgreSQL if MongoDB is unavailable.
155-
"""
156-
try:
157-
APIActivityLog.objects.create(**log_data)
158-
return True
159-
except Exception as e:
160-
log_exception(e)
161-
return False
162-
163112
def process_request(self, request, response, request_body):
164113
api_key_header = "X-Api-Key"
165114
api_key = request.headers.get(api_key_header)
@@ -195,10 +144,7 @@ def process_request(self, request, response, request_body):
195144
"updated_by": user_id,
196145
}
197146

198-
# Log to MongoDB if available
199-
if not self.log_to_mongo(mongo_log):
200-
# Fallback to logging to PostgreSQL
201-
self.log_to_postgres(log_data)
147+
process_logs.delay(log_data=log_data, mongo_log=mongo_log)
202148

203149
except Exception as e:
204150
log_exception(e)

0 commit comments

Comments
 (0)