grafana/docs/sources/developers/plugins/add-anonymous-usage-reporting.md
Jack Baldry c1c48dd610
Use relative aliases for all non-current Grafana aliases (#60062)
* Use relative aliases for all non-current Grafana aliases

Prevents non-latest documentation "stealing" the page away from latest
and through permanent redirects for latest pages that no longer exist.

The redirected pages are indexed by search engines but our robots.txt
forbids them crawling the non-latest page.

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Remove aliases from shared pages

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Rewrite all current latest aliases to be next

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Fix typo in latest alias

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Remove all current page aliases

find docs/sources -type f -name '*.md' -exec sed -z -i 's#\n *- /docs/grafana/next/[^\n]*\n#\n#' {} \;
find docs/sources -type f -name '*.md' -exec sed -Ez -i 's#\n((aliases:\n *-)|aliases:\n)#\n\2#' {} \;

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Prettier

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
2022-12-09 12:36:04 -04:00

4.5 KiB

title
Add anonymous usage reporting

Add anonymous usage reporting to you plugin

The Grafana server administrator has the possibility to configure [anonymous usage tracking]({{< relref "../../setup-grafana/configure-grafana/#reporting_enabled" >}}).

By adding usage tracking to your plugin you will send events of how your plugin is being used to the configured tracking system.

Lets say we have a QueryEditor that looks something like the example below. It has an editor field where you can write your query and a query type selector so you can select what kind of query result you are expecting that query to return.

import React, { ReactElement } from 'react';
import { InlineFieldRow, InlineField, Select, CodeEditor } from '@grafana/ui';
import type { EditorProps } from './types';

export function QueryEditor(props: EditorProps): ReactElement {
  const { datasource, query, onChange, onRunQuery } = props;
  const queryType = { value: query.value ?? 'timeserie' };
  const queryTypes = [
    {
      label: 'Timeserie',
      value: 'timeserie',
    },
    {
      label: 'Table',
      value: 'table',
    },
  ];

  const onChangeQueryType = (type: string) => {
    onChange({
      ...query,
      queryType: type,
    });
    runQuery();
  };

  const onChangeRawQuery = (rawQuery: string) => {
    onChange({
      ...query,
      rawQuery: type,
    });
    runQuery();
  };

  return (
    <>
      <div>
        <CodeEditor
          height="200px"
          showLineNumbers={true}
          language="sql"
          onBlur={onChangeRawQuery}
          value={query.rawQuery}
        />
      </div>
      <InlineFieldRow>
        <InlineField label="Query type" grow>
          <Select options={queryTypes} onChange={onChangeQueryType} value={queryType} />
        </InlineField>
      </InlineFieldRow>
    </>
  );
}

Lets say that we would like to track how the usage looks between time series and table queries. All you need to do is to add the usePluginInteractionReporter to fetch a report function which takes two arguments. The first one is the event name which is used to identify the interaction being made. It need to start with grafana_plugin_ which makes it easier to differentiate plugin events from Grafana core events. The second argument is optional and should be used to attach contextual data to the event. In our example, that would be the query type. It is optional because it does not make sense to pass contextual data for all user interactions.

import React, { ReactElement } from 'react';
import { InlineFieldRow, InlineField, Select, CodeEditor } from '@grafana/ui';
import { usePluginInteractionReporter } from '@grafana/runtime';
import type { EditorProps } from './types';

export function QueryEditor(props: EditorProps): ReactElement {
  const { datasource, query, onChange, onRunQuery } = props;
  const report = usePluginInteractionReporter();

  const queryType = { value: query.value ?? 'timeserie' };
  const queryTypes = [
    {
      label: 'Timeserie',
      value: 'timeserie',
    },
    {
      label: 'Table',
      value: 'table',
    },
  ];

  const onChangeQueryType = (type: string) => {
    onChange({
      ...query,
      queryType: type,
    });
    runQuery();
  };

  const onChangeRawQuery = (rawQuery: string) => {
    onChange({
      ...query,
      rawQuery: type,
    });

    report('grafana_plugin_executed_query', {
      query_type: queryType.value,
    });

    runQuery();
  };

  return (
    <>
      <div>
        <CodeEditor
          height="200px"
          showLineNumbers={true}
          language="sql"
          onBlur={onChangeRawQuery}
          value={query.rawQuery}
        />
      </div>
      <InlineFieldRow>
        <InlineField label="Query type" grow>
          <Select options={queryTypes} onChange={onChangeQueryType} value={queryType} />
        </InlineField>
      </InlineFieldRow>
    </>
  );
}

Another benefit of using the usePluginInteractionReporter is that the report function that is handed back to you will automatically attach contextual data about the plugin you are tracking to every event. In our example the following information will be sent to the analytics service configured by the Grafana server administrator.

{
  type: 'interaction',
  payload: {
    interactionName: 'grafana_plugin_executed_query',
    grafana_version: '9.2.1',
    plugin_type: 'datasource',
    plugin_version: '1.0.0',
    plugin_id: 'grafana-example-datasource',
    plugin_name: 'Example',
    datasource_uid: 'qeSI8VV7z', // will only be added for datasources
    query_type: 'timeserie'
  }
}