Logging and Instrumentation#
RAGFlow uses a multi-layer logging system built around a central utility in common/log_utils.py, covering rotating file handlers, environment-based log-level configuration, live admin-API controls, and per-step timing instrumentation in the chat pipeline and search backends.
Logger Initialization#
init_root_logger(logfile_basename) is the single entry point for logger setup. It:
- Configures a
RotatingFileHandlerwriting to<project_root>/logs/<basename>.logwith a 10 MB max size and 5 backup files. - Attaches a
StreamHandlerso the same output goes to stdout/stderr (visible viadocker logs). - Calls
logging.captureWarnings(True)to route Pythonwarningsthrough the logging framework . - Reads the
LOG_LEVELSenvironment variable (comma-separatedpkg=LEVELpairs) and applies each to the named logger . - Defaults
peeweeandpdfminertoWARNINGand the root logger toINFOunless overridden .
init_root_logger is called once per service process at startup:
| Process | Log file basename |
|---|---|
api/ragflow_server.py | ragflow_server |
admin/server/admin_server.py | admin_service |
rag/svr/task_executor.py | task_executor_<type>_<idx> |
rag/svr/sync_data_source.py | <consumer_name> |
Container Log Volumes#
The ragflow-cpu / ragflow-gpu services in docker/docker-compose.yml mount ./ragflow-logs:/ragflow/logs, so file-based logs are always accessible on the host regardless of container lifecycle.
Environment-Based Log Levels#
Set LOG_LEVELS in docker/.env before starting containers. Examples:
# Change ragflow.es_conn to DEBUG
LOG_LEVELS=ragflow.es_conn=DEBUG
# Multiple packages
LOG_LEVELS=ragflow.es_conn=DEBUG,peewee=INFO
Valid level names: DEBUG, INFO (default), WARNING, ERROR .
Runtime Admin API Controls#
Log levels can be changed without restarting via the admin server's REST endpoints, which call get_log_levels() and set_log_level(pkg_name, level) from common/log_utils.py.
| Endpoint | Method | Description |
|---|---|---|
/api/v1/admin/log_levels | GET | Return all current per-package log levels |
/api/v1/admin/log_levels | PUT | Set level for one package; body: {"pkg_name": "...", "level": "DEBUG"} |
Both routes require admin authentication (@check_admin_auth). The in-memory pkg_levels dict is updated alongside the live logger, so a subsequent GET reflects the change immediately .
Chat Pipeline Timing Instrumentation#
api/db/services/dialog_service.py uses timeit.default_timer for microsecond-precision wall-clock measurements across the async chat pipeline . Timestamps are captured after each major stage:
| Variable | Pipeline stage |
|---|---|
chat_start_ts | Start of async_chat |
check_llm_ts | After LLM config resolved |
check_langfuse_tracer_ts | After Langfuse tracer checked |
bind_models_ts | After embedding/rerank/chat/TTS models bound |
refine_question_ts | After question rewriting/keyword extraction |
retrieval_ts | After chunk retrieval |
finish_chat_ts | After LLM answer generation |
All deltas are calculated in milliseconds and appended to the prompt field of every chat response under a ## Time elapsed: section , including token count and generation speed (tokens/s). This data is also forwarded to Langfuse if a tracer is configured .
Search Backend Timing (OceanBase)#
The OceanBase connector in rag/utils/ob_conn.py logs per-query latency using time.time() around each SQL search call :
OBConnection.search table <index>, search type: hybrid, elapsed time: 0.123 seconds, got count: 42
This is consistent across fusion, vector, fulltext, and filter search variants . The pattern originates in the base class _execute_search_sql which returns (rows, elapsed_time) and is logged at INFO level in the caller.
The Elasticsearch/OpenSearch path in rag/nlp/search.py uses logging.debug for result counts (e.g., Dealer.search TOTAL: ...) but does not instrument per-query latency.
Helper Utilities#
log_exception(e, *args)— logs the exception withlogging.exception, then logs and re-raises with additional context (e.g., HTTP response.text) from each extra argument. Use it in request handlers to ensure full tracebacks appear in logs.pkg_levelsdict — module-level dict that the admin API updates in-place; the Python logging framework reads the logger's level directly from theLoggerobject, soset_log_levelmust also calllogger.setLevel().