mirror of
https://gitlab.com/crafty-controller/crafty-4.git
synced 2024-08-30 18:23:09 +00:00
Set audit logging to logfile instead of DB
This commit is contained in:
parent
0fbf14063c
commit
227d642546
54
app/classes/logging/log_formatter.py
Normal file
54
app/classes/logging/log_formatter.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import logging
|
||||||
|
import logging.config
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
class JsonEncoderStrFallback(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
try:
|
||||||
|
return super().default(o)
|
||||||
|
except TypeError as exc:
|
||||||
|
if "not JSON serializable" in str(exc):
|
||||||
|
return str(o)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class JsonEncoderDatetime(JsonEncoderStrFallback):
|
||||||
|
def default(self, o):
|
||||||
|
if isinstance(o, datetime):
|
||||||
|
return o.strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||||
|
else:
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonFormatter(logging.Formatter):
|
||||||
|
def formatTime(self, record, datefmt=None):
|
||||||
|
"""
|
||||||
|
Override formatTime to customize the time format.
|
||||||
|
"""
|
||||||
|
timestamp = datetime.fromtimestamp(record.created)
|
||||||
|
if datefmt:
|
||||||
|
# Use the specified date format
|
||||||
|
return timestamp.strftime(datefmt)
|
||||||
|
else:
|
||||||
|
# Default date format: YYYY-MM-DD HH:MM:SS,mmm
|
||||||
|
secs = int(record.msecs)
|
||||||
|
return f"{timestamp.strftime('%Y-%m-%d %H:%M:%S')},{secs:03d}"
|
||||||
|
|
||||||
|
def format(self, record):
|
||||||
|
log_data = {
|
||||||
|
"level": record.levelname,
|
||||||
|
"time": self.formatTime(record),
|
||||||
|
"log_msg": record.getMessage(),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Filter out standard log record attributes and include only custom ones
|
||||||
|
custom_attrs = ["user_name", "user_id", "server_id", "source_ip"]
|
||||||
|
extra_attrs = {
|
||||||
|
key: value for key, value in record.__dict__.items() if key in custom_attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
# Merge extra attributes with log data
|
||||||
|
log_data.update(extra_attrs)
|
||||||
|
return json.dumps(log_data)
|
@ -20,6 +20,7 @@ from app.classes.shared.main_models import DatabaseShortcuts
|
|||||||
from app.classes.shared.websocket_manager import WebSocketManager
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
auth_logger = logging.getLogger("audit_log")
|
||||||
|
|
||||||
|
|
||||||
# **********************************************************************************
|
# **********************************************************************************
|
||||||
@ -166,50 +167,26 @@ class HelpersManagement:
|
|||||||
WebSocketManager().broadcast_user(user, "notification", audit_msg)
|
WebSocketManager().broadcast_user(user, "notification", audit_msg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error broadcasting to user {user} - {e}")
|
logger.error(f"Error broadcasting to user {user} - {e}")
|
||||||
|
auth_logger.info(
|
||||||
AuditLog.insert(
|
str(log_msg),
|
||||||
{
|
extra={
|
||||||
AuditLog.user_name: user_data["username"],
|
"user_name": user_data["username"],
|
||||||
AuditLog.user_id: user_id,
|
"user_id": user_id,
|
||||||
AuditLog.server_id: server_id,
|
"server_id": server_id,
|
||||||
AuditLog.log_msg: audit_msg,
|
"source_ip": source_ip,
|
||||||
AuditLog.source_ip: source_ip,
|
},
|
||||||
}
|
)
|
||||||
).execute()
|
|
||||||
# deletes records when there's more than 300
|
|
||||||
ordered = AuditLog.select().order_by(+AuditLog.created)
|
|
||||||
for item in ordered:
|
|
||||||
if not self.helper.get_setting("max_audit_entries"):
|
|
||||||
max_entries = 300
|
|
||||||
else:
|
|
||||||
max_entries = self.helper.get_setting("max_audit_entries")
|
|
||||||
if AuditLog.select().count() > max_entries:
|
|
||||||
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
def add_to_audit_log_raw(self, user_name, user_id, server_id, log_msg, source_ip):
|
||||||
AuditLog.insert(
|
auth_logger.info(
|
||||||
{
|
str(log_msg),
|
||||||
AuditLog.user_name: user_name,
|
extra={
|
||||||
AuditLog.user_id: user_id,
|
"user_name": user_name,
|
||||||
AuditLog.server_id: server_id,
|
"user_id": user_id,
|
||||||
AuditLog.log_msg: log_msg,
|
"server_id": server_id,
|
||||||
AuditLog.source_ip: source_ip,
|
"source_ip": source_ip,
|
||||||
}
|
},
|
||||||
).execute()
|
)
|
||||||
# deletes records when there's more than 300
|
|
||||||
ordered = AuditLog.select().order_by(+AuditLog.created)
|
|
||||||
for item in ordered:
|
|
||||||
# configurable through app/config/config.json
|
|
||||||
if not self.helper.get_setting("max_audit_entries"):
|
|
||||||
max_entries = 300
|
|
||||||
else:
|
|
||||||
max_entries = self.helper.get_setting("max_audit_entries")
|
|
||||||
if AuditLog.select().count() > max_entries:
|
|
||||||
AuditLog.delete().where(AuditLog.audit_id == item.audit_id).execute()
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_crafty_row():
|
def create_crafty_row():
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
"auth": {
|
"auth": {
|
||||||
"format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s"
|
"format": "%(asctime)s - [AUTH] - %(levelname)s - %(message)s"
|
||||||
},
|
},
|
||||||
|
"audit": {
|
||||||
|
"()": "app.classes.logging.log_formatter.JsonFormatter"
|
||||||
|
},
|
||||||
"cmd_queue": {
|
"cmd_queue": {
|
||||||
"format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s"
|
"format": "%(asctime)s - [CMD_QUEUE] - %(levelname)s - %(message)s"
|
||||||
}
|
}
|
||||||
@ -70,6 +73,14 @@
|
|||||||
"maxBytes": 10485760,
|
"maxBytes": 10485760,
|
||||||
"backupCount": 20,
|
"backupCount": 20,
|
||||||
"encoding": "utf8"
|
"encoding": "utf8"
|
||||||
|
},
|
||||||
|
"audit_log_handler": {
|
||||||
|
"class": "logging.handlers.RotatingFileHandler",
|
||||||
|
"formatter": "audit",
|
||||||
|
"filename": "logs/audit.log",
|
||||||
|
"maxBytes": 10485760,
|
||||||
|
"backupCount": 20,
|
||||||
|
"encoding": "utf8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"loggers": {
|
"loggers": {
|
||||||
@ -108,6 +119,12 @@
|
|||||||
"cmd_queue_file_handler"
|
"cmd_queue_file_handler"
|
||||||
],
|
],
|
||||||
"propagate": false
|
"propagate": false
|
||||||
|
},
|
||||||
|
"audit_log": {
|
||||||
|
"level": "INFO",
|
||||||
|
"handlers": [
|
||||||
|
"audit_log_handler"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
6
main.py
6
main.py
@ -17,6 +17,7 @@ from app.classes.models.users import HelperUsers
|
|||||||
from app.classes.models.management import HelpersManagement
|
from app.classes.models.management import HelpersManagement
|
||||||
from app.classes.shared.import_helper import ImportHelpers
|
from app.classes.shared.import_helper import ImportHelpers
|
||||||
from app.classes.shared.websocket_manager import WebSocketManager
|
from app.classes.shared.websocket_manager import WebSocketManager
|
||||||
|
from app.classes.logging.log_formatter import JsonFormatter
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
helper = Helpers()
|
helper = Helpers()
|
||||||
@ -284,6 +285,11 @@ def setup_logging(debug=True):
|
|||||||
|
|
||||||
logging.config.dictConfig(logging_config)
|
logging.config.dictConfig(logging_config)
|
||||||
|
|
||||||
|
# Apply JSON formatting to the "audit" handler
|
||||||
|
for handler in logging.getLogger().handlers:
|
||||||
|
if handler.name == "audit_log_handler":
|
||||||
|
handler.setFormatter(JsonFormatter())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
logging.warning(f"Unable to read logging config from {logging_config_file}")
|
logging.warning(f"Unable to read logging config from {logging_config_file}")
|
||||||
|
Loading…
Reference in New Issue
Block a user