Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some collector classes for objects that get registered in a CollectorSet #19098

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* 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.
*/

export class Collector {
/*
* @param {String} type.type - property name as the key for the data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: all of these have an extraneous type. prefix now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're adding a server param before this object in the args, so I'm keeping the prefix but calling it properties which matches what it's called in UsageCollector

* @param {Function} type.init (optional) - initialization function
* @param {Function} type.fetch - function to query data
* @param {Function} type.cleanup (optional) - cleanup function
* @param {Boolean} type.fetchAfterInit (optional) - if collector should fetch immediately after init
*/
constructor({ type, init, fetch, cleanup, fetchAfterInit }) {
this.type = type;
this.init = init;
this.fetch = fetch;
this.cleanup = cleanup;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird blank line makes this extra field seem special.

this.fetchAfterInit = fetchAfterInit;
}

/*
* Allows using `server.log('debug', message)` as `this.log.debug(message)`.
* Works for info and warn logs as well.
*/
setLogger(logger) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could get this without making the Collector mutable by passing in the server object to the constructor, then avoid having a custom logger altogether. It would also avoid the implicit requirement that it gets defined after construction.

this.log = logger;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { flatten, isEmpty } from 'lodash';
import { LOGGING_TAG, KIBANA_MONITORING_LOGGING_TAG } from '../../../common/constants';
import Promise from 'bluebird';
import { UsageCollector } from './usage_collector';

const LOGGING_TAGS = [LOGGING_TAG, KIBANA_MONITORING_LOGGING_TAG];

Expand Down Expand Up @@ -52,13 +53,10 @@ export class CollectorSet {
}

/*
* @param {String} type.type
* @param {Function} type.init (optional)
* @param {Function} type.fetch
* @param {Function} type.cleanup (optional)
* @param {Collector} collector object
*/
register(type) {
this._collectors.push(type);
register(collector) {
this._collectors.push(collector);
}

/*
Expand Down Expand Up @@ -139,6 +137,19 @@ export class CollectorSet {
});
}

async bulkFetchUsage() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this isn't used yet. See #18894 for where it'll be used from an HTTP API

cc @ruflin the API will call this function from the handler, meaning that the stats will get collected whenever the API is called. No internal caching

const usageCollectors = this._collectors.filter(c => c instanceof UsageCollector);
const bulk = await this._bulkFetch(usageCollectors);

// summarize each type of stat
return bulk.reduce((accumulatedStats, currentStat) => {
return {
...accumulatedStats,
[currentStat.type]: currentStat.result,
};
}, {});
}

cleanup() {
this._log.info(`Stopping all stats collectors`);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 { Collector } from './collector';

export class UsageCollector extends Collector {
constructor(properties) {
super(properties);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { get, snakeCase } from 'lodash';
import { KIBANA_USAGE_TYPE } from '../../../common/constants';
import { UsageCollector } from '../classes/usage_collector';

const TYPES = [
'dashboard',
Expand All @@ -20,7 +21,7 @@ const TYPES = [
* Fetches saved object client counts by querying the saved object index
*/
export function getKibanaUsageCollector(server, callCluster) {
return {
return new UsageCollector({
type: KIBANA_USAGE_TYPE,
async fetch() {
const index = server.config().get('kibana.index');
Expand Down Expand Up @@ -62,5 +63,5 @@ export function getKibanaUsageCollector(server, callCluster) {
}), {})
};
}
};
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { KIBANA_STATS_TYPE } from '../../../common/constants';
import { opsBuffer } from './ops_buffer';
import { Collector } from '../classes/collector';

/*
* Initialize a collector for Kibana Ops Stats
Expand All @@ -20,11 +21,6 @@ export function getOpsStatsCollector(server) {
monitor.on('ops', onOps);
};

let _log;
const setLogger = logger => {
_log = logger;
};

const cleanup = () => {
if (monitor) {
monitor.removeListener('ops', onOps);
Expand All @@ -34,24 +30,23 @@ export function getOpsStatsCollector(server) {
// This needs to be removed once the FIXME for 1301 is fixed
// `process` is a NodeJS global, and is always available without using require/import
process.on('SIGHUP', () => {
_log.info('Re-initializing Kibana Monitoring due to SIGHUP');
this.log.info('Re-initializing Kibana Monitoring due to SIGHUP');
/* This timeout is a temporary stop-gap until collecting stats is not bound to even-better
* and collecting stats is not interfered by logging configuration reloading
* Related to https://github.com/elastic/x-pack-kibana/issues/1301
*/
setTimeout(() => {
cleanup();
init();
_log.info('Re-initialized Kibana Monitoring due to SIGHUP');
this.log.info('Re-initialized Kibana Monitoring due to SIGHUP');
}, 5 * 1000); // wait 5 seconds to avoid race condition with reloading logging configuration
});

