Commit 41856690519fed26bb9e35095fd8e72d54316497
1 parent
0ea456b2
embedding logs
Showing
4 changed files
with
50 additions
and
107 deletions
Show diff stats
embeddings/server.py
| ... | ... | @@ -14,7 +14,6 @@ import time |
| 14 | 14 | import uuid |
| 15 | 15 | from collections import deque |
| 16 | 16 | from dataclasses import dataclass |
| 17 | -from logging.handlers import TimedRotatingFileHandler | |
| 18 | 17 | from typing import Any, Dict, List, Optional |
| 19 | 18 | |
| 20 | 19 | import numpy as np |
| ... | ... | @@ -44,9 +43,7 @@ def configure_embedding_logging() -> None: |
| 44 | 43 | return |
| 45 | 44 | |
| 46 | 45 | log_dir = pathlib.Path("logs") |
| 47 | - verbose_dir = log_dir / "verbose" | |
| 48 | 46 | log_dir.mkdir(exist_ok=True) |
| 49 | - verbose_dir.mkdir(parents=True, exist_ok=True) | |
| 50 | 47 | |
| 51 | 48 | log_level = os.getenv("LOG_LEVEL", "INFO").upper() |
| 52 | 49 | numeric_level = getattr(logging, log_level, logging.INFO) |
| ... | ... | @@ -56,47 +53,18 @@ def configure_embedding_logging() -> None: |
| 56 | 53 | request_filter = _DefaultRequestIdFilter() |
| 57 | 54 | |
| 58 | 55 | root_logger.setLevel(numeric_level) |
| 59 | - | |
| 60 | - file_handler = TimedRotatingFileHandler( | |
| 61 | - filename=log_dir / "embedding_api.log", | |
| 62 | - when="midnight", | |
| 63 | - interval=1, | |
| 64 | - backupCount=30, | |
| 65 | - encoding="utf-8", | |
| 66 | - ) | |
| 67 | - file_handler.setLevel(numeric_level) | |
| 68 | - file_handler.setFormatter(formatter) | |
| 69 | - file_handler.addFilter(request_filter) | |
| 70 | - root_logger.addHandler(file_handler) | |
| 71 | - | |
| 72 | - error_handler = TimedRotatingFileHandler( | |
| 73 | - filename=log_dir / "embedding_api_error.log", | |
| 74 | - when="midnight", | |
| 75 | - interval=1, | |
| 76 | - backupCount=30, | |
| 77 | - encoding="utf-8", | |
| 78 | - ) | |
| 79 | - error_handler.setLevel(logging.ERROR) | |
| 80 | - error_handler.setFormatter(formatter) | |
| 81 | - error_handler.addFilter(request_filter) | |
| 82 | - root_logger.addHandler(error_handler) | |
| 56 | + root_logger.handlers.clear() | |
| 57 | + stream_handler = logging.StreamHandler() | |
| 58 | + stream_handler.setLevel(numeric_level) | |
| 59 | + stream_handler.setFormatter(formatter) | |
| 60 | + stream_handler.addFilter(request_filter) | |
| 61 | + root_logger.addHandler(stream_handler) | |
| 83 | 62 | |
| 84 | 63 | verbose_logger = logging.getLogger("embedding.verbose") |
| 85 | 64 | verbose_logger.setLevel(numeric_level) |
| 86 | 65 | verbose_logger.handlers.clear() |
| 87 | - verbose_logger.propagate = False | |
| 88 | - | |
| 89 | - verbose_handler = TimedRotatingFileHandler( | |
| 90 | - filename=verbose_dir / "embedding_verbose.log", | |
| 91 | - when="midnight", | |
| 92 | - interval=1, | |
| 93 | - backupCount=30, | |
| 94 | - encoding="utf-8", | |
| 95 | - ) | |
| 96 | - verbose_handler.setLevel(numeric_level) | |
| 97 | - verbose_handler.setFormatter(formatter) | |
| 98 | - verbose_handler.addFilter(request_filter) | |
| 99 | - verbose_logger.addHandler(verbose_handler) | |
| 66 | + # Consolidate verbose logs into the main embedding log stream. | |
| 67 | + verbose_logger.propagate = True | |
| 100 | 68 | |
| 101 | 69 | root_logger._embedding_logging_configured = True # type: ignore[attr-defined] |
| 102 | 70 | ... | ... |
frontend/static/css/style.css
| ... | ... | @@ -379,7 +379,7 @@ body { |
| 379 | 379 | margin-top: 8px; |
| 380 | 380 | } |
| 381 | 381 | |
| 382 | -.product-debug-inline-es-btn { | |
| 382 | +.product-debug-inline-result-btn { | |
| 383 | 383 | font-family: inherit; |
| 384 | 384 | font-size: 12px; |
| 385 | 385 | padding: 4px 10px; |
| ... | ... | @@ -390,27 +390,22 @@ body { |
| 390 | 390 | cursor: pointer; |
| 391 | 391 | } |
| 392 | 392 | |
| 393 | -.product-debug-inline-es-btn:hover { | |
| 393 | +.product-debug-inline-result-btn:hover { | |
| 394 | 394 | background: #f0f0f0; |
| 395 | 395 | border-color: #bbb; |
| 396 | 396 | } |
| 397 | 397 | |
| 398 | -.product-debug--es-expanded { | |
| 398 | +.product-debug--result-expanded { | |
| 399 | 399 | max-height: min(70vh, 720px); |
| 400 | 400 | } |
| 401 | 401 | |
| 402 | -.product-es-doc-panel { | |
| 402 | +.product-result-doc-panel { | |
| 403 | 403 | margin-top: 10px; |
| 404 | 404 | padding-top: 8px; |
| 405 | 405 | border-top: 1px dashed #e8e8e8; |
| 406 | 406 | } |
| 407 | 407 | |
| 408 | -.product-es-doc-panel-status { | |
| 409 | - font-size: 12px; | |
| 410 | - color: #888; | |
| 411 | -} | |
| 412 | - | |
| 413 | -.product-es-doc-pre { | |
| 408 | +.product-result-doc-pre { | |
| 414 | 409 | margin: 6px 0 0; |
| 415 | 410 | padding: 10px; |
| 416 | 411 | background: #f5f5f5; | ... | ... |
frontend/static/js/app.js
| ... | ... | @@ -68,25 +68,25 @@ function initializeApp() { |
| 68 | 68 | // 初始化租户下拉框和分面面板 |
| 69 | 69 | console.log('Initializing app...'); |
| 70 | 70 | initTenantSelect(); |
| 71 | - setupProductGridEsDocToggle(); | |
| 71 | + setupProductGridResultDocToggle(); | |
| 72 | 72 | const searchInput = document.getElementById('searchInput'); |
| 73 | 73 | if (searchInput) { |
| 74 | 74 | searchInput.focus(); |
| 75 | 75 | } |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | -/** Delegated handler: toggle inline ES raw response under each result card (survives innerHTML refresh on re-search). */ | |
| 79 | -function setupProductGridEsDocToggle() { | |
| 78 | +/** Delegated handler: toggle inline current result JSON under each result card (survives innerHTML refresh on re-search). */ | |
| 79 | +function setupProductGridResultDocToggle() { | |
| 80 | 80 | const grid = document.getElementById('productGrid'); |
| 81 | - if (!grid || grid.dataset.esDocToggleBound === '1') { | |
| 81 | + if (!grid || grid.dataset.resultDocToggleBound === '1') { | |
| 82 | 82 | return; |
| 83 | 83 | } |
| 84 | - grid.dataset.esDocToggleBound = '1'; | |
| 85 | - grid.addEventListener('click', onProductGridEsDocToggleClick); | |
| 84 | + grid.dataset.resultDocToggleBound = '1'; | |
| 85 | + grid.addEventListener('click', onProductGridResultDocToggleClick); | |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | -async function onProductGridEsDocToggleClick(event) { | |
| 89 | - const btn = event.target.closest('[data-action="toggle-es-inline-doc"]'); | |
| 88 | +function onProductGridResultDocToggleClick(event) { | |
| 89 | + const btn = event.target.closest('[data-action="toggle-result-inline-doc"]'); | |
| 90 | 90 | if (!btn) { |
| 91 | 91 | return; |
| 92 | 92 | } |
| ... | ... | @@ -95,55 +95,27 @@ async function onProductGridEsDocToggleClick(event) { |
| 95 | 95 | if (!debugRoot) { |
| 96 | 96 | return; |
| 97 | 97 | } |
| 98 | - const panel = debugRoot.querySelector('.product-es-doc-panel'); | |
| 99 | - const pre = debugRoot.querySelector('.product-es-doc-pre'); | |
| 100 | - const statusEl = debugRoot.querySelector('.product-es-doc-panel-status'); | |
| 101 | - if (!panel || !pre || !statusEl) { | |
| 98 | + const panel = debugRoot.querySelector('.product-result-doc-panel'); | |
| 99 | + const pre = debugRoot.querySelector('.product-result-doc-pre'); | |
| 100 | + if (!panel || !pre) { | |
| 102 | 101 | return; |
| 103 | 102 | } |
| 104 | 103 | |
| 105 | - const spuId = btn.getAttribute('data-spu-id') || ''; | |
| 106 | - const tenantId = getTenantId(); | |
| 107 | - const url = `${API_BASE_URL}/search/es-doc/${encodeURIComponent(spuId)}?tenant_id=${encodeURIComponent(tenantId)}`; | |
| 108 | - | |
| 109 | - if (debugRoot.dataset.esInlineOpen === '1') { | |
| 104 | + if (debugRoot.dataset.resultInlineOpen === '1') { | |
| 110 | 105 | panel.setAttribute('hidden', ''); |
| 111 | - debugRoot.classList.remove('product-debug--es-expanded'); | |
| 112 | - debugRoot.dataset.esInlineOpen = '0'; | |
| 113 | - btn.textContent = '在结果中显示 ES 文档'; | |
| 106 | + debugRoot.classList.remove('product-debug--result-expanded'); | |
| 107 | + debugRoot.dataset.resultInlineOpen = '0'; | |
| 108 | + btn.textContent = '在结果中显示当前结果数据'; | |
| 114 | 109 | return; |
| 115 | 110 | } |
| 116 | 111 | |
| 117 | 112 | panel.removeAttribute('hidden'); |
| 118 | - debugRoot.classList.add('product-debug--es-expanded'); | |
| 119 | - debugRoot.dataset.esInlineOpen = '1'; | |
| 120 | - btn.textContent = '隐藏 ES 文档'; | |
| 121 | - | |
| 122 | - if (pre.textContent.length > 0) { | |
| 123 | - panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); | |
| 124 | - return; | |
| 125 | - } | |
| 126 | - | |
| 127 | - statusEl.style.display = ''; | |
| 128 | - statusEl.textContent = '加载中…'; | |
| 129 | - pre.style.display = 'none'; | |
| 130 | - | |
| 131 | - try { | |
| 132 | - const response = await fetch(url); | |
| 133 | - if (!response.ok) { | |
| 134 | - const errText = await response.text(); | |
| 135 | - throw new Error(`HTTP ${response.status}: ${errText.slice(0, 200)}`); | |
| 136 | - } | |
| 137 | - const data = await response.json(); | |
| 138 | - pre.textContent = customStringify(data); | |
| 139 | - statusEl.style.display = 'none'; | |
| 140 | - pre.style.display = 'block'; | |
| 141 | - } catch (err) { | |
| 142 | - console.error('ES doc fetch failed', err); | |
| 143 | - statusEl.textContent = `加载失败: ${err.message || err}`; | |
| 144 | - pre.style.display = 'none'; | |
| 113 | + debugRoot.classList.add('product-debug--result-expanded'); | |
| 114 | + debugRoot.dataset.resultInlineOpen = '1'; | |
| 115 | + btn.textContent = '隐藏当前结果数据'; | |
| 116 | + if (pre.textContent.length === 0) { | |
| 117 | + pre.textContent = btn.getAttribute('data-result-json') || '{}'; | |
| 145 | 118 | } |
| 146 | - | |
| 147 | 119 | panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); |
| 148 | 120 | } |
| 149 | 121 | |
| ... | ... | @@ -213,7 +185,7 @@ function initTenantSelect() { |
| 213 | 185 | }); |
| 214 | 186 | // 设置默认值(仅当输入框为空时) |
| 215 | 187 | if (!tenantSelect.value.trim()) { |
| 216 | - tenantSelect.value = availableTenants.includes('170') ? '170' : availableTenants[0]; | |
| 188 | + tenantSelect.value = availableTenants.includes('0') ? '0' : availableTenants[0]; | |
| 217 | 189 | } |
| 218 | 190 | } |
| 219 | 191 | |
| ... | ... | @@ -462,6 +434,7 @@ function displayResults(data) { |
| 462 | 434 | }); |
| 463 | 435 | } |
| 464 | 436 | |
| 437 | + const resultJson = customStringify(result); | |
| 465 | 438 | const rawUrl = `${API_BASE_URL}/search/es-doc/${encodeURIComponent(spuId)}?tenant_id=${encodeURIComponent(tenantId)}`; |
| 466 | 439 | |
| 467 | 440 | debugHtml = ` |
| ... | ... | @@ -475,18 +448,17 @@ function displayResults(data) { |
| 475 | 448 | <div class="product-debug-line">Fused score: ${fusedScore}</div> |
| 476 | 449 | ${titleLines} |
| 477 | 450 | <div class="product-debug-actions"> |
| 478 | - <button type="button" class="product-debug-inline-es-btn" | |
| 479 | - data-action="toggle-es-inline-doc" | |
| 480 | - data-spu-id="${escapeAttr(String(spuId || ''))}"> | |
| 481 | - 在结果中显示 ES 文档 | |
| 451 | + <button type="button" class="product-debug-inline-result-btn" | |
| 452 | + data-action="toggle-result-inline-doc" | |
| 453 | + data-result-json="${escapeAttr(resultJson)}"> | |
| 454 | + 在结果中显示当前结果数据 | |
| 482 | 455 | </button> |
| 483 | 456 | <a class="product-debug-link" href="${rawUrl}" target="_blank" rel="noopener noreferrer"> |
| 484 | 457 | 查看 ES 原始文档 |
| 485 | 458 | </a> |
| 486 | 459 | </div> |
| 487 | - <div class="product-es-doc-panel" hidden> | |
| 488 | - <div class="product-es-doc-panel-status"></div> | |
| 489 | - <pre class="product-es-doc-pre"></pre> | |
| 460 | + <div class="product-result-doc-panel" hidden> | |
| 461 | + <pre class="product-result-doc-pre"></pre> | |
| 490 | 462 | </div> |
| 491 | 463 | </div> |
| 492 | 464 | `; | ... | ... |
scripts/start_embedding_service.sh
| ... | ... | @@ -138,7 +138,11 @@ fi |
| 138 | 138 | if [[ "${IMAGE_MODEL_ENABLED}" == "1" ]]; then |
| 139 | 139 | echo "Image max inflight: ${IMAGE_MAX_INFLIGHT:-1}" |
| 140 | 140 | fi |
| 141 | -echo "Logs: logs/embedding_api.log, logs/embedding_api_error.log, logs/verbose/embedding_verbose.log" | |
| 141 | +if [[ "${SERVICE_KIND}" == "image" ]]; then | |
| 142 | + echo "Logs: logs/embedding-image.log" | |
| 143 | +else | |
| 144 | + echo "Logs: logs/embedding.log" | |
| 145 | +fi | |
| 142 | 146 | echo |
| 143 | 147 | echo "Tips:" |
| 144 | 148 | echo " - Use a single worker (GPU models cannot be safely duplicated across workers)." |
| ... | ... | @@ -153,12 +157,16 @@ echo |
| 153 | 157 | |
| 154 | 158 | UVICORN_LOG_LEVEL="${EMBEDDING_UVICORN_LOG_LEVEL:-info}" |
| 155 | 159 | UVICORN_ACCESS_LOG="${EMBEDDING_UVICORN_ACCESS_LOG:-true}" |
| 160 | +UVICORN_LOG_CONFIG="${EMBEDDING_UVICORN_LOG_CONFIG:-${PROJECT_ROOT}/config/uvicorn_embedding_logging.json}" | |
| 156 | 161 | UVICORN_ARGS=( |
| 157 | 162 | --host "${EMBEDDING_SERVICE_HOST}" |
| 158 | 163 | --port "${EMBEDDING_SERVICE_PORT}" |
| 159 | 164 | --workers 1 |
| 160 | 165 | --log-level "${UVICORN_LOG_LEVEL}" |
| 161 | 166 | ) |
| 167 | +if [[ -f "${UVICORN_LOG_CONFIG}" ]]; then | |
| 168 | + UVICORN_ARGS+=(--log-config "${UVICORN_LOG_CONFIG}") | |
| 169 | +fi | |
| 162 | 170 | if [[ "${UVICORN_ACCESS_LOG}" == "0" || "${UVICORN_ACCESS_LOG}" == "false" || "${UVICORN_ACCESS_LOG}" == "no" ]]; then |
| 163 | 171 | UVICORN_ARGS+=(--no-access-log) |
| 164 | 172 | fi | ... | ... |