yangdx
commited on
Commit
·
a2c0b5a
1
Parent(s):
5d78930
fix: log filtering void when uvicorn wokers is greater than 1
Browse files- Centralize logging setup
- Fix logger propagation issues
- lightrag/api/lightrag_server.py +55 -31
- lightrag/utils.py +16 -13
lightrag/api/lightrag_server.py
CHANGED
@@ -30,7 +30,6 @@ from lightrag import LightRAG
|
|
30 |
from lightrag.types import GPTKeywordExtractionFormat
|
31 |
from lightrag.api import __api_version__
|
32 |
from lightrag.utils import EmbeddingFunc
|
33 |
-
from lightrag.utils import logger
|
34 |
from .routers.document_routes import (
|
35 |
DocumentManager,
|
36 |
create_document_routes,
|
@@ -40,36 +39,40 @@ from .routers.query_routes import create_query_routes
|
|
40 |
from .routers.graph_routes import create_graph_routes
|
41 |
from .routers.ollama_api import OllamaAPI
|
42 |
|
|
|
|
|
43 |
# Load environment variables
|
44 |
try:
|
45 |
load_dotenv(override=True)
|
46 |
except Exception as e:
|
47 |
-
|
48 |
|
49 |
# Initialize config parser
|
50 |
config = configparser.ConfigParser()
|
51 |
config.read("config.ini")
|
52 |
|
53 |
|
54 |
-
class
|
|
|
55 |
def __init__(self):
|
56 |
super().__init__()
|
57 |
# Define paths to be filtered
|
58 |
self.filtered_paths = ["/documents", "/health", "/webui/"]
|
59 |
-
|
60 |
def filter(self, record):
|
61 |
try:
|
|
|
62 |
if not hasattr(record, "args") or not isinstance(record.args, tuple):
|
63 |
return True
|
64 |
if len(record.args) < 5:
|
65 |
return True
|
66 |
|
|
|
67 |
method = record.args[1]
|
68 |
path = record.args[2]
|
69 |
status = record.args[4]
|
70 |
-
# print(f"Debug - Method: {method}, Path: {path}, Status: {status}")
|
71 |
-
# print(f"Debug - Filtered paths: {self.filtered_paths}")
|
72 |
|
|
|
73 |
if (
|
74 |
method == "GET"
|
75 |
and (status == 200 or status == 304)
|
@@ -78,17 +81,23 @@ class AccessLogFilter(logging.Filter):
|
|
78 |
return False
|
79 |
|
80 |
return True
|
81 |
-
|
82 |
except Exception:
|
|
|
83 |
return True
|
84 |
|
85 |
|
86 |
def create_app(args):
|
87 |
# Initialize verbose debug setting
|
88 |
-
|
89 |
-
|
|
|
|
|
90 |
set_verbose_debug(args.verbose)
|
91 |
|
|
|
|
|
|
|
|
|
92 |
# Verify that bindings are correctly setup
|
93 |
if args.llm_binding not in [
|
94 |
"lollms",
|
@@ -120,11 +129,6 @@ def create_app(args):
|
|
120 |
if not os.path.exists(args.ssl_keyfile):
|
121 |
raise Exception(f"SSL key file not found: {args.ssl_keyfile}")
|
122 |
|
123 |
-
# Setup logging
|
124 |
-
logging.basicConfig(
|
125 |
-
format="%(levelname)s:%(message)s", level=getattr(logging, args.log_level)
|
126 |
-
)
|
127 |
-
|
128 |
# Check if API key is provided either through env var or args
|
129 |
api_key = os.getenv("LIGHTRAG_API_KEY") or args.key
|
130 |
|
@@ -406,6 +410,9 @@ def create_app(args):
|
|
406 |
|
407 |
def get_application():
|
408 |
"""Factory function for creating the FastAPI application"""
|
|
|
|
|
|
|
409 |
# Get args from environment variable
|
410 |
args_json = os.environ.get('LIGHTRAG_ARGS')
|
411 |
if not args_json:
|
@@ -414,24 +421,22 @@ def get_application():
|
|
414 |
import types
|
415 |
args = types.SimpleNamespace(**json.loads(args_json))
|
416 |
|
417 |
-
|
|
|
|
|
418 |
|
|
|
419 |
|
420 |
-
def main():
|
421 |
-
from multiprocessing import freeze_support
|
422 |
-
freeze_support()
|
423 |
-
|
424 |
-
args = parse_args()
|
425 |
-
# Save args to environment variable for child processes
|
426 |
-
os.environ['LIGHTRAG_ARGS'] = json.dumps(vars(args))
|
427 |
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
|
|
|
|
433 |
|
434 |
-
# Configure
|
435 |
logging.config.dictConfig({
|
436 |
"version": 1,
|
437 |
"disable_existing_loggers": False,
|
@@ -452,13 +457,32 @@ def main():
|
|
452 |
"handlers": ["default"],
|
453 |
"level": "INFO",
|
454 |
"propagate": False,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
},
|
456 |
},
|
457 |
})
|
458 |
|
459 |
-
|
460 |
-
|
461 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
|
463 |
display_splash_screen(args)
|
464 |
|
|
|
30 |
from lightrag.types import GPTKeywordExtractionFormat
|
31 |
from lightrag.api import __api_version__
|
32 |
from lightrag.utils import EmbeddingFunc
|
|
|
33 |
from .routers.document_routes import (
|
34 |
DocumentManager,
|
35 |
create_document_routes,
|
|
|
39 |
from .routers.graph_routes import create_graph_routes
|
40 |
from .routers.ollama_api import OllamaAPI
|
41 |
|
42 |
+
from lightrag.utils import logger as utils_logger
|
43 |
+
|
44 |
# Load environment variables
|
45 |
try:
|
46 |
load_dotenv(override=True)
|
47 |
except Exception as e:
|
48 |
+
utils_logger.warning(f"Failed to load .env file: {e}")
|
49 |
|
50 |
# Initialize config parser
|
51 |
config = configparser.ConfigParser()
|
52 |
config.read("config.ini")
|
53 |
|
54 |
|
55 |
+
class LightragPathFilter(logging.Filter):
|
56 |
+
"""Filter for lightrag logger to filter out frequent path access logs"""
|
57 |
def __init__(self):
|
58 |
super().__init__()
|
59 |
# Define paths to be filtered
|
60 |
self.filtered_paths = ["/documents", "/health", "/webui/"]
|
61 |
+
|
62 |
def filter(self, record):
|
63 |
try:
|
64 |
+
# Check if record has the required attributes for an access log
|
65 |
if not hasattr(record, "args") or not isinstance(record.args, tuple):
|
66 |
return True
|
67 |
if len(record.args) < 5:
|
68 |
return True
|
69 |
|
70 |
+
# Extract method, path and status from the record args
|
71 |
method = record.args[1]
|
72 |
path = record.args[2]
|
73 |
status = record.args[4]
|
|
|
|
|
74 |
|
75 |
+
# Filter out successful GET requests to filtered paths
|
76 |
if (
|
77 |
method == "GET"
|
78 |
and (status == 200 or status == 304)
|
|
|
81 |
return False
|
82 |
|
83 |
return True
|
|
|
84 |
except Exception:
|
85 |
+
# In case of any error, let the message through
|
86 |
return True
|
87 |
|
88 |
|
89 |
def create_app(args):
|
90 |
# Initialize verbose debug setting
|
91 |
+
# Can not use the logger at the top of this module when workers > 1
|
92 |
+
from lightrag.utils import set_verbose_debug, logger
|
93 |
+
# Setup logging
|
94 |
+
logger.setLevel(getattr(logging, args.log_level))
|
95 |
set_verbose_debug(args.verbose)
|
96 |
|
97 |
+
# Display splash screen
|
98 |
+
from lightrag.kg.shared_storage import is_multiprocess
|
99 |
+
logger.info(f"==== Multi-processor mode: {is_multiprocess} ====")
|
100 |
+
|
101 |
# Verify that bindings are correctly setup
|
102 |
if args.llm_binding not in [
|
103 |
"lollms",
|
|
|
129 |
if not os.path.exists(args.ssl_keyfile):
|
130 |
raise Exception(f"SSL key file not found: {args.ssl_keyfile}")
|
131 |
|
|
|
|
|
|
|
|
|
|
|
132 |
# Check if API key is provided either through env var or args
|
133 |
api_key = os.getenv("LIGHTRAG_API_KEY") or args.key
|
134 |
|
|
|
410 |
|
411 |
def get_application():
|
412 |
"""Factory function for creating the FastAPI application"""
|
413 |
+
# Configure logging for this worker process
|
414 |
+
configure_logging()
|
415 |
+
|
416 |
# Get args from environment variable
|
417 |
args_json = os.environ.get('LIGHTRAG_ARGS')
|
418 |
if not args_json:
|
|
|
421 |
import types
|
422 |
args = types.SimpleNamespace(**json.loads(args_json))
|
423 |
|
424 |
+
# if args.workers > 1:
|
425 |
+
# from lightrag.kg.shared_storage import initialize_manager
|
426 |
+
# initialize_manager()
|
427 |
|
428 |
+
return create_app(args)
|
429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
430 |
|
431 |
+
def configure_logging():
|
432 |
+
"""Configure logging for both uvicorn and lightrag"""
|
433 |
+
# Reset any existing handlers to ensure clean configuration
|
434 |
+
for logger_name in ["uvicorn.access", "lightrag"]:
|
435 |
+
logger = logging.getLogger(logger_name)
|
436 |
+
logger.handlers = []
|
437 |
+
logger.filters = []
|
438 |
|
439 |
+
# Configure basic logging
|
440 |
logging.config.dictConfig({
|
441 |
"version": 1,
|
442 |
"disable_existing_loggers": False,
|
|
|
457 |
"handlers": ["default"],
|
458 |
"level": "INFO",
|
459 |
"propagate": False,
|
460 |
+
"filters": ["path_filter"],
|
461 |
+
},
|
462 |
+
"lightrag": {
|
463 |
+
"handlers": ["default"],
|
464 |
+
"level": "INFO",
|
465 |
+
"propagate": False,
|
466 |
+
"filters": ["path_filter"],
|
467 |
+
},
|
468 |
+
},
|
469 |
+
"filters": {
|
470 |
+
"path_filter": {
|
471 |
+
"()": "lightrag.api.lightrag_server.LightragPathFilter",
|
472 |
},
|
473 |
},
|
474 |
})
|
475 |
|
476 |
+
def main():
|
477 |
+
from multiprocessing import freeze_support
|
478 |
+
freeze_support()
|
479 |
+
|
480 |
+
args = parse_args()
|
481 |
+
# Save args to environment variable for child processes
|
482 |
+
os.environ['LIGHTRAG_ARGS'] = json.dumps(vars(args))
|
483 |
+
|
484 |
+
# Configure logging before starting uvicorn
|
485 |
+
configure_logging()
|
486 |
|
487 |
display_splash_screen(args)
|
488 |
|
lightrag/utils.py
CHANGED
@@ -55,22 +55,13 @@ def set_verbose_debug(enabled: bool):
|
|
55 |
global VERBOSE_DEBUG
|
56 |
VERBOSE_DEBUG = enabled
|
57 |
|
58 |
-
|
59 |
-
class UnlimitedSemaphore:
|
60 |
-
"""A context manager that allows unlimited access."""
|
61 |
-
|
62 |
-
async def __aenter__(self):
|
63 |
-
pass
|
64 |
-
|
65 |
-
async def __aexit__(self, exc_type, exc, tb):
|
66 |
-
pass
|
67 |
-
|
68 |
-
|
69 |
-
ENCODER = None
|
70 |
-
|
71 |
statistic_data = {"llm_call": 0, "llm_cache": 0, "embed_call": 0}
|
72 |
|
|
|
73 |
logger = logging.getLogger("lightrag")
|
|
|
|
|
|
|
74 |
|
75 |
# Set httpx logging level to WARNING
|
76 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
@@ -97,6 +88,18 @@ def set_logger(log_file: str, level: int = logging.DEBUG):
|
|
97 |
logger.addHandler(file_handler)
|
98 |
|
99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
@dataclass
|
101 |
class EmbeddingFunc:
|
102 |
embedding_dim: int
|
|
|
55 |
global VERBOSE_DEBUG
|
56 |
VERBOSE_DEBUG = enabled
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
statistic_data = {"llm_call": 0, "llm_cache": 0, "embed_call": 0}
|
59 |
|
60 |
+
# Initialize logger
|
61 |
logger = logging.getLogger("lightrag")
|
62 |
+
logger.propagate = False # prevent log message send to root loggger
|
63 |
+
# Let the main application configure the handlers
|
64 |
+
logger.setLevel(logging.INFO)
|
65 |
|
66 |
# Set httpx logging level to WARNING
|
67 |
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
|
88 |
logger.addHandler(file_handler)
|
89 |
|
90 |
|
91 |
+
class UnlimitedSemaphore:
|
92 |
+
"""A context manager that allows unlimited access."""
|
93 |
+
|
94 |
+
async def __aenter__(self):
|
95 |
+
pass
|
96 |
+
|
97 |
+
async def __aexit__(self, exc_type, exc, tb):
|
98 |
+
pass
|
99 |
+
|
100 |
+
|
101 |
+
ENCODER = None
|
102 |
+
|
103 |
@dataclass
|
104 |
class EmbeddingFunc:
|
105 |
embedding_dim: int
|