improve find fileds for default profiles
This commit is contained in:
@@ -189,11 +189,17 @@ function profileDiscoveryDetails(row) {
|
||||
}
|
||||
const selected = d.selected_fields || {};
|
||||
const top = (d.top_fields || []).slice(0,8).map(item => `${item.field} ${(Number(item.coverage || 0)*100).toFixed(0)}%/${item.unique_values}`).join(', ');
|
||||
const shared = (d.shared_fields || row.shared_fields || []).slice(0,8).map(item => `${item.field} (${item.streams} streams)`).join(', ');
|
||||
const shared = (d.shared_fields || row.shared_fields || []).slice(0,8).map(sharedFieldLabel).join(', ');
|
||||
const rejected = (d.rejected_fields || []).slice(0,8).map(item => `${item.field}: ${item.reason}`).join('; ');
|
||||
const reasons = (d.reasons || []).join('; ');
|
||||
return `<details data-detail-id="${esc(detailId)}"><summary>${esc(d.field_count)} fields analyzed</summary><p><b>Selected</b><br>Entity: ${esc((selected.entity || []).join(', ') || '-')}<br>Time: ${esc((selected.time || []).join(', ') || '-')}<br>Categorical: ${esc((selected.categorical || []).join(', ') || '-')}<br>Numeric: ${esc((selected.numeric || []).join(', ') || '-')}</p><p><b>Shared across streams</b><br>${esc(shared || '-')}</p><p><b>Top fields</b><br>${esc(top || '-')}</p><p><b>Rejected</b><br>${esc(rejected || '-')}</p><p>${esc(reasons || '')}</p></details>`;
|
||||
}
|
||||
function sharedFieldLabel(item) {
|
||||
const aliases = (item.aliases || []).filter(alias => alias !== item.field).slice(0,4);
|
||||
const suffix = aliases.length ? ` via ${aliases.join('/')}` : '';
|
||||
const kind = item.kind ? ` ${item.kind}` : '';
|
||||
return `${item.field}${kind} (${item.streams} streams${suffix})`;
|
||||
}
|
||||
function profileAdvisorLabel(row, advisor) {
|
||||
const rowAdvisor = row.profile_advisor || {};
|
||||
if (rowAdvisor.status === 'heuristic' && rowAdvisor.error) return 'heuristic (advisor error)';
|
||||
@@ -346,8 +352,9 @@ async function refresh() {
|
||||
{label:'Baseline fields', render:r => esc([...(r.categorical_fields || []), ...(r.numeric_fields || [])].slice(0,8).join(', ') || '-')},
|
||||
{label:'Detectors', render:r => esc(Object.keys(r.detectors || {}).join(', ') || '-')},
|
||||
{label:'Common denominators', render:r => esc((r.common_fields || []).slice(0,5).map(item => `${item.field} ${(item.coverage*100).toFixed(0)}%`).join(', ') || '-')},
|
||||
{label:'Shared fields', render:r => esc((r.shared_fields || []).slice(0,4).map(sharedFieldLabel).join(', ') || '-')},
|
||||
{label:'Discovery', render:r => profileDiscoveryDetails(r)},
|
||||
{label:'Action', render:r => r.profile_exists ? '<button type="button" disabled>Applied</button>' : `<button type="button" class="apply-suggested-profile" data-stream-id="${esc(r.stream_id)}">Apply profile</button>`}
|
||||
{label:'Action', render:r => `<button type="button" class="apply-suggested-profile" data-stream-id="${esc(r.stream_id)}">${r.profile_exists ? 'Update profile' : 'Apply profile'}</button>`}
|
||||
], 'profile-suggestions') : '<p class="muted">No missing stream profiles. Enable "show existing profiles" to inspect already configured profiles.</p>';
|
||||
const llmText = llm.text ? esc(llm.text).replace(/\\n/g, '<br>') : esc(llm.error || 'LLM assessment disabled or waiting for first run.');
|
||||
document.getElementById('llmAssessment').innerHTML = `<div>Status: <code>${esc(llm.status || 'unknown')}</code></div><p>${llmText}</p>`;
|
||||
@@ -552,6 +559,22 @@ function renderStreamPicker(errorText='') {
|
||||
document.getElementById('enableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = true; }); renderStreamPicker(); });
|
||||
document.getElementById('disableVisibleStreams').addEventListener('click', () => { streams.forEach(stream => { window.streamSelection[stream.id].enabled = false; }); renderStreamPicker(); });
|
||||
}
|
||||
function mergeProfile(existing, recommended) {
|
||||
const mergeList = (left, right) => [...new Set([...(left || []), ...(right || [])].filter(Boolean))];
|
||||
const detectors = {...(recommended.detectors || {}), ...(existing.detectors || {})};
|
||||
return {
|
||||
...recommended,
|
||||
...existing,
|
||||
name: existing.name || recommended.name,
|
||||
entity_field: (existing.entity_field || recommended.entity_field || (recommended.entity_fields || [])[0] || ''),
|
||||
entity_fields: mergeList(existing.entity_fields || (existing.entity_field ? [existing.entity_field] : []), recommended.entity_fields || (recommended.entity_field ? [recommended.entity_field] : [])),
|
||||
timestamp_field: existing.timestamp_field || recommended.timestamp_field || 'timestamp',
|
||||
categorical_fields: mergeList(existing.categorical_fields, recommended.categorical_fields),
|
||||
numeric_fields: mergeList(existing.numeric_fields, recommended.numeric_fields),
|
||||
detectors,
|
||||
field_weights: {...(recommended.field_weights || {}), ...(existing.field_weights || {})}
|
||||
};
|
||||
}
|
||||
async function applySuggestedProfile(streamId) {
|
||||
const notice = document.getElementById('profileApplyResult');
|
||||
const button = document.querySelector(`.apply-suggested-profile[data-stream-id="${CSS.escape(streamId)}"]`);
|
||||
@@ -565,7 +588,8 @@ async function applySuggestedProfile(streamId) {
|
||||
notice.textContent = `Applying profile for ${suggestion.stream_name || streamId}...`;
|
||||
const configResponse = await fetch('/api/config', {cache: 'no-store'});
|
||||
const config = await configResponse.json();
|
||||
const profile = suggestion.profile;
|
||||
const existingProfile = (config.graylog_stream_profiles || []).find(item => String(item.stream_id) === String(suggestion.profile.stream_id));
|
||||
const profile = existingProfile ? mergeProfile(existingProfile, suggestion.profile) : suggestion.profile;
|
||||
const payload = {
|
||||
graylog_stream_profiles: [...(config.graylog_stream_profiles || []).filter(item => item.stream_id !== profile.stream_id), profile],
|
||||
graylog_streams: config.graylog_streams || [],
|
||||
@@ -601,7 +625,7 @@ async function applySuggestedProfile(streamId) {
|
||||
notice.textContent = `Could not apply profile: ${result.error || response.statusText || response.status}`;
|
||||
return;
|
||||
}
|
||||
notice.textContent = `Applied recommended profile for ${suggestion.stream_name || streamId}. Monitor will use it on the next cycle.`;
|
||||
notice.textContent = `${existingProfile ? 'Updated' : 'Applied'} recommended profile for ${suggestion.stream_name || streamId}. Monitor will use it on the next cycle.`;
|
||||
document.getElementById('settingsResult').textContent = notice.textContent;
|
||||
window.streamProfiles = result.graylog_stream_profiles || payload.graylog_stream_profiles;
|
||||
loadSettings();
|
||||
|
||||
Reference in New Issue
Block a user