mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
Use docLinks plugin to generate server side URL (#147940)
Resolves: https://github.com/elastic/kibana/issues/109937
In this PR, I'm fixing the `Task Manager detected a degradation in
performance` log message to provide the proper versioned link to the
task manager health monitoring docs. Prior to this PR, it would always
point to main / master docs.
## To verify
1. Turn on debug logging in your kibana.yml file
```
logging:
loggers:
- name: plugins.taskManager
level: debug
```
2. Move this code line outside of the `if (logLevel..` statement =>
4c7ce9d249/x-pack/plugins/task_manager/server/lib/log_health_metrics.ts (L99)
3. Startup Kibana
4. Notice the `Task Manager detected a degradation in performance...`
logged
5. Test the URL provided by the log message
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2ca590e006
commit
750e5e0e95
8 changed files with 71 additions and 32 deletions
|
@ -490,6 +490,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
|
|||
teamsAction: `${KIBANA_DOCS}teams-action-type.html#configuring-teams`,
|
||||
connectors: `${KIBANA_DOCS}action-types.html`,
|
||||
},
|
||||
taskManager: {
|
||||
healthMonitoring: `${KIBANA_DOCS}task-manager-health-monitoring.html`,
|
||||
},
|
||||
maps: {
|
||||
guide: `${KIBANA_DOCS}maps.html`,
|
||||
importGeospatialPrivileges: `${KIBANA_DOCS}import-geospatial-data.html#import-geospatial-privileges`,
|
||||
|
|
|
@ -351,7 +351,31 @@ export interface DocLinks {
|
|||
syntheticsCommandReference: string;
|
||||
syntheticsProjectMonitors: string;
|
||||
}>;
|
||||
readonly alerting: Record<string, string>;
|
||||
readonly alerting: Readonly<{
|
||||
guide: string;
|
||||
actionTypes: string;
|
||||
apmRules: string;
|
||||
emailAction: string;
|
||||
emailActionConfig: string;
|
||||
emailExchangeClientSecretConfig: string;
|
||||
emailExchangeClientIdConfig: string;
|
||||
generalSettings: string;
|
||||
indexAction: string;
|
||||
esQuery: string;
|
||||
indexThreshold: string;
|
||||
pagerDutyAction: string;
|
||||
preconfiguredConnectors: string;
|
||||
preconfiguredAlertHistoryConnector: string;
|
||||
serviceNowAction: string;
|
||||
serviceNowSIRAction: string;
|
||||
setupPrerequisites: string;
|
||||
slackAction: string;
|
||||
teamsAction: string;
|
||||
connectors: string;
|
||||
}>;
|
||||
readonly taskManager: Readonly<{
|
||||
healthMonitoring: string;
|
||||
}>;
|
||||
readonly maps: Readonly<{
|
||||
guide: string;
|
||||
importGeospatialPrivileges: string;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
import { merge } from 'lodash';
|
||||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { loggingSystemMock, docLinksServiceMock } from '@kbn/core/server/mocks';
|
||||
import { configSchema, TaskManagerConfig } from '../config';
|
||||
import { HealthStatus } from '../monitoring';
|
||||
import { MonitoredHealth } from '../routes/health';
|
||||
|
@ -18,6 +18,8 @@ jest.mock('./calculate_health_status', () => ({
|
|||
}));
|
||||
|
||||
describe('logHealthMetrics', () => {
|
||||
const docLinks = docLinksServiceMock.create().setup();
|
||||
|
||||
afterEach(() => {
|
||||
const { calculateHealthStatus } = jest.requireMock('./calculate_health_status');
|
||||
// Reset the last state by running through this as OK
|
||||
|
@ -40,16 +42,16 @@ describe('logHealthMetrics', () => {
|
|||
|
||||
// We must change from OK to Warning
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.OK);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(
|
||||
() => HealthStatus.Warning
|
||||
);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
// We must change from OK to Error
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.OK);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.Error);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const debugCalls = (logger as jest.Mocked<Logger>).debug.mock.calls;
|
||||
const performanceMessage = /^Task Manager detected a degradation in performance/;
|
||||
|
@ -78,9 +80,9 @@ describe('logHealthMetrics', () => {
|
|||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(
|
||||
() => HealthStatus.Warning
|
||||
);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.OK);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
expect((logger as jest.Mocked<Logger>).warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -99,9 +101,9 @@ describe('logHealthMetrics', () => {
|
|||
|
||||
// We must change from Error to OK
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.Error);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.OK);
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
expect((logger as jest.Mocked<Logger>).warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -116,7 +118,7 @@ describe('logHealthMetrics', () => {
|
|||
});
|
||||
const health = getMockMonitoredHealth();
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const firstDebug = JSON.parse(
|
||||
(logger as jest.Mocked<Logger>).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '')
|
||||
|
@ -135,7 +137,7 @@ describe('logHealthMetrics', () => {
|
|||
});
|
||||
const health = getMockMonitoredHealth();
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const firstInfo = JSON.parse(
|
||||
(logger as jest.Mocked<Logger>).info.mock.calls[0][0].replace('Latest Monitored Stats: ', '')
|
||||
|
@ -154,7 +156,7 @@ describe('logHealthMetrics', () => {
|
|||
});
|
||||
const health = getMockMonitoredHealth();
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const firstDebug = JSON.parse(
|
||||
(logger as jest.Mocked<Logger>).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '')
|
||||
|
@ -177,7 +179,7 @@ describe('logHealthMetrics', () => {
|
|||
() => HealthStatus.Warning
|
||||
);
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const logMessage = JSON.parse(
|
||||
((logger as jest.Mocked<Logger>).warn.mock.calls[0][0] as string).replace(
|
||||
|
@ -201,7 +203,7 @@ describe('logHealthMetrics', () => {
|
|||
const { calculateHealthStatus } = jest.requireMock('./calculate_health_status');
|
||||
(calculateHealthStatus as jest.Mock<HealthStatus>).mockImplementation(() => HealthStatus.Error);
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const logMessage = JSON.parse(
|
||||
((logger as jest.Mocked<Logger>).error.mock.calls[0][0] as string).replace(
|
||||
|
@ -241,7 +243,7 @@ describe('logHealthMetrics', () => {
|
|||
},
|
||||
});
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
expect((logger as jest.Mocked<Logger>).warn.mock.calls[0][0] as string).toBe(
|
||||
`Detected delay task start of 60s for task(s) \"taskType:test\" (which exceeds configured value of 60s)`
|
||||
|
@ -285,7 +287,7 @@ describe('logHealthMetrics', () => {
|
|||
},
|
||||
});
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
expect((logger as jest.Mocked<Logger>).warn.mock.calls[0][0] as string).toBe(
|
||||
`Detected delay task start of 60s for task(s) \"taskType:test, taskType:test2\" (which exceeds configured value of 60s)`
|
||||
|
@ -317,7 +319,7 @@ describe('logHealthMetrics', () => {
|
|||
stats: {},
|
||||
};
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const firstDebug = JSON.parse(
|
||||
(logger as jest.Mocked<Logger>).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '')
|
||||
|
@ -354,7 +356,7 @@ describe('logHealthMetrics', () => {
|
|||
},
|
||||
});
|
||||
|
||||
logHealthMetrics(health, logger, config, false);
|
||||
logHealthMetrics(health, logger, config, false, docLinks);
|
||||
|
||||
const firstDebug = JSON.parse(
|
||||
(logger as jest.Mocked<Logger>).debug.mock.calls[0][0].replace('Latest Monitored Stats: ', '')
|
||||
|
@ -379,7 +381,7 @@ describe('logHealthMetrics', () => {
|
|||
},
|
||||
});
|
||||
|
||||
logHealthMetrics(health, logger, config, true);
|
||||
logHealthMetrics(health, logger, config, true, docLinks);
|
||||
|
||||
const { calculateHealthStatus } = jest.requireMock('./calculate_health_status');
|
||||
expect(calculateHealthStatus).toBeCalledTimes(1);
|
||||
|
|
|
@ -5,10 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { kibanaPackageJson } from '@kbn/repo-info';
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Logger } from '@kbn/core/server';
|
||||
import { Logger, DocLinksServiceSetup } from '@kbn/core/server';
|
||||
import { HealthStatus } from '../monitoring';
|
||||
import { TaskManagerConfig } from '../config';
|
||||
import { MonitoredHealth } from '../routes/health';
|
||||
|
@ -29,7 +27,8 @@ export function logHealthMetrics(
|
|||
monitoredHealth: MonitoredHealth,
|
||||
logger: Logger,
|
||||
config: TaskManagerConfig,
|
||||
shouldRunTasks: boolean
|
||||
shouldRunTasks: boolean,
|
||||
docLinks: DocLinksServiceSetup
|
||||
) {
|
||||
let logLevel: LogLevel =
|
||||
config.monitored_stats_health_verbose_log.level === 'info' ? LogLevel.Info : LogLevel.Debug;
|
||||
|
@ -54,10 +53,7 @@ export function logHealthMetrics(
|
|||
}
|
||||
|
||||
const message = `Latest Monitored Stats: ${JSON.stringify(monitoredHealth)}`;
|
||||
// TODO: remove when docs support "main"
|
||||
const docsBranch = kibanaPackageJson.branch === 'main' ? 'master' : 'main';
|
||||
|
||||
const docLink = `https://www.elastic.co/guide/en/kibana/${docsBranch}/task-manager-health-monitoring.html`;
|
||||
const docLink = docLinks.links.taskManager.healthMonitoring;
|
||||
const detectedProblemMessage = `Task Manager detected a degradation in performance. This is usually temporary, and Kibana can recover automatically. If the problem persists, check the docs for troubleshooting information: ${docLink} .`;
|
||||
|
||||
// Drift looks at runtime stats which are not available when task manager is not running tasks
|
||||
|
|
|
@ -135,6 +135,7 @@ export class TaskManagerPlugin
|
|||
getClusterClient: () =>
|
||||
startServicesPromise.then(({ elasticsearch }) => elasticsearch.client),
|
||||
shouldRunTasks: this.shouldRunBackgroundTasks,
|
||||
docLinks: core.docLinks,
|
||||
});
|
||||
const monitoredUtilization$ = backgroundTaskUtilizationRoute({
|
||||
router,
|
||||
|
@ -200,6 +201,7 @@ export class TaskManagerPlugin
|
|||
savedObjects,
|
||||
elasticsearch,
|
||||
executionContext,
|
||||
docLinks,
|
||||
}: CoreStart): TaskManagerStartContract {
|
||||
const savedObjectsRepository = savedObjects.createInternalRepository(['task']);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Observable, of, Subject } from 'rxjs';
|
|||
import { take } from 'rxjs/operators';
|
||||
import { merge } from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
import { httpServiceMock } from '@kbn/core/server/mocks';
|
||||
import { httpServiceMock, docLinksServiceMock } from '@kbn/core/server/mocks';
|
||||
import { healthRoute } from './health';
|
||||
import { mockHandlerArguments } from './_mock_handler_arguments';
|
||||
import { sleep } from '../test_utils';
|
||||
|
@ -47,6 +47,7 @@ const createMockClusterClient = (response: any) => {
|
|||
|
||||
describe('healthRoute', () => {
|
||||
const logger = loggingSystemMock.create().get();
|
||||
const docLinks = docLinksServiceMock.create().setup();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -65,6 +66,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
const [config] = router.get.mock.calls[0];
|
||||
|
@ -88,6 +90,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(mockClusterClient),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
const [, handler] = router.get.mock.calls[0];
|
||||
|
@ -129,6 +132,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(mockClusterClient),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
const [, handler] = router.get.mock.calls[0];
|
||||
|
@ -175,6 +179,7 @@ describe('healthRoute', () => {
|
|||
kibanaIndexName: 'foo',
|
||||
getClusterClient: () => Promise.resolve(mockClusterClient),
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
const [, handler] = router.get.mock.calls[0];
|
||||
|
@ -218,6 +223,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
stats$.next(mockStat);
|
||||
|
@ -278,6 +284,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
stats$.next(warnRuntimeStat);
|
||||
|
@ -356,6 +363,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
stats$.next(errorRuntimeStat);
|
||||
|
@ -420,6 +428,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
const serviceStatus = getLatest(serviceStatus$);
|
||||
|
@ -502,6 +511,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
|
@ -576,6 +586,7 @@ describe('healthRoute', () => {
|
|||
getClusterClient: () => Promise.resolve(elasticsearchServiceMock.createClusterClient()),
|
||||
usageCounter: mockUsageCounter,
|
||||
shouldRunTasks: true,
|
||||
docLinks,
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
IKibanaResponse,
|
||||
KibanaResponseFactory,
|
||||
} from '@kbn/core/server';
|
||||
import { IClusterClient } from '@kbn/core/server';
|
||||
import { IClusterClient, DocLinksServiceSetup } from '@kbn/core/server';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { tap, map } from 'rxjs/operators';
|
||||
import { throttleTime } from 'rxjs/operators';
|
||||
|
@ -60,6 +60,7 @@ export interface HealthRouteParams {
|
|||
shouldRunTasks: boolean;
|
||||
getClusterClient: () => Promise<IClusterClient>;
|
||||
usageCounter?: UsageCounter;
|
||||
docLinks: DocLinksServiceSetup;
|
||||
}
|
||||
|
||||
export function healthRoute(params: HealthRouteParams): {
|
||||
|
@ -77,6 +78,7 @@ export function healthRoute(params: HealthRouteParams): {
|
|||
getClusterClient,
|
||||
usageCounter,
|
||||
shouldRunTasks,
|
||||
docLinks,
|
||||
} = params;
|
||||
|
||||
// if "hot" health stats are any more stale than monitored_stats_required_freshness (pollInterval +1s buffer by default)
|
||||
|
@ -111,7 +113,7 @@ export function healthRoute(params: HealthRouteParams): {
|
|||
.subscribe(([monitoredHealth, serviceStatus]) => {
|
||||
serviceStatus$.next(serviceStatus);
|
||||
monitoredHealth$.next(monitoredHealth);
|
||||
logHealthMetrics(monitoredHealth, logger, config, shouldRunTasks);
|
||||
logHealthMetrics(monitoredHealth, logger, config, shouldRunTasks, docLinks);
|
||||
});
|
||||
|
||||
router.get(
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"@kbn/utility-types",
|
||||
"@kbn/safer-lodash-set",
|
||||
"@kbn/es-types",
|
||||
"@kbn/repo-info",
|
||||
"@kbn/apm-utils",
|
||||
],
|
||||
"exclude": [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue