perf: build debug log messages lazily so disabled debug logs cost nothing (#27834)

GLOBAL_LOG_LEVEL defaults to INFO, so every log.debug(...) in the backend is discarded, but the message is built first: 187 call sites interpolate their payload into an f-string before the logging call runs, so the work happens on every request and the result is thrown away. The worst one sits in process_chat_payload and stringifies the whole request body, full conversation history included, once per chat completion.

That one line with DEBUG disabled, CPython 3.12:

| conversation | payload | before   | after   |
| ------------ | ------- | -------- | ------- |
| 4 messages   | 1.2 kB  | 3.4 us   | 0.07 us |
| 20 messages  | 17 kB   | 24.8 us  | 0.07 us |
| 60 messages  | 123 kB  | 216.6 us | 0.07 us |

The lazy form log.debug('form_data: %s', form_data) hands the payload to record.getMessage(), which the InterceptHandler only reaches once a record has passed the level check. With DEBUG enabled the emitted lines are byte-identical, f'{x=}' sites included: those map to %r. MistralLoader._debug_log callers get the same treatment, since that wrapper already forwards *args.
This commit is contained in:
Classic298
2026-07-31 19:09:01 -05:00
committed by GitHub
parent b67804f2b6
commit 52cfb02c72
40 changed files with 198 additions and 190 deletions
+22 -20
View File
@@ -310,7 +310,7 @@ class VectorSearchRetriever(BaseRetriever):
def query_doc(collection_name: str, query_embedding: list[float], k: int, user: UserModel = None):
try:
log.debug(f'query_doc:doc {collection_name}')
log.debug('query_doc:doc %s', collection_name)
result = VECTOR_DB_CLIENT.search(
collection_name=collection_name,
vectors=[query_embedding],
@@ -328,7 +328,7 @@ def query_doc(collection_name: str, query_embedding: list[float], k: int, user:
def get_doc(collection_name: str, user: UserModel = None):
try:
log.debug(f'get_doc:doc {collection_name}')
log.debug('get_doc:doc %s', collection_name)
result = VECTOR_DB_CLIENT.get(collection_name=collection_name)
if result:
@@ -457,7 +457,7 @@ async def query_doc_with_native_hybrid_search(
'metadatas': [metadatas],
}
except Exception as e:
log.debug(f'Native hybrid search failed for {collection_name}, falling back to legacy hybrid search: {e}')
log.debug('Native hybrid search failed for %s, falling back to legacy hybrid search: %s', collection_name, e)
return None
@@ -510,7 +510,7 @@ async def query_doc_with_hybrid_search(
log.warning(f'query_doc_with_hybrid_search:no_docs {collection_name}')
return {'documents': [], 'metadatas': [], 'distances': []}
log.debug(f'query_doc_with_hybrid_search:doc {collection_name}')
log.debug('query_doc_with_hybrid_search:doc %s', collection_name)
original_texts = collection_result.documents[0]
bm25_metadatas = [
@@ -707,7 +707,7 @@ async def query_collection(
enable_enriched_texts=config.get('rag.enable_hybrid_search_enriched_texts'),
)
except Exception as e:
log.debug(f'Hybrid search failed, falling back to vector search: {e}')
log.debug('Hybrid search failed, falling back to vector search: %s', e)
results = []
error = False
@@ -736,7 +736,7 @@ async def query_collection(
# Generate all query embeddings (in one call)
query_embeddings = await embedding_function(queries, prefix=RAG_EMBEDDING_QUERY_PREFIX)
log.debug(f'query_collection: processing {len(queries)} queries across {len(collection_names)} collections')
log.debug('query_collection: processing %s queries across %s collections', len(queries), len(collection_names))
task_results = await asyncio.gather(
*[
@@ -866,7 +866,7 @@ def generate_openai_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'generate_openai_batch_embeddings:model {model} batch size: {len(texts)}')
log.debug('generate_openai_batch_embeddings:model %s batch size: %s', model, len(texts))
json_data = {'input': texts, 'model': model}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
json_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -899,7 +899,7 @@ async def agenerate_openai_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'agenerate_openai_batch_embeddings:model {model} batch size: {len(texts)}')
log.debug('agenerate_openai_batch_embeddings:model %s batch size: %s', model, len(texts))
form_data = {'input': texts, 'model': model}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
form_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -937,7 +937,7 @@ def generate_azure_openai_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'generate_azure_openai_batch_embeddings:deployment {model} batch size: {len(texts)}')
log.debug('generate_azure_openai_batch_embeddings:deployment %s batch size: %s', model, len(texts))
json_data = {'input': texts}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
json_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -979,7 +979,7 @@ async def agenerate_azure_openai_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'agenerate_azure_openai_batch_embeddings:deployment {model} batch size: {len(texts)}')
log.debug('agenerate_azure_openai_batch_embeddings:deployment %s batch size: %s', model, len(texts))
form_data = {'input': texts}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
form_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -1018,7 +1018,7 @@ def generate_ollama_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'generate_ollama_batch_embeddings:model {model} batch size: {len(texts)}')
log.debug('generate_ollama_batch_embeddings:model %s batch size: %s', model, len(texts))
json_data = {'input': texts, 'model': model, 'truncate': True}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
json_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -1054,7 +1054,7 @@ async def agenerate_ollama_batch_embeddings(
prefix: str = None,
user: UserModel = None,
) -> list[list[float]]:
log.debug(f'agenerate_ollama_batch_embeddings:model {model} batch size: {len(texts)}')
log.debug('agenerate_ollama_batch_embeddings:model %s batch size: %s', model, len(texts))
form_data = {'input': texts, 'model': model, 'truncate': True}
if isinstance(RAG_EMBEDDING_PREFIX_FIELD_NAME, str) and isinstance(prefix, str):
form_data[RAG_EMBEDDING_PREFIX_FIELD_NAME] = prefix
@@ -1138,7 +1138,7 @@ def get_embedding_function(
batches = [query[i : i + embedding_batch_size] for i in range(0, len(query), embedding_batch_size)]
if enable_async:
log.debug(f'generate_multiple_async: Processing {len(batches)} batches in parallel')
log.debug('generate_multiple_async: Processing %s batches in parallel', len(batches))
# Use semaphore to limit concurrent embedding API requests
# 0 = unlimited (no semaphore)
if concurrent_requests:
@@ -1153,7 +1153,7 @@ def get_embedding_function(
tasks = [embedding_function(batch, prefix=prefix, user=user) for batch in batches]
batch_results = await asyncio.gather(*tasks)
else:
log.debug(f'generate_multiple_async: Processing {len(batches)} batches sequentially')
log.debug('generate_multiple_async: Processing %s batches sequentially', len(batches))
batch_results = []
for batch in batches:
batch_results.append(await embedding_function(batch, prefix=prefix, user=user))
@@ -1166,7 +1166,9 @@ def get_embedding_function(
embeddings.extend(batch_embeddings)
log.debug(
f'generate_multiple_async: Generated {len(embeddings)} embeddings from {len(batches)} parallel batches'
'generate_multiple_async: Generated %s embeddings from %s parallel batches',
len(embeddings),
len(batches),
)
return embeddings
else:
@@ -1606,14 +1608,14 @@ async def get_sources_from_items(
if query_result is None and collection_names:
collection_names = set(collection_names).difference(extracted_collections)
if not collection_names:
log.debug(f'skipping {item} as it has already been extracted')
log.debug('skipping %s as it has already been extracted', item)
continue
# Filter out collections the user cannot read
if user and (item.get('type'), item.get('id')) not in folder_items:
collection_names = await filter_accessible_collections(collection_names, user)
if not collection_names:
log.debug(f'access denied for all collections in item {item}')
log.debug('access denied for all collections in item %s', item)
continue
try:
@@ -1672,8 +1674,8 @@ def get_model_path(model: str, update_model: bool = False):
'local_files_only': local_files_only,
}
log.debug(f'model: {model}')
log.debug(f'snapshot_kwargs: {snapshot_kwargs}')
log.debug('model: %s', model)
log.debug('snapshot_kwargs: %s', snapshot_kwargs)
# Inspiration from upstream sentence_transformers
if os.path.exists(model) or ('\\' in model or model.count('/') > 1) and local_files_only:
@@ -1688,7 +1690,7 @@ def get_model_path(model: str, update_model: bool = False):
# Attempt to query the huggingface_hub library to determine the local path and/or to update
try:
model_repo_path = snapshot_download(**snapshot_kwargs)
log.debug(f'model_repo_path: {model_repo_path}')
log.debug('model_repo_path: %s', model_repo_path)
return model_repo_path
except Exception as e:
log.exception(f'Cannot determine model snapshot path: {e}')