From b0051dce39ed560ba8c67c05c59a50853014d7e9 Mon Sep 17 00:00:00 2001 From: hashjang Date: Fri, 16 May 2025 13:49:09 +0800 Subject: [PATCH] feat: add debug log for request and response (#19781) -- reformatted based on comment (thanks to @QuantumGhost) --- api/app_factory.py | 2 + api/configs/deploy/__init__.py | 6 +++ api/extensions/ext_logging.py | 44 ------------------- api/extensions/ext_request_logging.py | 61 +++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 api/extensions/ext_request_logging.py diff --git a/api/app_factory.py b/api/app_factory.py index 1c886ac5c7..3a258be28f 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -54,6 +54,7 @@ def initialize_extensions(app: DifyApp): ext_otel, ext_proxy_fix, ext_redis, + ext_request_logging, ext_sentry, ext_set_secretkey, ext_storage, @@ -83,6 +84,7 @@ def initialize_extensions(app: DifyApp): ext_blueprints, ext_commands, ext_otel, + ext_request_logging, ] for ext in extensions: short_name = ext.__name__.split(".")[-1] diff --git a/api/configs/deploy/__init__.py b/api/configs/deploy/__init__.py index 950936d3c6..63f4dfba63 100644 --- a/api/configs/deploy/__init__.py +++ b/api/configs/deploy/__init__.py @@ -17,6 +17,12 @@ class DeploymentConfig(BaseSettings): default=False, ) + # Request logging configuration + ENABLE_REQUEST_LOGGING: bool = Field( + description="Enable request and response body logging", + default=False, + ) + EDITION: str = Field( description="Deployment edition of the application (e.g., 'SELF_HOSTED', 'CLOUD')", default="SELF_HOSTED", diff --git a/api/extensions/ext_logging.py b/api/extensions/ext_logging.py index 410ca7aed9..79d49aba5e 100644 --- a/api/extensions/ext_logging.py +++ b/api/extensions/ext_logging.py @@ -1,4 +1,3 @@ -import json import logging import os import sys @@ -61,49 +60,6 @@ def init_app(app: DifyApp): if handler.formatter: handler.formatter.converter = time_converter - # for debugging purposes, log the request context - @app.before_request - def log_request(): - # only log if DEBUG is enabled - if logging.getLogger().isEnabledFor(logging.DEBUG): - content_type = flask.request.content_type - # only log body if the request is JSON - if flask.request.data and "application/json" in content_type.lower(): - try: - json_data = json.loads(flask.request.data) - formatted_json = json.dumps(json_data, ensure_ascii=False, indent=2) - logging.debug( - "Received Request %s -> %s, Request Body:\n%s", - flask.request.method, - flask.request.path, - formatted_json, - ) - except Exception: - logging.exception("Failed to parse JSON request") - else: - logging.debug("Received Request %s -> %s", flask.request.method, flask.request.path) - - # for debugging purposes, log the response - @app.after_request - def log_response(response): - # only log if DEBUG is enabled - if logging.getLogger().isEnabledFor(logging.DEBUG): - # only log body if the response is JSON - if response.content_type and "application/json" in response.content_type.lower(): - try: - response_data = response.get_data(as_text=True) - json_data = json.loads(response_data) - formatted_json = json.dumps(json_data, ensure_ascii=False, indent=2) - logging.debug( - "Response %s %s, Response Body:\n%s", response.status, response.content_type, formatted_json - ) - except Exception: - logging.exception("Failed to parse JSON response") - else: - logging.debug("Response %s %s", response.status, response.content_type) - - return response - def get_request_id(): if getattr(flask.g, "request_id", None): diff --git a/api/extensions/ext_request_logging.py b/api/extensions/ext_request_logging.py new file mode 100644 index 0000000000..d3c33ddf25 --- /dev/null +++ b/api/extensions/ext_request_logging.py @@ -0,0 +1,61 @@ +import json +import logging + +import flask +from flask import Flask +from flask.signals import request_finished, request_started + +from configs import dify_config + +logger = logging.getLogger(__name__) + + +def init_app(app: Flask): + """Initialize the request logging extension.""" + if not dify_config.ENABLE_REQUEST_LOGGING: + return + + @request_started.connect_via(app) + def log_request_started(sender, **extra): + """Log the start of a request.""" + if not logger.isEnabledFor(logging.DEBUG): + return + + request = flask.request + content_type = request.content_type + if request.data and "application/json" in content_type.lower(): + try: + json_data = json.loads(request.data) + formatted_json = json.dumps(json_data, ensure_ascii=False, indent=2) + logger.debug( + "Received Request %s -> %s, Request Body:\n%s", + request.method, + request.path, + formatted_json, + ) + except Exception: + logger.exception("Failed to parse JSON request") + else: + logger.debug("Received Request %s -> %s", request.method, request.path) + + @request_finished.connect_via(app) + def log_request_finished(sender, response, **extra): + """Log the end of a request.""" + if not logger.isEnabledFor(logging.DEBUG) or response is None: + return + + if response.content_type and "application/json" in response.content_type.lower(): + try: + response_data = response.get_data(as_text=True) + json_data = json.loads(response_data) + formatted_json = json.dumps(json_data, ensure_ascii=False, indent=2) + logger.debug( + "Response %s %s, Response Body:\n%s", + response.status, + response.content_type, + formatted_json, + ) + except Exception: + logger.exception("Failed to parse JSON response") + else: + logger.debug("Response %s %s", response.status, response.content_type)