From 3d6aa9f44df06eed78e563ffda175bd517e45114 Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Tue, 10 Aug 2021 00:09:26 -0400 Subject: [PATCH] [Stack Monitoring] Adds optional debug logging for Stack Monitoring (#107711) --- .../plugins/monitoring/server/config.test.ts | 5 +- x-pack/plugins/monitoring/server/config.ts | 2 + .../plugins/monitoring/server/debug_logger.ts | 85 +++++++++++++++++++ x-pack/plugins/monitoring/server/plugin.ts | 51 +++++------ .../plugins/monitoring/server/routes/index.ts | 15 +++- 5 files changed, 129 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/monitoring/server/debug_logger.ts diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 45b3e0720068..9a5699189241 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -7,8 +7,7 @@ import fs from 'fs'; import { when } from 'jest-when'; - -import { createConfig, configSchema } from './config'; +import { configSchema, createConfig } from './config'; const MOCKED_PATHS = [ '/proc/self/cgroup', @@ -71,6 +70,8 @@ describe('config schema', () => { "enabled": false, }, }, + "debug_log_path": "", + "debug_mode": false, "elasticsearch": Object { "apiVersion": "master", "customHeaders": Object {}, diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 8c411fb5c28a..98fd02b03539 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -25,6 +25,8 @@ export const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), + debug_mode: schema.boolean({ defaultValue: false }), + debug_log_path: schema.string({ defaultValue: '' }), ccs: schema.object({ enabled: schema.boolean({ defaultValue: true }), }), diff --git a/x-pack/plugins/monitoring/server/debug_logger.ts b/x-pack/plugins/monitoring/server/debug_logger.ts new file mode 100644 index 000000000000..0add1f12f030 --- /dev/null +++ b/x-pack/plugins/monitoring/server/debug_logger.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import fs from 'fs'; +import { MonitoringConfig } from './config'; +import { RouteDependencies } from './types'; + +export function decorateDebugServer( + _server: any, + config: MonitoringConfig, + logger: RouteDependencies['logger'] +) { + // bail if the proper config value is not set (extra protection) + if (!config.ui.debug_mode) { + return _server; + } + + // create a debug logger that will either write to file (if debug_log_path exists) or log out via logger + const debugLog = createDebugLogger({ path: config.ui.debug_log_path, logger }); + + return { + // maintain the rest of _server untouched + ..._server, + // TODO: replace any + route: (options: any) => { + const apiPath = options.path; + return _server.route({ + ...options, + // TODO: replace any + handler: async (req: any) => { + const { elasticsearch: cached } = req.server.plugins; + const apiRequestHeaders = req.headers; + req.server.plugins.elasticsearch = { + ...req.server.plugins.elasticsearch, + getCluster: (name: string) => { + const cluster = cached.getCluster(name); + return { + ...cluster, + // TODO: better types? + callWithRequest: async (_req: typeof req, type: string, params: any) => { + const result = await cluster.callWithRequest(_req, type, params); + + // log everything about this request -> query -> result + debugLog({ + api_path: apiPath, + referer_url: apiRequestHeaders.referer, + query: { + params, + result, + }, + }); + + return result; + }, + }; + }, + }; + return options.handler(req); + }, + }); + }, + }; +} + +function createDebugLogger({ + path, + logger, +}: { + path: string; + logger: RouteDependencies['logger']; +}) { + if (path.length > 0) { + const stream = fs.createWriteStream('./stack_monitoring_debug_log.ndjson', { flags: 'a' }); + return function logToFile(line: any) { + stream.write(JSON.stringify(line)); + }; + } else { + return function logToStdOut(line: any) { + logger.info(JSON.stringify(line)); + }; + } +} diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index aed48b739152..6bfa052a6fe8 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -6,53 +6,52 @@ */ import Boom from '@hapi/boom'; -import { i18n } from '@kbn/i18n'; -import { has, get } from 'lodash'; import { TypeOf } from '@kbn/config-schema'; +import { i18n } from '@kbn/i18n'; import { - Logger, - PluginInitializerContext, - KibanaRequest, - KibanaResponseFactory, CoreSetup, - ICustomClusterClient, CoreStart, CustomHttpResponseOptions, - ResponseError, + ICustomClusterClient, + KibanaRequest, + KibanaResponseFactory, + Logger, Plugin, + PluginInitializerContext, + ResponseError, SharedGlobalConfig, } from 'kibana/server'; +import { get, has } from 'lodash'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/server'; import { - LOGGING_TAG, + ALERTS, KIBANA_MONITORING_LOGGING_TAG, KIBANA_STATS_TYPE_MONITORING, - ALERTS, + LOGGING_TAG, SAVED_OBJECT_TELEMETRY, } from '../common/constants'; -import { MonitoringConfig, createConfig, configSchema } from './config'; -import { requireUIRoutes } from './routes'; -import { initBulkUploader } from './kibana_monitoring'; -import { initInfraSource } from './lib/logs/init_infra_source'; -import { registerCollectors } from './kibana_monitoring/collectors'; -import { registerMonitoringTelemetryCollection } from './telemetry_collection'; -import { LicenseService } from './license_service'; import { AlertsFactory } from './alerts'; +import { configSchema, createConfig, MonitoringConfig } from './config'; +import { instantiateClient } from './es_client/instantiate_client'; +import { initBulkUploader } from './kibana_monitoring'; +import { registerCollectors } from './kibana_monitoring/collectors'; +import { initInfraSource } from './lib/logs/init_infra_source'; +import { LicenseService } from './license_service'; +import { requireUIRoutes } from './routes'; +import { EndpointTypes, Globals } from './static_globals'; +import { registerMonitoringTelemetryCollection } from './telemetry_collection'; import { + IBulkUploader, + LegacyRequest, + LegacyShimDependencies, MonitoringCore, MonitoringLicenseService, MonitoringPluginSetup, - LegacyShimDependencies, - IBulkUploader, PluginsSetup, PluginsStart, - LegacyRequest, RequestHandlerContextMonitoringPlugin, } from './types'; -import { Globals, EndpointTypes } from './static_globals'; -import { instantiateClient } from './es_client/instantiate_client'; - // This is used to test the version of kibana const snapshotRegex = /-snapshot/i; @@ -192,7 +191,11 @@ export class MonitoringPlugin plugins ); - requireUIRoutes(this.monitoringCore, { + if (config.ui.debug_mode) { + this.log.info('MONITORING DEBUG MODE: ON'); + } + + requireUIRoutes(this.monitoringCore, config, { cluster, router, licenseService: this.licenseService, diff --git a/x-pack/plugins/monitoring/server/routes/index.ts b/x-pack/plugins/monitoring/server/routes/index.ts index 0f65fde1b296..05a8de96b4c0 100644 --- a/x-pack/plugins/monitoring/server/routes/index.ts +++ b/x-pack/plugins/monitoring/server/routes/index.ts @@ -4,14 +4,23 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - /* eslint import/namespace: ['error', { allowComputed: true }]*/ + +import { MonitoringConfig } from '../config'; +import { decorateDebugServer } from '../debug_logger'; +import { RouteDependencies } from '../types'; // @ts-ignore import * as uiRoutes from './api/v1/ui'; // namespace import -import { RouteDependencies } from '../types'; -export function requireUIRoutes(server: any, npRoute: RouteDependencies) { +export function requireUIRoutes( + _server: any, + config: MonitoringConfig, + npRoute: RouteDependencies +) { const routes = Object.keys(uiRoutes); + const server = config.ui.debug_mode + ? decorateDebugServer(_server, config, npRoute.logger) + : _server; routes.forEach((route) => { const registerRoute = uiRoutes[route]; // computed reference to module objects imported via namespace