perf: build info log messages lazily so raising the log level actually saves work (#27837)

Raising GLOBAL_LOG_LEVEL to WARNING buys quieter output but not less work: 241 INFO call sites interpolate their payload into an f-string before the logging call gets to drop it. The heaviest is get_doc, which logs every chunk id and metadata dict in a collection, so on the full-context retrieval path that is the entire knowledge base, once per chat request.

That one line at WARNING, CPython 3.12:

| knowledge base | payload | before   | after   |
| -------------- | ------- | -------- | ------- |
| top-k of 3     | 1.2 kB  | 3.8 us   | 0.07 us |
| 500 chunks     | 201 kB  | 583.6 us | 0.08 us |
| 5000 chunks    | 2.0 MB  | 5.8 ms   | 0.15 us |

The lazy form log.info('query_doc:result %s %s', result.ids, result.metadatas) hands the payload to record.getMessage(), which the InterceptHandler only reaches once a record has passed the level check. Output at INFO is byte-identical. Two sites that already built their message eagerly, one str concat and one % operator, move to the same lazy form.
This commit is contained in:
Classic298 authored and GitHub committed 2026-08-02 15:39:10 -05:00
1 parent 52145eede9
commit 2d18727ab8
48 files changed
+297 -247

No files matched your search

+8 -6
View File
@@ -157,7 +157,7 @@ def convert_audio_to_mp3(file_path):
output_path = os.path.splitext(file_path)[0] + '.mp3'
audio = AudioSegment.from_file(file_path)
audio.export(output_path, format='mp3')
log.info(f'Converted {file_path} to {output_path}')
log.info('Converted %s to %s', file_path, output_path)
return output_path
except Exception as e:
log.error(f'Error converting audio file: {e}')
@@ -208,7 +208,7 @@ def transcode_audio_to_mp3(audio_data: bytes, content_type_header: str, output_p
audio_segment = AudioSegment.from_file(io.BytesIO(audio_data))
audio_segment.export(str(output_path), format='mp3')
log.info(f'Transcoded {mime_type} audio to MP3: {output_path}')
log.info('Transcoded %s audio to MP3: %s', mime_type, output_path)
return True
@@ -631,7 +631,7 @@ async def _transcribe_whisper(request, file_path, languages, file_dir, id):
language=languages[0],
multilingual=WHISPER_MULTILINGUAL,
)
log.info("Detected language '%s' with probability %f" % (info.language, info.language_probability))
log.info("Detected language '%s' with probability %f", info.language, info.language_probability)
return ''.join([segment.text for segment in list(segments)])
transcript = await asyncio.to_thread(_run)
@@ -952,7 +952,9 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir,
try:
model = await Config.get('audio.stt.model') or 'voxtral-mini-latest'
log.info(
f'Mistral STT - model: {model}, method: {"chat_completions" if use_chat_completions else "transcriptions"}'
'Mistral STT - model: %s, method: %s',
model,
'chat_completions' if use_chat_completions else 'transcriptions',
)
session = await get_session()
@@ -1075,7 +1077,7 @@ async def _transcribe_mistral(request, file_path, filename, metadata, file_dir,
async def transcribe(request: Request, file_path: str, metadata: Optional[dict] = None, user=None):
log.info(f'transcribe: {file_path} {metadata}')
log.info('transcribe: %s %s', file_path, metadata)
if BYPASS_PYDUB_PREPROCESSING:
log.info('Bypassing pydub preprocessing (BYPASS_PYDUB_PREPROCESSING=true)')
@@ -1202,7 +1204,7 @@ async def transcription(
status_code=status.HTTP_403_FORBIDDEN,
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
log.info(f'file.content_type: {file.content_type}')
log.info('file.content_type: %s', file.content_type)
stt_supported_content_types = await Config.get('audio.stt.supported_content_types', [])
if not strict_match_mime_type(stt_supported_content_types, file.content_type):
+14 -14
View File
@@ -548,8 +548,8 @@ async def ldap_auth(
]
if ENABLE_LDAP_GROUP_MANAGEMENT:
search_attributes.append(f'{LDAP_ATTRIBUTE_FOR_GROUPS}')
log.info(f'LDAP Group Management enabled. Adding {LDAP_ATTRIBUTE_FOR_GROUPS} to search attributes')
log.info(f'LDAP search attributes: {search_attributes}')
log.info('LDAP Group Management enabled. Adding %s to search attributes', LDAP_ATTRIBUTE_FOR_GROUPS)
log.info('LDAP search attributes: %s', search_attributes)
search_success = await asyncio.to_thread(
connection_app.search,
@@ -586,30 +586,30 @@ async def ldap_auth(
user_groups = []
if ENABLE_LDAP_GROUP_MANAGEMENT and LDAP_ATTRIBUTE_FOR_GROUPS in entry:
group_dns = entry[LDAP_ATTRIBUTE_FOR_GROUPS]
log.info(f'LDAP raw group DNs for user {username_list}: {group_dns}')
log.info('LDAP raw group DNs for user %s: %s', username_list, group_dns)
if group_dns:
log.info(f'LDAP group_dns original: {group_dns}')
log.info(f'LDAP group_dns type: {type(group_dns)}')
log.info(f'LDAP group_dns length: {len(group_dns)}')
log.info('LDAP group_dns original: %s', group_dns)
log.info('LDAP group_dns type: %s', type(group_dns))
log.info('LDAP group_dns length: %s', len(group_dns))
if hasattr(group_dns, 'value'):
group_dns = group_dns.value
log.info(f'Extracted .value property: {group_dns}')
log.info('Extracted .value property: %s', group_dns)
elif hasattr(group_dns, '__iter__') and not isinstance(group_dns, (str, bytes)):
group_dns = list(group_dns)
log.info(f'Converted to list: {group_dns}')
log.info('Converted to list: %s', group_dns)
if isinstance(group_dns, list):
group_dns = [str(item) for item in group_dns]
else:
group_dns = [str(group_dns)]
log.info(f'LDAP group_dns after processing - type: {type(group_dns)}, length: {len(group_dns)}')
log.info('LDAP group_dns after processing - type: %s, length: %s', type(group_dns), len(group_dns))
for group_idx, group_dn in enumerate(group_dns):
group_dn = str(group_dn)
log.info(f'Processing group DN #{group_idx + 1}: {group_dn}')
log.info('Processing group DN #%s: %s', group_idx + 1, group_dn)
try:
group_cn = extract_group_cn_from_dn(group_dn)
@@ -621,9 +621,9 @@ async def ldap_auth(
except Exception as e:
log.warning(f'Failed to extract group name from DN {group_dn}: {e}')
log.info(f'LDAP groups for user {username_list}: {user_groups} (total: {len(user_groups)})')
log.info('LDAP groups for user %s: %s (total: %s)', username_list, user_groups, len(user_groups))
else:
log.info(f'No groups found for user {username_list}')
log.info('No groups found for user %s', username_list)
elif ENABLE_LDAP_GROUP_MANAGEMENT:
log.warning(
f'LDAP Group Management enabled but {LDAP_ATTRIBUTE_FOR_GROUPS} attribute not found in user entry'
@@ -691,7 +691,7 @@ async def ldap_auth(
if ENABLE_LDAP_GROUP_CREATION:
await Groups.create_groups_by_group_names(user.id, user_groups, db=db)
await Groups.sync_groups_by_group_names(user.id, user_groups, db=db)
log.info(f'Successfully synced groups for user {user.id}: {user_groups}')
log.info('Successfully synced groups for user %s: %s', user.id, user_groups)
except Exception as e:
log.error(f'Failed to sync groups for user {user.id}: {e}')
@@ -1156,7 +1156,7 @@ async def get_admin_details(
admin_email = await Config.get('auth.admin.email')
admin_name = None
log.info(f'Admin details - Email: {admin_email}, Name: {admin_name}')
log.info('Admin details - Email: %s, Name: %s', admin_email, admin_name)
if admin_email:
admin = await Users.get_user_by_email(admin_email, db=db)
+1 -1
View File
@@ -575,7 +575,7 @@ async def verify_tool_servers_config(request: Request, form_data: ToolServerConn
'oauth_server_metadata': oauth_server_metadata.model_dump(mode='json'),
}
except Exception as e:
log.info(f'Failed to parse OAuth 2.1 discovery document: {e}')
log.info('Failed to parse OAuth 2.1 discovery document: %s', e)
raise HTTPException(
status_code=400,
detail=f'Failed to parse OAuth 2.1 discovery document from {discovery_url}',
+5 -5
View File
@@ -177,7 +177,7 @@ async def process_uploaded_file(
# processing (Tools, vision models). Attempting text
# extraction causes "Timeout reached while detecting
# encoding" errors.
log.info(f'Video file detected ({content_type}), skipping text extraction')
log.info('Video file detected (%s), skipping text extraction', content_type)
await Files.update_file_data_by_id(
file_item.id,
{'status': 'completed'},
@@ -190,7 +190,7 @@ async def process_uploaded_file(
# Documents, or media files explicitly enabled for the
# configured content extraction engine.
if not content_type:
log.info(f'File type {file.content_type} is not provided, but trying to process anyway')
log.info('File type %s is not provided, but trying to process anyway', file.content_type)
await process_file(
request,
ProcessFileForm(file_id=file_item.id),
@@ -242,7 +242,7 @@ async def process_uploaded_file(
)
if not knowledge_file:
raise Exception(f'Failed to link file {file_item.id} to knowledge {knowledge_id}')
log.info(f'Linked file {file_item.id} to knowledge {knowledge_id}')
log.info('Linked file %s to knowledge %s', file_item.id, knowledge_id)
except Exception as e:
log.warning(f'Failed to link file {file_item.id} to knowledge {knowledge_id}: {e}')
raise
@@ -322,7 +322,7 @@ async def upload_file_handler(
background_tasks: Optional[BackgroundTasks] = None,
db: Optional[AsyncSession] = None,
):
log.info(f'file.content_type: {file.content_type} {process}')
log.info('file.content_type: %s %s', file.content_type, process)
if isinstance(metadata, str):
try:
@@ -868,7 +868,7 @@ async def get_html_file_content_by_id(
# Check if the file already exists in the cache
if file_path.is_file():
log.info(f'file_path: {file_path}')
log.info('file_path: %s', file_path)
return FileResponse(file_path)
else:
raise HTTPException(
+1 -1
View File
@@ -170,7 +170,7 @@ def get_image_file_item(base64_string, param_name='image'):
async def set_image_model(request: Request, model: str):
log.info(f'Setting image model to {model}')
log.info('Setting image model to %s', model)
await Config.upsert({'image_generation.model': model})
image_config = await get_image_config()
if image_config.IMAGE_GENERATION_ENGINE in ['', 'automatic1111']:
+15 -10
View File
@@ -351,7 +351,7 @@ async def reindex_knowledge_files(
failed_files = []
start_time = time.monotonic()
log.info(f'Starting reindexing for {len(knowledge_bases)} knowledge bases ({total_files} files)')
log.info('Starting reindexing for %s knowledge bases (%s files)', len(knowledge_bases), total_files)
for kb_idx, (knowledge_base, files) in enumerate(knowledge_base_files, start=1):
try:
@@ -371,8 +371,13 @@ async def reindex_knowledge_files(
eta = f', ETA: {round(elapsed / (processed_files - 1) * remaining_files)}s'
log.info(
f'Reindexing knowledge base {kb_idx}/{len(knowledge_bases)} '
f'file {processed_files}/{total_files}{eta}: {file.filename}'
'Reindexing knowledge base %s/%s file %s/%s%s: %s',
kb_idx,
len(knowledge_bases),
processed_files,
total_files,
eta,
file.filename,
)
try:
@@ -397,7 +402,7 @@ async def reindex_knowledge_files(
for failed in failed_files:
log.warning(f'File ID: {failed["file_id"]}, Error: {failed["error"]}')
log.info(f'Reindexing completed in {round(time.monotonic() - start_time)}s.')
log.info('Reindexing completed in %ss.', round(time.monotonic() - start_time))
await publish_event(
request,
EVENTS.KNOWLEDGE_REINDEXED,
@@ -426,14 +431,14 @@ async def reindex_knowledge_base_metadata_embeddings(
this entire operation would exhaust the connection pool.
"""
knowledge_bases = await Knowledges.get_knowledge_bases()
log.info(f'Reindexing embeddings for {len(knowledge_bases)} knowledge bases')
log.info('Reindexing embeddings for %s knowledge bases', len(knowledge_bases))
success_count = 0
for kb in knowledge_bases:
if await embed_knowledge_base_metadata(request, kb.id, kb.name, kb.description):
success_count += 1
log.info(f'Embedding reindex complete: {success_count}/{len(knowledge_bases)}')
log.info('Embedding reindex complete: %s/%s', success_count, len(knowledge_bases))
return {'total': len(knowledge_bases), 'success': success_count}
@@ -1687,11 +1692,11 @@ async def delete_knowledge_by_id(
detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
)
log.info(f'Deleting knowledge base: {id} (name: {knowledge.name})')
log.info('Deleting knowledge base: %s (name: %s)', id, knowledge.name)
# Get all models
models = await Models.get_all_models(db=db)
log.info(f'Found {len(models)} models to check for knowledge base {id}')
log.info('Found %s models to check for knowledge base %s', len(models), id)
# Update models that reference this knowledge base
for model in models:
@@ -1702,7 +1707,7 @@ async def delete_knowledge_by_id(
# If the knowledge list changed, update the model
if len(updated_knowledge) != len(knowledge_list):
log.info(f'Updating model {model.id} to remove knowledge base {id}')
log.info('Updating model %s to remove knowledge base %s', model.id, id)
model.meta.knowledge = updated_knowledge
model_form = ModelForm(**model.model_dump())
await Models.update_model_by_id(model.id, model_form, db=db)
@@ -2022,7 +2027,7 @@ async def add_files_to_knowledge_batch(
)
# Batch-fetch all files to avoid N+1 queries
log.info(f'files/batch/add - {len(form_data)} files')
log.info('files/batch/add - %s files', len(form_data))
file_ids = [form.file_id for form in form_data]
files = await Files.get_files_by_ids(file_ids, db=db)
+7 -7
View File
@@ -650,7 +650,7 @@ async def pull_model(
form_data['model'] = form_data.get('model', form_data.get('name'))
url = (await Config.get('ollama.base_urls', []))[url_idx]
log.info(f'url: {url}')
log.info('url: %s', url)
# Admins may pull from any registry
return await send_request(
@@ -881,7 +881,7 @@ async def embed(
if not await Config.get('ollama.enable'):
raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED)
log.info(f'generate_ollama_batch_embeddings {form_data}')
log.info('generate_ollama_batch_embeddings %s', form_data)
await check_model_access(user, await Models.get_model_by_id(form_data.model), BYPASS_MODEL_ACCESS_CONTROL)
await validate_ollama_backend_idx(request, form_data.model, url_idx, user)
@@ -932,7 +932,7 @@ async def embeddings(
if not await Config.get('ollama.enable'):
raise HTTPException(status_code=503, detail=ERROR_MESSAGES.OLLAMA_API_DISABLED)
log.info(f'generate_ollama_embeddings {form_data}')
log.info('generate_ollama_embeddings %s', form_data)
await check_model_access(user, await Models.get_model_by_id(form_data.model), BYPASS_MODEL_ACCESS_CONTROL)
await validate_ollama_backend_idx(request, form_data.model, url_idx, user)
@@ -1637,11 +1637,11 @@ async def upload_model(
async def file_process_stream():
nonlocal ollama_url
total_size = os.path.getsize(file_path)
log.info(f'Total Model Size: {total_size}')
log.info('Total Model Size: %s', total_size)
# Stage 2: hash the file and emit SSE progress
file_hash = await asyncio.to_thread(calculate_sha256, file_path, chunk_size)
log.info(f'Model Hash: {file_hash}')
log.info('Model Hash: %s', file_hash)
try:
bytes_read = 0
@@ -1675,13 +1675,13 @@ async def upload_model(
# Stage 4: create the model
model, _ext = os.path.splitext(filename)
log.info(f'Created Model: {model}')
log.info('Created Model: %s', model)
create_payload = {
'model': model,
'files': {filename: f'sha256:{file_hash}'},
}
log.info(f'Model Payload: {create_payload}')
log.info('Model Payload: %s', create_payload)
async with session.post(
f'{ollama_url}/api/create',
+1 -1
View File
@@ -221,7 +221,7 @@ async def upload_pipeline(
file: UploadFile = File(...),
user=Depends(get_admin_user),
):
log.info(f'upload_pipeline: urlIdx={urlIdx}, filename={file.filename}')
log.info('upload_pipeline: urlIdx=%s, filename=%s', urlIdx, file.filename)
filename = os.path.basename(file.filename)
# Check if the uploaded file is a python file
+12 -12
View File
@@ -519,7 +519,7 @@ async def unload_embedding_model(request: Request):
@router.post('/embedding/update')
async def update_embedding_config(request: Request, form_data: EmbeddingModelUpdateForm, user=Depends(get_admin_user)):
config = await get_retrieval_config()
log.info(f'Updating embedding model: {config.RAG_EMBEDDING_MODEL} to {form_data.RAG_EMBEDDING_MODEL}')
log.info('Updating embedding model: %s to %s', config.RAG_EMBEDDING_MODEL, form_data.RAG_EMBEDDING_MODEL)
await unload_embedding_model(request)
try:
config.RAG_EMBEDDING_ENGINE = form_data.RAG_EMBEDDING_ENGINE
@@ -1159,7 +1159,7 @@ async def update_rag_config(request: Request, form_data: ConfigForm, user=Depend
else config.RAG_RERANKING_BATCH_SIZE
)
log.info(f'Updating reranking model: {config.RAG_RERANKING_MODEL} to {form_data.RAG_RERANKING_MODEL}')
log.info('Updating reranking model: %s to %s', config.RAG_RERANKING_MODEL, form_data.RAG_RERANKING_MODEL)
try:
config.RAG_RERANKING_MODEL = (
form_data.RAG_RERANKING_MODEL if form_data.RAG_RERANKING_MODEL is not None else config.RAG_RERANKING_MODEL
@@ -1663,7 +1663,7 @@ def save_docs_to_vector_db(
existing_file_id = result.metadatas[0][0].get('file_id')
if existing_file_id != metadata.get('file_id'):
log.info(f'Document with hash {metadata["hash"]} already exists')
log.info('Document with hash %s already exists', metadata['hash'])
raise ValueError(ERROR_MESSAGES.DUPLICATE_CONTENT)
if split:
@@ -1706,7 +1706,7 @@ def save_docs_to_vector_db(
)
docs = text_splitter.split_documents(docs)
elif config.TEXT_SPLITTER == 'token':
log.info(f'Using token text splitter: {config.TIKTOKEN_ENCODING_NAME}')
log.info('Using token text splitter: %s', config.TIKTOKEN_ENCODING_NAME)
tiktoken.get_encoding(str(config.TIKTOKEN_ENCODING_NAME))
text_splitter = TokenTextSplitter(
@@ -1748,16 +1748,16 @@ def save_docs_to_vector_db(
try:
if VECTOR_DB_CLIENT.has_collection(collection_name=collection_name):
log.info(f'collection {collection_name} already exists')
log.info('collection %s already exists', collection_name)
if overwrite:
VECTOR_DB_CLIENT.delete_collection(collection_name=collection_name)
log.info(f'deleting existing collection {collection_name}')
log.info('deleting existing collection %s', collection_name)
elif add is False:
log.info(f'collection {collection_name} already exists, overwrite is False and add is False')
log.info('collection %s already exists, overwrite is False and add is False', collection_name)
return True
log.info(f'generating embeddings for {collection_name}')
log.info('generating embeddings for %s', collection_name)
embedding_function = get_embedding_function(
config.RAG_EMBEDDING_ENGINE,
config.RAG_EMBEDDING_MODEL,
@@ -1801,7 +1801,7 @@ def save_docs_to_vector_db(
request.app.state.main_loop,
)
embeddings = future.result(timeout=embedding_timeout)
log.info(f'embeddings generated {len(embeddings)} for {len(texts)} items')
log.info('embeddings generated %s for %s items', len(embeddings), len(texts))
items = [
{
@@ -1813,13 +1813,13 @@ def save_docs_to_vector_db(
for idx, text in enumerate(texts)
]
log.info(f'adding to collection {collection_name}')
log.info('adding to collection %s', collection_name)
VECTOR_DB_CLIENT.insert(
collection_name=collection_name,
items=items,
)
log.info(f'added {len(items)} items to collection {collection_name}')
log.info('added %s items to collection %s', len(items), collection_name)
return True
except Exception as e:
log.exception(e)
@@ -2011,7 +2011,7 @@ async def process_file(
add=(True if form_data.collection_name else False),
user=user,
)
log.info(f'added {len(docs)} items to collection {collection_name}')
log.info('added %s items to collection %s', len(docs), collection_name)
if result:
# Fresh session for the final update.
+1 -1
View File
@@ -258,7 +258,7 @@ def get_scim_auth(request: Request, authorization: Optional[str] = Header(None))
# Check if SCIM is enabled
enable_scim = getattr(request.app.state, 'ENABLE_SCIM', False)
log.info(f'SCIM auth check - raw ENABLE_SCIM: {enable_scim}, type: {type(enable_scim)}')
log.info('SCIM auth check - raw ENABLE_SCIM: %s, type: %s', enable_scim, type(enable_scim))
if not enable_scim:
raise HTTPException(
+1 -1
View File
@@ -412,7 +412,7 @@ async def generate_queries(request: Request, form_data: dict, user=Depends(get_v
)
if getattr(request.state, 'cached_queries', None):
log.info(f'Reusing cached queries: {request.state.cached_queries}')
log.info('Reusing cached queries: %s', request.state.cached_queries)
return request.state.cached_queries
if getattr(request.state, 'direct', False) and hasattr(request.state, 'model'):