viberadar 0.3.77 → 0.3.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/dashboard.html +352 -4
- package/package.json +1 -1
package/dist/ui/dashboard.html
CHANGED
|
@@ -739,6 +739,98 @@
|
|
|
739
739
|
.obs-tab-content.active { display:block; }
|
|
740
740
|
@media (max-width:640px) { .obs-kpi-strip { grid-template-columns:repeat(2,1fr); } }
|
|
741
741
|
|
|
742
|
+
/* ── Tab hint panels ── */
|
|
743
|
+
.obs-hint {
|
|
744
|
+
margin-bottom: 12px;
|
|
745
|
+
border: 1px solid var(--border);
|
|
746
|
+
border-radius: 8px;
|
|
747
|
+
overflow: hidden;
|
|
748
|
+
}
|
|
749
|
+
.obs-hint-toggle {
|
|
750
|
+
display: flex; align-items: center; gap: 8px;
|
|
751
|
+
width: 100%; padding: 10px 14px;
|
|
752
|
+
background: none; border: none; cursor: pointer;
|
|
753
|
+
color: var(--muted); font-size: 12px; font-weight: 600;
|
|
754
|
+
text-align: left; transition: background 0.1s, color 0.1s;
|
|
755
|
+
}
|
|
756
|
+
.obs-hint-toggle:hover { background: var(--bg-card); color: var(--text); }
|
|
757
|
+
.obs-hint-icon {
|
|
758
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
759
|
+
width: 18px; height: 18px; border-radius: 50%;
|
|
760
|
+
border: 1.5px solid currentColor; font-size: 11px; font-weight: 700;
|
|
761
|
+
flex-shrink: 0;
|
|
762
|
+
}
|
|
763
|
+
.obs-hint-chevron { margin-left: auto; font-size: 10px; transition: transform 0.2s; }
|
|
764
|
+
.obs-hint.open .obs-hint-chevron { transform: rotate(180deg); }
|
|
765
|
+
.obs-hint-body {
|
|
766
|
+
display: none;
|
|
767
|
+
padding: 0 16px 16px 16px;
|
|
768
|
+
background: var(--bg-card);
|
|
769
|
+
font-size: 12px; color: var(--muted); line-height: 1.6;
|
|
770
|
+
}
|
|
771
|
+
.obs-hint.open .obs-hint-body { display: block; }
|
|
772
|
+
.obs-hint-section { margin-top: 12px; }
|
|
773
|
+
.obs-hint-section-title { font-size: 11px; font-weight: 700; color: var(--text); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
|
|
774
|
+
.obs-hint-code {
|
|
775
|
+
font-family: monospace; font-size: 11px;
|
|
776
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
777
|
+
border-radius: 5px; padding: 8px 10px; margin: 4px 0;
|
|
778
|
+
line-height: 1.5; overflow-x: auto; white-space: pre;
|
|
779
|
+
}
|
|
780
|
+
.obs-hint-code .hl-red { color: var(--red); }
|
|
781
|
+
.obs-hint-code .hl-green { color: var(--green); }
|
|
782
|
+
.obs-hint-code .hl-yellow { color: var(--yellow); }
|
|
783
|
+
.obs-hint-code .hl-dim { color: var(--dim); }
|
|
784
|
+
.obs-hint-row { display: flex; gap: 12px; }
|
|
785
|
+
.obs-hint-row > * { flex: 1; min-width: 0; }
|
|
786
|
+
.obs-hint-label { font-size: 10px; font-weight: 700; margin-bottom: 3px; }
|
|
787
|
+
.obs-hint-label.before { color: var(--red); }
|
|
788
|
+
.obs-hint-label.after { color: var(--green); }
|
|
789
|
+
|
|
790
|
+
/* ── Floating selection action bar ── */
|
|
791
|
+
.obs-floating-bar {
|
|
792
|
+
position: fixed;
|
|
793
|
+
bottom: -80px;
|
|
794
|
+
left: 50%;
|
|
795
|
+
transform: translateX(-50%);
|
|
796
|
+
z-index: 300;
|
|
797
|
+
display: flex; align-items: center; gap: 12px;
|
|
798
|
+
background: #1c2128;
|
|
799
|
+
border: 1px solid var(--accent);
|
|
800
|
+
border-radius: 10px;
|
|
801
|
+
padding: 10px 10px 10px 16px;
|
|
802
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.5), 0 0 0 1px rgba(88,166,255,0.15);
|
|
803
|
+
min-width: 340px;
|
|
804
|
+
transition: bottom 0.25s cubic-bezier(0.34,1.56,0.64,1);
|
|
805
|
+
pointer-events: none;
|
|
806
|
+
opacity: 0;
|
|
807
|
+
}
|
|
808
|
+
.obs-floating-bar.visible {
|
|
809
|
+
bottom: 28px;
|
|
810
|
+
pointer-events: auto;
|
|
811
|
+
opacity: 1;
|
|
812
|
+
}
|
|
813
|
+
.obs-floating-count {
|
|
814
|
+
color: var(--text); font-size: 13px; font-weight: 600; white-space: nowrap;
|
|
815
|
+
}
|
|
816
|
+
.obs-floating-label {
|
|
817
|
+
color: var(--muted); font-size: 12px; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
818
|
+
}
|
|
819
|
+
.obs-floating-btn {
|
|
820
|
+
padding: 8px 18px; font-size: 13px; font-weight: 600;
|
|
821
|
+
background: var(--accent); color: var(--bg);
|
|
822
|
+
border: none; border-radius: 6px; cursor: pointer; min-height: 36px;
|
|
823
|
+
white-space: nowrap; flex-shrink: 0;
|
|
824
|
+
transition: opacity 0.1s;
|
|
825
|
+
}
|
|
826
|
+
.obs-floating-btn:hover { opacity: 0.85; }
|
|
827
|
+
.obs-floating-dismiss {
|
|
828
|
+
background: none; border: none; color: var(--dim); cursor: pointer;
|
|
829
|
+
font-size: 16px; padding: 4px 6px; line-height: 1; flex-shrink: 0;
|
|
830
|
+
border-radius: 4px; transition: color 0.1s;
|
|
831
|
+
}
|
|
832
|
+
.obs-floating-dismiss:hover { color: var(--text); }
|
|
833
|
+
|
|
742
834
|
.file-row-err-badge {
|
|
743
835
|
display: inline-flex; align-items: center;
|
|
744
836
|
font-size: 11px; padding: 1px 6px; border-radius: 10px;
|
|
@@ -1171,6 +1263,13 @@
|
|
|
1171
1263
|
<div id="panelContent"></div>
|
|
1172
1264
|
</div>
|
|
1173
1265
|
|
|
1266
|
+
<div class="obs-floating-bar" id="obsFloatingBar">
|
|
1267
|
+
<span class="obs-floating-count" id="obsFloatingCount">0 выбрано</span>
|
|
1268
|
+
<span class="obs-floating-label" id="obsFloatingLabel"></span>
|
|
1269
|
+
<button class="obs-floating-btn" id="obsFloatingBtn" onclick="obsDispatchFloating()">запустить</button>
|
|
1270
|
+
<button class="obs-floating-dismiss" title="Снять выделение" onclick="obsFloatingDismiss()">✕</button>
|
|
1271
|
+
</div>
|
|
1272
|
+
|
|
1174
1273
|
<div class="agent-panel" id="agentPanel">
|
|
1175
1274
|
<div class="agent-panel-header">
|
|
1176
1275
|
<span class="agent-panel-title" id="agentPanelTitle">🤖 Agent</span>
|
|
@@ -1220,6 +1319,10 @@ let e2ePlan = null; // current E2E plan object
|
|
|
1220
1319
|
let e2ePlanLoading = false;
|
|
1221
1320
|
let obsActiveTab = 'overview'; // active observability tab
|
|
1222
1321
|
|
|
1322
|
+
function toggleObsHint(id) {
|
|
1323
|
+
document.getElementById(id).classList.toggle('open');
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1223
1326
|
function switchObsTab(tabId) {
|
|
1224
1327
|
obsActiveTab = tabId;
|
|
1225
1328
|
history.replaceState(null, '', location.pathname + location.search + '#obs-tab=' + tabId);
|
|
@@ -2185,9 +2288,62 @@ function obsUpdateSelectedCount(groupId) {
|
|
|
2185
2288
|
const checked = container.querySelectorAll('input[type="checkbox"]:checked').length;
|
|
2186
2289
|
const btn = container.querySelector('.obs-run-selected');
|
|
2187
2290
|
if (btn) {
|
|
2188
|
-
|
|
2291
|
+
const baseLabel = btn.dataset.baseLabel || btn.textContent.replace(/\s*\(\d+\)$/, '');
|
|
2292
|
+
btn.dataset.baseLabel = baseLabel;
|
|
2293
|
+
btn.textContent = checked > 0 ? `${baseLabel} (${checked})` : baseLabel;
|
|
2189
2294
|
btn.disabled = checked === 0;
|
|
2190
2295
|
}
|
|
2296
|
+
// Update floating action bar
|
|
2297
|
+
const bar = document.getElementById('obsFloatingBar');
|
|
2298
|
+
if (!bar) return;
|
|
2299
|
+
if (checked > 0) {
|
|
2300
|
+
const action = container.dataset.obsAction || '';
|
|
2301
|
+
const actionLabels = { missing: 'добавить логи', field: 'обогатить поля', rec: 'исправить' };
|
|
2302
|
+
const groupLabel = container.dataset.obsLabel || '';
|
|
2303
|
+
bar.dataset.groupId = groupId;
|
|
2304
|
+
document.getElementById('obsFloatingCount').textContent = `${checked} выбрано`;
|
|
2305
|
+
document.getElementById('obsFloatingLabel').textContent = groupLabel;
|
|
2306
|
+
document.getElementById('obsFloatingBtn').textContent = actionLabels[action] || 'исправить';
|
|
2307
|
+
bar.classList.add('visible');
|
|
2308
|
+
} else {
|
|
2309
|
+
if (bar.dataset.groupId === groupId) {
|
|
2310
|
+
bar.classList.remove('visible');
|
|
2311
|
+
delete bar.dataset.groupId;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
function obsDispatchFloating() {
|
|
2317
|
+
const bar = document.getElementById('obsFloatingBar');
|
|
2318
|
+
if (!bar) return;
|
|
2319
|
+
const groupId = bar.dataset.groupId;
|
|
2320
|
+
if (!groupId) return;
|
|
2321
|
+
const container = document.getElementById('obs-detail-' + groupId);
|
|
2322
|
+
if (!container) return;
|
|
2323
|
+
const action = container.dataset.obsAction || '';
|
|
2324
|
+
if (action === 'missing') {
|
|
2325
|
+
obsMissingRunSelected(groupId);
|
|
2326
|
+
} else {
|
|
2327
|
+
const meta = container.dataset.obsMeta ? JSON.parse(container.dataset.obsMeta) : {};
|
|
2328
|
+
obsRunSelected(groupId, 'obs-fix-selected', meta);
|
|
2329
|
+
}
|
|
2330
|
+
bar.classList.remove('visible');
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
function obsFloatingDismiss() {
|
|
2334
|
+
const bar = document.getElementById('obsFloatingBar');
|
|
2335
|
+
if (!bar) return;
|
|
2336
|
+
const groupId = bar.dataset.groupId;
|
|
2337
|
+
if (groupId) {
|
|
2338
|
+
const container = document.getElementById('obs-detail-' + groupId);
|
|
2339
|
+
if (container) {
|
|
2340
|
+
container.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
2341
|
+
const btn = container.querySelector('.obs-run-selected');
|
|
2342
|
+
if (btn) { btn.textContent = btn.dataset.baseLabel || btn.textContent.replace(/\s*\(\d+\)$/, ''); btn.disabled = true; }
|
|
2343
|
+
}
|
|
2344
|
+
delete bar.dataset.groupId;
|
|
2345
|
+
}
|
|
2346
|
+
bar.classList.remove('visible');
|
|
2191
2347
|
}
|
|
2192
2348
|
|
|
2193
2349
|
function obsToggleAll(groupId) {
|
|
@@ -2593,7 +2749,7 @@ function renderObservability(c) {
|
|
|
2593
2749
|
}).join('');
|
|
2594
2750
|
const addBtn = hasAgent ? `<button class="obs-run-selected" disabled onclick="obsMissingRunSelected('${groupId}')">добавить логи выбранным</button>` : '';
|
|
2595
2751
|
const detail = hasAgent ? `
|
|
2596
|
-
<div id="obs-detail-${groupId}" class="obs-detail">
|
|
2752
|
+
<div id="obs-detail-${groupId}" class="obs-detail" data-obs-action="missing" data-obs-label="${tierLabels[tier]}">
|
|
2597
2753
|
<div class="obs-detail-bar" style="border-top:none;padding-top:0;margin-bottom:4px">
|
|
2598
2754
|
<button class="obs-select-all" onclick="obsToggleAll('${groupId}')">выбрать все / снять</button>
|
|
2599
2755
|
</div>
|
|
@@ -2625,7 +2781,7 @@ function renderObservability(c) {
|
|
|
2625
2781
|
return `<label class="obs-detail-item"><input type="checkbox" data-idx="${catIdx}" onchange="obsUpdateSelectedCount('${groupId}')"><span title="${escapeHtml(ci.modulePath)}">${escapeHtml(ci.modulePath.split('/').slice(-2).join('/'))}</span><span style="color:var(--dim);flex-shrink:0">${ci.format}</span></label>`;
|
|
2626
2782
|
}).join('');
|
|
2627
2783
|
const detail = hasAgent ? `
|
|
2628
|
-
<div id="obs-detail-${groupId}" class="obs-detail">
|
|
2784
|
+
<div id="obs-detail-${groupId}" class="obs-detail" data-obs-action="field" data-obs-meta='{"fieldName":"${escapeHtml(name)}"}' data-obs-label="поле ${escapeHtml(name)}">
|
|
2629
2785
|
<div class="obs-detail-bar" style="border-top:none;padding-top:0;margin-bottom:4px">
|
|
2630
2786
|
<button class="obs-select-all" onclick="obsToggleAll('${groupId}')">выбрать все / снять</button>
|
|
2631
2787
|
</div>
|
|
@@ -2700,6 +2856,35 @@ function renderObservability(c) {
|
|
|
2700
2856
|
</div>
|
|
2701
2857
|
|
|
2702
2858
|
<div class="obs-tab-content" data-obstab="overview">
|
|
2859
|
+
<div class="obs-hint" id="obs-hint-overview">
|
|
2860
|
+
<button class="obs-hint-toggle" onclick="toggleObsHint('obs-hint-overview')">
|
|
2861
|
+
<span class="obs-hint-icon">?</span>
|
|
2862
|
+
Что такое «Обзор» и как читать эти числа?
|
|
2863
|
+
<span class="obs-hint-chevron">▼</span>
|
|
2864
|
+
</button>
|
|
2865
|
+
<div class="obs-hint-body">
|
|
2866
|
+
<p>Обзор — это «пульс» логирования в проекте. Четыре метрики наверху показывают, насколько логи полезны прямо сейчас.</p>
|
|
2867
|
+
<div class="obs-hint-section">
|
|
2868
|
+
<div class="obs-hint-section-title">Что означает каждая метрика</div>
|
|
2869
|
+
<div class="obs-hint-code"><span class="hl-red">Шум 40%</span> — почти половина логов бесполезны. Они засоряют Kibana/Datadog
|
|
2870
|
+
и скрывают реальные ошибки.
|
|
2871
|
+
|
|
2872
|
+
<span class="hl-yellow">Структурированность 20%</span> — большинство логов — просто строки вроде
|
|
2873
|
+
<span class="hl-dim">"Starting process..."</span>. Их нельзя фильтровать, группировать,
|
|
2874
|
+
строить алерты. Цель — 80%+.
|
|
2875
|
+
|
|
2876
|
+
<span class="hl-yellow">Actionable ошибки 50%</span> — половина ошибок логируется без контекста.
|
|
2877
|
+
Инженер видит <span class="hl-dim">"Error occurred"</span> — и ничего больше.
|
|
2878
|
+
|
|
2879
|
+
<span class="hl-red">Покрытие сценариев 24%</span> — 76% модулей вообще не имеют warn/error логов.
|
|
2880
|
+
Когда они сломаются — узнаешь только от пользователей.</div>
|
|
2881
|
+
</div>
|
|
2882
|
+
<div class="obs-hint-section">
|
|
2883
|
+
<div class="obs-hint-section-title">Блок «Рекомендации»</div>
|
|
2884
|
+
<p>Итоговый план действий по всему проекту: сколько источников нужно <span style="color:var(--red)">убрать</span>, <span style="color:var(--yellow)">обогатить</span>, <span style="color:var(--yellow)">добавить</span>. Каждая строчка раскрывается — можно выбрать файлы и отдать агенту.</p>
|
|
2885
|
+
</div>
|
|
2886
|
+
</div>
|
|
2887
|
+
</div>
|
|
2703
2888
|
<div class="obs-grid" style="grid-template-columns:1fr 1fr 1fr;margin-bottom:12px">
|
|
2704
2889
|
<div class="obs-card">
|
|
2705
2890
|
<div class="obs-title">Источники по формату</div>
|
|
@@ -2733,7 +2918,7 @@ function renderObservability(c) {
|
|
|
2733
2918
|
return `<label class="obs-detail-item"><input type="checkbox" data-idx="${catIdx}" onchange="obsUpdateSelectedCount('${groupId}')"><span title="${escapeHtml(ci.modulePath)}">${escapeHtml(ci.modulePath.split('/').slice(-2).join('/'))}</span><span style="color:var(--dim);flex-shrink:0">${ci.format}</span></label>`;
|
|
2734
2919
|
}).join('');
|
|
2735
2920
|
const detail = hasAgent ? `
|
|
2736
|
-
<div id="obs-detail-${groupId}" class="obs-detail">
|
|
2921
|
+
<div id="obs-detail-${groupId}" class="obs-detail" data-obs-action="rec" data-obs-meta='{"recommendationType":"${rec}"}' data-obs-label="${recLabels[rec] || rec}">
|
|
2737
2922
|
<div class="obs-detail-bar" style="border-top:none;padding-top:0;margin-bottom:4px">
|
|
2738
2923
|
<button class="obs-select-all" onclick="obsToggleAll('${groupId}')">выбрать все / снять</button>
|
|
2739
2924
|
</div>
|
|
@@ -2753,6 +2938,42 @@ function renderObservability(c) {
|
|
|
2753
2938
|
</div>
|
|
2754
2939
|
|
|
2755
2940
|
<div class="obs-tab-content" data-obstab="remove">
|
|
2941
|
+
<div class="obs-hint" id="obs-hint-remove">
|
|
2942
|
+
<button class="obs-hint-toggle" onclick="toggleObsHint('obs-hint-remove')">
|
|
2943
|
+
<span class="obs-hint-icon">?</span>
|
|
2944
|
+
Зачем убирать эти логи? Разве лишний лог — не лучше, чем ничего?
|
|
2945
|
+
<span class="obs-hint-chevron">▼</span>
|
|
2946
|
+
</button>
|
|
2947
|
+
<div class="obs-hint-body">
|
|
2948
|
+
<p>Шумные логи — это строки, которые пишутся при каждом запросе или в цикле и не несут полезной информации. Они создают три реальные проблемы:</p>
|
|
2949
|
+
<ul style="margin:8px 0 0 16px;display:flex;flex-direction:column;gap:4px">
|
|
2950
|
+
<li><strong style="color:var(--red)">Топят сигнал.</strong> Когда в Kibana 10 000 строк в минуту, реальная ошибка теряется на странице 47.</li>
|
|
2951
|
+
<li><strong style="color:var(--yellow)">Стоят деньги.</strong> Datadog, New Relic, Elastic — тарификация по объёму. Шум = деньги на ветер.</li>
|
|
2952
|
+
<li><strong style="color:var(--muted)">Замедляют дебаг.</strong> Инженер тратит 20 минут, чтобы понять, что «[unknown]» — это просто фоновый краулер.</li>
|
|
2953
|
+
</ul>
|
|
2954
|
+
<div class="obs-hint-section">
|
|
2955
|
+
<div class="obs-hint-section-title">До / После</div>
|
|
2956
|
+
<div class="obs-hint-row">
|
|
2957
|
+
<div>
|
|
2958
|
+
<div class="obs-hint-label before">БЫЛО — шум</div>
|
|
2959
|
+
<div class="obs-hint-code"><span class="hl-red">logger.info('[unknown]') // ×116/день</span>
|
|
2960
|
+
<span class="hl-red">logger.info('Converting WebM to OGG...')// ×200/день</span>
|
|
2961
|
+
<span class="hl-red">logger.info('Using API key from env') // каждый запрос</span></div>
|
|
2962
|
+
</div>
|
|
2963
|
+
<div>
|
|
2964
|
+
<div class="obs-hint-label after">СТАЛО — чисто</div>
|
|
2965
|
+
<div class="obs-hint-code"><span class="hl-green">// строки удалены или понижены до debug</span>
|
|
2966
|
+
<span class="hl-dim">// В production debug-логи выключены</span>
|
|
2967
|
+
<span class="hl-green">// В мониторинге только важные события</span></div>
|
|
2968
|
+
</div>
|
|
2969
|
+
</div>
|
|
2970
|
+
</div>
|
|
2971
|
+
<div class="obs-hint-section">
|
|
2972
|
+
<div class="obs-hint-section-title">Как использовать</div>
|
|
2973
|
+
<p>Нажми <strong>«убрать»</strong> рядом с паттерном — агент найдёт все места в коде и либо удалит строку, либо заменит <code>logger.info</code> на <code>logger.debug</code>. Можно обработать несколько паттернов сразу.</p>
|
|
2974
|
+
</div>
|
|
2975
|
+
</div>
|
|
2976
|
+
</div>
|
|
2756
2977
|
<div class="obs-card">
|
|
2757
2978
|
<div class="obs-title">Шумные паттерны</div>
|
|
2758
2979
|
<div class="obs-list" style="gap:4px">${noisyRows}</div>
|
|
@@ -2760,6 +2981,57 @@ function renderObservability(c) {
|
|
|
2760
2981
|
</div>
|
|
2761
2982
|
|
|
2762
2983
|
<div class="obs-tab-content" data-obstab="add">
|
|
2984
|
+
<div class="obs-hint" id="obs-hint-add">
|
|
2985
|
+
<button class="obs-hint-toggle" onclick="toggleObsHint('obs-hint-add')">
|
|
2986
|
+
<span class="obs-hint-icon">?</span>
|
|
2987
|
+
Почему важно добавлять логи именно в эти места?
|
|
2988
|
+
<span class="obs-hint-chevron">▼</span>
|
|
2989
|
+
</button>
|
|
2990
|
+
<div class="obs-hint-body">
|
|
2991
|
+
<p>Сканер нашёл «точки отказа» — конструкции в коде, где что-то может пойти не так, но лог туда не написан. Если здесь случится ошибка в production — ты узнаешь об этом только от пользователя.</p>
|
|
2992
|
+
<div class="obs-hint-section">
|
|
2993
|
+
<div class="obs-hint-section-title">Типы точек отказа</div>
|
|
2994
|
+
<div class="obs-hint-code"><span class="hl-red">empty-catch</span> — catch(){} без лога. Ошибка молча проглочена.
|
|
2995
|
+
<span class="hl-red">catch-no-log</span> — catch(e){ doSomething() } — ошибка обработана, но не залогирована.
|
|
2996
|
+
<span class="hl-yellow">http-no-error-handling</span> — HTTP-запрос без обработки ошибки (нет .catch или try/catch).
|
|
2997
|
+
<span class="hl-yellow">db-no-error-handling</span> — запрос к БД без обработки ошибки.
|
|
2998
|
+
<span class="hl-yellow">throw-no-log</span> — throw new Error(...) без предварительного лога.
|
|
2999
|
+
<span class="hl-dim">error-check-no-log</span> — if (err) { return } без лога.</div>
|
|
3000
|
+
</div>
|
|
3001
|
+
<div class="obs-hint-section">
|
|
3002
|
+
<div class="obs-hint-section-title">До / После</div>
|
|
3003
|
+
<div class="obs-hint-row">
|
|
3004
|
+
<div>
|
|
3005
|
+
<div class="obs-hint-label before">БЫЛО — немая ошибка</div>
|
|
3006
|
+
<div class="obs-hint-code"><span class="hl-dim">async function processPayment(data) {</span>
|
|
3007
|
+
<span class="hl-dim">try {</span>
|
|
3008
|
+
<span class="hl-dim">await stripe.charge(data);</span>
|
|
3009
|
+
<span class="hl-red">} catch (e) {</span>
|
|
3010
|
+
<span class="hl-red"> // пусто! ошибка проглочена</span>
|
|
3011
|
+
<span class="hl-red">}</span>
|
|
3012
|
+
<span class="hl-dim">}</span></div>
|
|
3013
|
+
</div>
|
|
3014
|
+
<div>
|
|
3015
|
+
<div class="obs-hint-label after">СТАЛО — видимая ошибка</div>
|
|
3016
|
+
<div class="obs-hint-code"><span class="hl-dim">async function processPayment(data) {</span>
|
|
3017
|
+
<span class="hl-dim">try {</span>
|
|
3018
|
+
<span class="hl-dim">await stripe.charge(data);</span>
|
|
3019
|
+
<span class="hl-green">} catch (e) {</span>
|
|
3020
|
+
<span class="hl-green"> logger.error('payment_failed', {</span>
|
|
3021
|
+
<span class="hl-green"> error: e.message, userId: data.userId</span>
|
|
3022
|
+
<span class="hl-green"> });</span>
|
|
3023
|
+
<span class="hl-green"> throw e;</span>
|
|
3024
|
+
<span class="hl-green">}</span>
|
|
3025
|
+
<span class="hl-dim">}</span></div>
|
|
3026
|
+
</div>
|
|
3027
|
+
</div>
|
|
3028
|
+
</div>
|
|
3029
|
+
<div class="obs-hint-section">
|
|
3030
|
+
<div class="obs-hint-section-title">Как использовать</div>
|
|
3031
|
+
<p>Модули разбиты по уровню риска: <span style="color:var(--red)">Критичные</span> — трогать в первую очередь (платежи, авторизация, запись данных). Раскрой группу, выбери модули и нажми <strong>«добавить логи выбранным»</strong>.</p>
|
|
3032
|
+
</div>
|
|
3033
|
+
</div>
|
|
3034
|
+
</div>
|
|
2763
3035
|
<div class="obs-card">
|
|
2764
3036
|
<div class="obs-title">Нет критичных логов</div>
|
|
2765
3037
|
<div class="obs-list" style="gap:4px">${missingSection}</div>
|
|
@@ -2767,6 +3039,54 @@ function renderObservability(c) {
|
|
|
2767
3039
|
</div>
|
|
2768
3040
|
|
|
2769
3041
|
<div class="obs-tab-content" data-obstab="enrich">
|
|
3042
|
+
<div class="obs-hint" id="obs-hint-enrich">
|
|
3043
|
+
<button class="obs-hint-toggle" onclick="toggleObsHint('obs-hint-enrich')">
|
|
3044
|
+
<span class="obs-hint-icon">?</span>
|
|
3045
|
+
Какая разница, есть ли поля в логе? Главное, что сообщение есть.
|
|
3046
|
+
<span class="obs-hint-chevron">▼</span>
|
|
3047
|
+
</button>
|
|
3048
|
+
<div class="obs-hint-body">
|
|
3049
|
+
<p>Лог без полей — это как сигнал пожарной тревоги без адреса. Ты знаешь, что что-то горит, но не знаешь где и что именно. Структурированные поля превращают лог из строки в <em>данные</em>.</p>
|
|
3050
|
+
<div class="obs-hint-section">
|
|
3051
|
+
<div class="obs-hint-section-title">До / После</div>
|
|
3052
|
+
<div class="obs-hint-row">
|
|
3053
|
+
<div>
|
|
3054
|
+
<div class="obs-hint-label before">БЫЛО — бесполезный лог</div>
|
|
3055
|
+
<div class="obs-hint-code"><span class="hl-red">logger.error('Request failed')</span>
|
|
3056
|
+
<span class="hl-dim">// В Kibana: "Request failed"</span>
|
|
3057
|
+
<span class="hl-dim">// Что за запрос? Чей? Когда?</span>
|
|
3058
|
+
<span class="hl-dim">// Какая ошибка? Какой эндпоинт?</span>
|
|
3059
|
+
<span class="hl-dim">// → звонишь разработчику в 2 ночи</span></div>
|
|
3060
|
+
</div>
|
|
3061
|
+
<div>
|
|
3062
|
+
<div class="obs-hint-label after">СТАЛО — actionable лог</div>
|
|
3063
|
+
<div class="obs-hint-code"><span class="hl-green">logger.error('request_failed', {</span>
|
|
3064
|
+
<span class="hl-green">event: 'request_failed',</span>
|
|
3065
|
+
<span class="hl-green">service: 'payment-api',</span>
|
|
3066
|
+
<span class="hl-green">trace_id: req.traceId,</span>
|
|
3067
|
+
<span class="hl-green">user_id: req.userId,</span>
|
|
3068
|
+
<span class="hl-green">error_code: e.code,</span>
|
|
3069
|
+
<span class="hl-green">outcome: 'failure'</span>
|
|
3070
|
+
<span class="hl-green">})</span>
|
|
3071
|
+
<span class="hl-dim">// → фильтр по user_id, алерт по error_code</span></div>
|
|
3072
|
+
</div>
|
|
3073
|
+
</div>
|
|
3074
|
+
</div>
|
|
3075
|
+
<div class="obs-hint-section">
|
|
3076
|
+
<div class="obs-hint-section-title">Обязательные поля и зачем они нужны</div>
|
|
3077
|
+
<div class="obs-hint-code"><span class="hl-green">service</span> — какой сервис логирует (нужен для фильтрации в мульти-сервисе)
|
|
3078
|
+
<span class="hl-green">event_name</span> — машиночитаемое название события (для алертов и дашбордов)
|
|
3079
|
+
<span class="hl-green">trace_id</span> — сквозной ID запроса (связывает логи из разных сервисов)
|
|
3080
|
+
<span class="hl-green">outcome</span> — success/failure (позволяет строить SLO-метрики)
|
|
3081
|
+
<span class="hl-yellow">user_id</span> — чей запрос (обязателен для error/warn)
|
|
3082
|
+
<span class="hl-yellow">error_code</span> — код ошибки (обязателен для error/warn)</div>
|
|
3083
|
+
</div>
|
|
3084
|
+
<div class="obs-hint-section">
|
|
3085
|
+
<div class="obs-hint-section-title">Как использовать</div>
|
|
3086
|
+
<p>Каждая строчка — пропущенное поле и сколько раз оно отсутствует. Раскрой строку, выбери затронутые модули и нажми <strong>«обогатить выбранные»</strong> — агент добавит поля в существующие лог-вызовы.</p>
|
|
3087
|
+
</div>
|
|
3088
|
+
</div>
|
|
3089
|
+
</div>
|
|
2770
3090
|
<div class="obs-card">
|
|
2771
3091
|
<div class="obs-title">Пробелы по полям</div>
|
|
2772
3092
|
<div class="obs-sub" style="margin-bottom:8px">Обязательные поля по стандарту: service, env, trace_id, request_id, event_name, outcome, error_code (warn/error), user_id.</div>
|
|
@@ -2775,6 +3095,34 @@ function renderObservability(c) {
|
|
|
2775
3095
|
</div>
|
|
2776
3096
|
|
|
2777
3097
|
<div class="obs-tab-content" data-obstab="catalog">
|
|
3098
|
+
<div class="obs-hint" id="obs-hint-catalog">
|
|
3099
|
+
<button class="obs-hint-toggle" onclick="toggleObsHint('obs-hint-catalog')">
|
|
3100
|
+
<span class="obs-hint-icon">?</span>
|
|
3101
|
+
Как читать таблицу и что означают колонки?
|
|
3102
|
+
<span class="obs-hint-chevron">▼</span>
|
|
3103
|
+
</button>
|
|
3104
|
+
<div class="obs-hint-body">
|
|
3105
|
+
<p>Каталог — полная карта всех источников логов в проекте. Каждая строка = один файл/модуль. Это отправная точка для ручного аудита или приоритизации задач агента.</p>
|
|
3106
|
+
<div class="obs-hint-section">
|
|
3107
|
+
<div class="obs-hint-section-title">Что означают колонки</div>
|
|
3108
|
+
<div class="obs-hint-code"><span class="hl-green">формат</span> <span class="hl-green">structured</span> — логи в JSON с полями. Можно фильтровать, алертить.
|
|
3109
|
+
<span class="hl-yellow">mixed</span> — часть логов структурированы, часть — нет.
|
|
3110
|
+
<span class="hl-red">unstructured</span> — просто строки. Ни алертов, ни поиска по полям.
|
|
3111
|
+
|
|
3112
|
+
<span class="hl-yellow">пробелы</span> <span class="hl-dim">N/8</span> — сколько из 8 обязательных полей отсутствуют.
|
|
3113
|
+
<span class="hl-green">0/8</span> — отлично. <span class="hl-red">5/8</span> — нужно работать.
|
|
3114
|
+
|
|
3115
|
+
<span class="hl-dim">действие</span> suppress — модуль генерирует шум, логи нужно убрать/понизить.
|
|
3116
|
+
enrich fields — логи есть, но без нужных полей.
|
|
3117
|
+
add event — нет вообще никаких логов в критичных местах.
|
|
3118
|
+
downgrade level— используется INFO там, где достаточно DEBUG.</div>
|
|
3119
|
+
</div>
|
|
3120
|
+
<div class="obs-hint-section">
|
|
3121
|
+
<div class="obs-hint-section-title">Как использовать</div>
|
|
3122
|
+
<p>Нажми <strong>«исправить»</strong> в строке конкретного модуля — агент откроет файл, посмотрит контекст и применит рекомендованное действие. Удобно для точечных правок без выбора целой группы.</p>
|
|
3123
|
+
</div>
|
|
3124
|
+
</div>
|
|
3125
|
+
</div>
|
|
2778
3126
|
<div class="obs-card">
|
|
2779
3127
|
<div class="obs-title">Каталог источников логов</div>
|
|
2780
3128
|
<div class="obs-catalog" style="margin-top:8px;border:none;padding:0">
|