Skip to content

Commit

Permalink
[Ingest Manager] UI to configure agent log level (#84112) (#84215)
Browse files Browse the repository at this point in the history
  • Loading branch information
nchaulet committed Nov 24, 2020
1 parent 820759b commit fd30a17
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 4 deletions.
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/common/services/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ export const agentRouteService = {
getBulkUpgradePath: () => AGENT_API_ROUTES.BULK_UPGRADE_PATTERN,
getListPath: () => AGENT_API_ROUTES.LIST_PATTERN,
getStatusPath: () => AGENT_API_ROUTES.STATUS_PATTERN,
getCreateActionPath: (agentId: string) =>
AGENT_API_ROUTES.ACTIONS_PATTERN.replace('{agentId}', agentId),
};

export const outputRoutesService = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import {
PostBulkAgentUpgradeRequest,
PostAgentUpgradeResponse,
PostBulkAgentUpgradeResponse,
PostNewAgentActionRequest,
PostNewAgentActionResponse,
} from '../../types';

type RequestOptions = Pick<Partial<UseRequestConfig>, 'pollIntervalMs'>;
Expand Down Expand Up @@ -144,6 +146,19 @@ export function sendPostAgentUpgrade(
});
}

export function sendPostAgentAction(
agentId: string,
body: PostNewAgentActionRequest['body'],
options?: RequestOptions
) {
return sendRequest<PostNewAgentActionResponse>({
path: agentRouteService.getCreateActionPath(agentId),
method: 'post',
body,
...options,
});
}

