From 098f870d5b0f70e6dc9f754d082f75af3a1c33ad Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 4 Nov 2020 12:29:32 -0700 Subject: [PATCH] [7.x] Usage collector readme (#82548) (#82637) Updates README with more details on `isReady` --- src/plugins/usage_collection/README.md | 15 +++++++++++++-- .../server/collector/collector.ts | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/plugins/usage_collection/README.md b/src/plugins/usage_collection/README.md index 5241cced7e4cac..33aa84396e5b56 100644 --- a/src/plugins/usage_collection/README.md +++ b/src/plugins/usage_collection/README.md @@ -8,7 +8,13 @@ To integrate with the telemetry services for usage collection of your feature, t ## Creating and Registering Usage Collector -All you need to provide is a `type` for organizing your fields, `schema` field to define the expected types of usage fields reported, and a `fetch` method for returning your usage data. Then you need to make the Telemetry service aware of the collector by registering it. +Your usage collector needs to provide +- a `type` for organizing your fields, +- `schema` field to define the expected types of usage fields reported, +- a `fetch` method for returning your usage data, and +- an `isReady` method (that returns true or false) for letting the telemetry service know if it needs to wait for any asynchronous action (initialization of clients or other services) before calling the `fetch` method. + +Then you need to make the Telemetry service aware of the collector by registering it. 1. Make sure `usageCollection` is in your optional Plugins: @@ -62,6 +68,8 @@ All you need to provide is a `type` for organizing your fields, `schema` field t total: 'long', }, }, + isReady: () => isCollectorFetchReady, // Method to return `true`/`false` or Promise(`true`/`false`) to confirm if the collector is ready for the `fetch` method to be called. + fetch: async (collectorFetchContext: CollectorFetchContext) => { // query ES or saved objects and get some data @@ -84,6 +92,9 @@ All you need to provide is a `type` for organizing your fields, `schema` field t Some background: - `MY_USAGE_TYPE` can be any string. It usually matches the plugin name. As a safety mechanism, we double check there are no duplicates at the moment of registering the collector. + +- `isReady` (added in v7.2.0 and v6.8.4) is a way for a usage collector to announce that some async process must finish first before it can return data in the `fetch` method (e.g. a client needs to ne initialized, or the task manager needs to run a task first). If any collector reports that it is not ready when we call its `fetch` method, we reset a flag to try again and, after a set amount of time, collect data from those collectors that are ready and skip any that are not. This means that if a collector returns `true` for `isReady` and it actually isn't ready to return data, there won't be telemetry data from that collector in that telemetry report (usually once per day). You should consider what it means if your collector doesn't return data in the first few documents when Kibana starts or, if we should wait for any other reason (e.g. the task manager needs to run your task first). If you need to tell telemetry collection to wait, you should implement this function with custom logic. If your `fetch` method can run without the need of any previous dependencies, then you can return true for `isReady` as shown in the example below. + - The `fetch` method needs to support multiple contexts in which it is called. For example, when stats are pulled from a Kibana Metricbeat module, the Beat calls Kibana's stats API to invoke usage collection. In this case, the `fetch` method is called as a result of an HTTP API request and `callCluster` wraps `callWithRequest` or `esClient` wraps `asCurrentUser`, where the request headers are expected to have read privilege on the entire `.kibana' index. The `fetch` method also exposes the saved objects client that will have the correct scope when the collectors' `fetch` method is called. @@ -154,7 +165,7 @@ If any of your properties is an array, the schema definition must follow the con ```ts export const myCollector = makeUsageCollector({ type: 'my_working_collector', - isReady: () => true, + isReady: () => true, // `fetch` doesn't require any validation for dependencies to be met fetch() { return { my_greeting: 'hello', diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index 73febc0183fc55..c04b087d4adf56 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -72,7 +72,7 @@ export interface CollectorOptions { type: string; init?: Function; /** - * Method to return `true`/`false` to confirm if the collector is ready for the `fetch` method to be called. + * Method to return `true`/`false` or Promise(`true`/`false`) to confirm if the collector is ready for the `fetch` method to be called. */ isReady: () => Promise | boolean; /** @@ -101,6 +101,7 @@ export class Collector { * @param {Function} options.init (optional) - initialization function * @param {Function} options.fetch - function to query data * @param {Function} options.formatForBulkUpload - optional + * @param {Function} options.isReady - method that returns a boolean or Promise of a boolean to indicate the collector is ready to report data * @param {Function} options.rest - optional other properties */ constructor(