mirror of
https://github.com/outbackdingo/step-ca-webui.git
synced 2026-01-28 02:20:18 +00:00
scoped logging
This commit is contained in:
@@ -169,4 +169,5 @@ project_root/
|
||||
---
|
||||
|
||||
# TODO
|
||||
- [ ] Adjust class diagram after finalizing the project
|
||||
- [ ] Adjust class diagram after finalizing the project
|
||||
- [ ] Prevent simultaneous actions calls
|
||||
@@ -1,15 +1,38 @@
|
||||
from flask import Flask, request, jsonify, Response
|
||||
from typing import Dict, List, Union, Tuple
|
||||
from uuid import uuid4
|
||||
import uuid
|
||||
from contextlib import contextmanager
|
||||
|
||||
from flask import Flask, request, jsonify, Response, g
|
||||
|
||||
from certificate_manager import CertificateManager
|
||||
from shared.logger import Logger, LogSeverity
|
||||
from shared.models import CommandInfo
|
||||
from shared.logger import Logger
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _logging_scope():
|
||||
"""
|
||||
Context manager for setting up a unique trace ID for each request in a Flask application.
|
||||
|
||||
This function ensures that each request has a unique trace ID, which can be used for logging.
|
||||
It generates a unique UUID and assigns it to the Flask `g` (global) object as `trace_id`.
|
||||
|
||||
Usage:
|
||||
with _logging_scope():
|
||||
# Code within this block can access g.trace_id
|
||||
pass
|
||||
# After this block, the finally part of _logging_scope is executed
|
||||
"""
|
||||
g.trace_id = str(uuid.uuid4())
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
class APIServer:
|
||||
_app = Flask(__name__)
|
||||
|
||||
def __init__(self, cert_manager: CertificateManager, logger: Logger):
|
||||
self._app = Flask(__name__)
|
||||
|
||||
self.cert_manager = cert_manager
|
||||
self.logger = logger
|
||||
|
||||
@@ -20,6 +43,17 @@ class APIServer:
|
||||
self._app.add_url_rule('/logs', 'get_logs', self.get_logs, methods=['GET'])
|
||||
self._app.add_url_rule('/logs/single', 'get_log_entry', self.get_log_entry, methods=['GET'])
|
||||
|
||||
@self._app.before_request
|
||||
def set_logging_scope():
|
||||
g.logging_scope = _logging_scope()
|
||||
g.logging_scope.__enter__()
|
||||
|
||||
@self._app.after_request
|
||||
def clear_logging_scope(response):
|
||||
if hasattr(g, 'logging_scope'):
|
||||
g.logging_scope.__exit__(None, None, None)
|
||||
return response
|
||||
|
||||
def run(self):
|
||||
self._app.run(host='0.0.0.0', port=5000)
|
||||
|
||||
|
||||
@@ -52,18 +52,16 @@ class CertificateManager:
|
||||
success = exit_code == 0
|
||||
message = "Certificate generated successfully" if success else "Failed to generate certificate"
|
||||
|
||||
trace_id = uuid4() # TODO: use scoped logging
|
||||
self.logger.log(
|
||||
entry_id = self.logger.log_scoped(
|
||||
LogSeverity.INFO if success else LogSeverity.ERROR,
|
||||
message,
|
||||
trace_id,
|
||||
CommandInfo(command, output, exit_code, "GENERATE_CERT")
|
||||
)
|
||||
|
||||
return { # TODO: extract to dataclass or namedtuple or typed dict
|
||||
"success": success,
|
||||
"message": message,
|
||||
"logEntryId": str(trace_id),
|
||||
"logEntryId": str(entry_id),
|
||||
"certificateId": params['keyName'],
|
||||
"certificateName": params['keyName'],
|
||||
"expirationDate": (datetime.now() + self._parse_duration(params['duration'])).isoformat() # TODO: remove _parse_duration
|
||||
@@ -81,18 +79,16 @@ class CertificateManager:
|
||||
success = exit_code == 0
|
||||
message = "Certificate renewed successfully" if success else "Failed to renew certificate"
|
||||
|
||||
trace_id = uuid4() # TODO: use scoped logging
|
||||
self.logger.log(
|
||||
entry_id = self.logger.log_scoped(
|
||||
LogSeverity.INFO if success else LogSeverity.ERROR,
|
||||
message,
|
||||
trace_id,
|
||||
CommandInfo(command, output, exit_code, "RENEW_CERT")
|
||||
)
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"message": message,
|
||||
"logEntryId": str(trace_id),
|
||||
"logEntryId": str(entry_id),
|
||||
"certificateId": cert_id,
|
||||
"newExpirationDate": (datetime.now() + timedelta(seconds=duration)).isoformat()
|
||||
}
|
||||
@@ -109,18 +105,16 @@ class CertificateManager:
|
||||
success = exit_code == 0
|
||||
message = "Certificate revoked successfully" if success else "Failed to revoke certificate"
|
||||
|
||||
trace_id = uuid4() # TODO: use scoped logging
|
||||
self.logger.log(
|
||||
entry_id = self.logger.log_scoped(
|
||||
LogSeverity.INFO if success else LogSeverity.ERROR,
|
||||
message,
|
||||
trace_id,
|
||||
CommandInfo(command, output, exit_code, "REVOKE_CERT")
|
||||
)
|
||||
|
||||
return {
|
||||
"success": success,
|
||||
"message": message,
|
||||
"logEntryId": str(trace_id),
|
||||
"logEntryId": str(entry_id),
|
||||
"certificateId": cert_id,
|
||||
"revocationDate": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import logging
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from flask import g
|
||||
|
||||
from .models import LogEntry, LogSeverity, CommandInfo
|
||||
|
||||
|
||||
@@ -10,10 +13,13 @@ class Logger:
|
||||
self.log_file = log_file
|
||||
logging.basicConfig(filename=log_file, level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||
|
||||
def log(
|
||||
self, severity: LogSeverity, message: str, trace_id: UUID,
|
||||
def log_scoped(
|
||||
self,
|
||||
severity: LogSeverity,
|
||||
message: str,
|
||||
command_info: Optional[CommandInfo] = None
|
||||
) -> None:
|
||||
) -> int:
|
||||
trace_id = g.trace_id if hasattr(g, 'trace_id') else "AUTO_SCOPE_BROKEN"
|
||||
log_entry = LogEntry(
|
||||
entry_id=self._get_next_entry_id(),
|
||||
timestamp=datetime.now(),
|
||||
@@ -23,6 +29,25 @@ class Logger:
|
||||
command_info=command_info
|
||||
)
|
||||
self._write_log_entry(log_entry)
|
||||
return log_entry.entry_id
|
||||
|
||||
def log_with_trace(
|
||||
self,
|
||||
severity: LogSeverity,
|
||||
message: str,
|
||||
trace_id: UUID,
|
||||
command_info: Optional[CommandInfo] = None
|
||||
) -> int:
|
||||
log_entry = LogEntry(
|
||||
entry_id=self._get_next_entry_id(),
|
||||
timestamp=datetime.now(),
|
||||
severity=severity,
|
||||
message=message,
|
||||
trace_id=trace_id,
|
||||
command_info=command_info
|
||||
)
|
||||
self._write_log_entry(log_entry)
|
||||
return log_entry.entry_id
|
||||
|
||||
def get_logs(self, filters: Dict) -> List[LogEntry]:
|
||||
# Implementation for retrieving logs based on filters
|
||||
|
||||
Reference in New Issue
Block a user