return {
return new Collector({
type: KIBANA_STATS_TYPE,
init,
setLogger,
fetch: buffer.flush,
fetchAfterInit: true,
cleanup
};
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
*/

import { get } from 'lodash';

import { XPACK_DEFAULT_ADMIN_EMAIL_UI_SETTING } from '../../../../../server/lib/constants';
import { KIBANA_SETTINGS_TYPE } from '../../../common/constants';
import { Collector } from '../classes/collector';

/*
* Check if Cluster Alert email notifications is enabled in config
Expand Down Expand Up @@ -58,36 +58,28 @@ export function getSettingsCollector(server) {
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
const config = server.config();

let _log;
const setLogger = logger => {
_log = logger;
};

const fetch = async () => {
let kibanaSettingsData;
const defaultAdminEmail = await checkForEmailValue(config, callWithInternalUser);

// skip everything if defaultAdminEmail === undefined
if (defaultAdminEmail || (defaultAdminEmail === null && shouldUseNull)) {
kibanaSettingsData = {
xpack: {
default_admin_email: defaultAdminEmail
}
};
_log.debug(`[${defaultAdminEmail}] default admin email setting found, sending [${KIBANA_SETTINGS_TYPE}] monitoring document.`);
} else {
_log.debug(`not sending [${KIBANA_SETTINGS_TYPE}] monitoring document because [${defaultAdminEmail}] is null or invalid.`);
}

// remember the current email so that we can mark it as successful if the bulk does not error out
shouldUseNull = !!defaultAdminEmail;

return kibanaSettingsData;
};

return {
return new Collector({
type: KIBANA_SETTINGS_TYPE,
setLogger,
fetch
};
async fetch() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had to move this function into the object literal for context to work

let kibanaSettingsData;
const defaultAdminEmail = await checkForEmailValue(config, callWithInternalUser);

// skip everything if defaultAdminEmail === undefined
if (defaultAdminEmail || (defaultAdminEmail === null && shouldUseNull)) {
kibanaSettingsData = {
xpack: {
default_admin_email: defaultAdminEmail
}
};
this.log.debug(`[${defaultAdminEmail}] default admin email setting found, sending [${KIBANA_SETTINGS_TYPE}] monitoring document.`);
} else {
this.log.debug(`not sending [${KIBANA_SETTINGS_TYPE}] monitoring document because [${defaultAdminEmail}] is null or invalid.`);
}

// remember the current email so that we can mark it as successful if the bulk does not error out
shouldUseNull = !!defaultAdminEmail;

return kibanaSettingsData;
}
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
export { createCollectorSet } from './create_collector_set';
export { getKibanaUsageCollector } from './collectors/get_kibana_usage_collector';
export { getReportingUsageCollector } from './collectors/get_reporting_usage_collector';
export { UsageCollector } from './classes/usage_collector';
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { callClusterFactory } from '../../../xpack_main';
import { CollectorSet } from './lib/collector_set';
import { CollectorSet } from './classes/collector_set';
import { getOpsStatsCollector } from './collectors/get_ops_stats_collector';
import { getSettingsCollector } from './collectors/get_settings_collector';
import { getKibanaUsageCollector } from './collectors/get_kibana_usage_collector';
Expand Down