export function sendPostBulkAgentUpgrade(
body: PostBulkAgentUpgradeRequest['body'],
options?: RequestOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ export const AgentDetailsContent: React.FunctionComponent<{
: 'stable'
: '-',
},
{
title: i18n.translate('xpack.fleet.agentDetails.logLevel', {
defaultMessage: 'Log level',
}),
description:
typeof agent.local_metadata.elastic === 'object' &&
typeof agent.local_metadata.elastic.agent === 'object' &&
typeof agent.local_metadata.elastic.agent.log_level === 'string'
? agent.local_metadata.elastic.agent.log_level
: '-',
},
{
title: i18n.translate('xpack.fleet.agentDetails.platformLabel', {
defaultMessage: 'Platform',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ export const DEFAULT_DATE_RANGE = {
start: 'now-1d',
end: 'now',
};

export const AGENT_LOG_LEVELS = {
ERROR: 'error',
WARNING: 'warning',
INFO: 'info',
DEBUG: 'debug',
};

export const DEFAULT_LOG_LEVEL = AGENT_LOG_LEVELS.INFO;
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import React, { memo, useState, useEffect } from 'react';
import { EuiPopover, EuiFilterButton, EuiFilterSelectItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AGENT_LOG_LEVELS, AGENT_LOG_INDEX_PATTERN, LOG_LEVEL_FIELD } from './constants';
import { useStartServices } from '../../../../../hooks';
import { AGENT_LOG_INDEX_PATTERN, LOG_LEVEL_FIELD } from './constants';

const LEVEL_VALUES = Object.values(AGENT_LOG_LEVELS);

export const LogLevelFilter: React.FunctionComponent<{
selectedLevels: string[];
Expand All @@ -16,21 +18,21 @@ export const LogLevelFilter: React.FunctionComponent<{
const { data } = useStartServices();
const [isOpen, setIsOpen] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [levelValues, setLevelValues] = useState<string[]>([]);
const [levelValues, setLevelValues] = useState<string[]>(LEVEL_VALUES);

useEffect(() => {
const fetchValues = async () => {
setIsLoading(true);
try {
const values = await data.autocomplete.getValueSuggestions({
const values: string[] = await data.autocomplete.getValueSuggestions({
indexPattern: {
title: AGENT_LOG_INDEX_PATTERN,
fields: [LOG_LEVEL_FIELD],
},
field: LOG_LEVEL_FIELD,
query: '',
});
setLevelValues(values.sort());
setLevelValues([...new Set([...LEVEL_VALUES, ...values.sort()])]);
} catch (e) {
setLevelValues([]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
EuiButtonEmpty,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import semverGte from 'semver/functions/gte';
import semverCoerce from 'semver/functions/coerce';
import { RedirectAppLinks } from '../../../../../../../../../../../src/plugins/kibana_react/public';
import { TimeRange, esKuery } from '../../../../../../../../../../../src/plugins/data/public';
import { LogStream } from '../../../../../../../../../infra/public';
Expand All @@ -27,6 +29,7 @@ import { DatasetFilter } from './filter_dataset';
import { LogLevelFilter } from './filter_log_level';
import { LogQueryBar } from './query_bar';
import { buildQuery } from './build_query';
import { SelectLogLevel } from './select_log_level';

const WrapperFlexGroup = styled(EuiFlexGroup)`
height: 100%;
Expand Down Expand Up @@ -137,6 +140,18 @@ export const AgentLogs: React.FunctionComponent<{ agent: Agent }> = memo(({ agen
[logStreamQuery, dateRange.endExpression, dateRange.startExpression, http.basePath]
);

const agentVersion = agent.local_metadata?.elastic?.agent?.version;
const isLogLevelSelectionAvailable = useMemo(() => {
if (!agentVersion) {
return false;
}
const agentVersionWithPrerelease = semverCoerce(agentVersion)?.version;
if (!agentVersionWithPrerelease) {
return false;
}
return semverGte(agentVersionWithPrerelease, '7.11.0');
}, [agentVersion]);

return (
<WrapperFlexGroup direction="column" gutterSize="m">
<EuiFlexItem grow={false}>
Expand Down Expand Up @@ -213,6 +228,11 @@ export const AgentLogs: React.FunctionComponent<{ agent: Agent }> = memo(({ agen
/>
</EuiPanel>
</EuiFlexItem>
{isLogLevelSelectionAvailable && (
<EuiFlexItem grow={false}>
<SelectLogLevel agent={agent} />
</EuiFlexItem>
)}
</WrapperFlexGroup>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { memo, useState, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiSelect, EuiFormLabel, EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } from '@elastic/eui';
import { Agent } from '../../../../../types';
import { sendPostAgentAction, useStartServices } from '../../../../../hooks';
import { AGENT_LOG_LEVELS, DEFAULT_LOG_LEVEL } from './constants';

const LEVEL_VALUES = Object.values(AGENT_LOG_LEVELS);

export const SelectLogLevel: React.FC<{ agent: Agent }> = memo(({ agent }) => {
const { notifications } = useStartServices();
const [isLoading, setIsLoading] = useState(false);
const [agentLogLevel, setAgentLogLevel] = useState(
agent.local_metadata?.elastic?.agent?.log_level ?? DEFAULT_LOG_LEVEL
);
const [selectedLogLevel, setSelectedLogLevel] = useState(agentLogLevel);

const onClickApply = useCallback(() => {
setIsLoading(true);
async function send() {
try {
const res = await sendPostAgentAction(agent.id, {
action: {
type: 'SETTINGS',
data: {
log_level: selectedLogLevel,
},
},
});
if (res.error) {
throw res.error;
}
setAgentLogLevel(selectedLogLevel);
notifications.toasts.addSuccess(
i18n.translate('xpack.fleet.agentLogs.selectLogLevel.successText', {
defaultMessage: `Changed agent logging level to '{logLevel}'.`,
values: {
logLevel: selectedLogLevel,
},
})
);
} catch (error) {
notifications.toasts.addError(error, {
title: i18n.translate('xpack.fleet.agentLogs.selectLogLevel.errorTitleText', {
defaultMessage: 'Error updating agent logging level',
}),
});
}
setIsLoading(false);
}

send();
}, [notifications, selectedLogLevel, agent.id]);

return (
<EuiFlexGroup gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFormLabel htmlFor="selectAgentLogLevel">
<FormattedMessage
id="xpack.fleet.agentLogs.selectLogLevelLabelText"
defaultMessage="Agent logging level"
/>
</EuiFormLabel>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSelect
disabled={isLoading}
compressed={true}
id="selectAgentLogLevel"
value={selectedLogLevel}
onChange={(event) => {
setSelectedLogLevel(event.target.value);
}}
options={LEVEL_VALUES.map((level) => ({ text: level, value: level }))}
/>
</EuiFlexItem>
{agentLogLevel !== selectedLogLevel && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty
flush="left"
size="xs"
isLoading={isLoading}
disabled={agentLogLevel === selectedLogLevel}
iconType="refresh"
onClick={onClickApply}
>
{isLoading ? (
<FormattedMessage
id="xpack.fleet.agentLogs.updateButtonLoadingText"
defaultMessage="Applying changes..."
/>
) : (
<FormattedMessage
id="xpack.fleet.agentLogs.updateButtonText"
defaultMessage="Apply changes"
/>
)}
</EuiButtonEmpty>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
});
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/public/applications/fleet/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export {
PutAgentReassignResponse,
PostBulkAgentReassignRequest,
PostBulkAgentReassignResponse,
PostNewAgentActionResponse,
PostNewAgentActionRequest,
// API schemas - Enrollment API Keys
GetEnrollmentAPIKeysResponse,
GetEnrollmentAPIKeysRequest,
Expand Down

0 comments on commit fd30a17

Please sign in to comment.