pub struct Plugin<D, Q, R, S> { /* private fields */ }
Expand description

Main entrypoint into the Grafana plugin SDK.

A Plugin handles the negotiation with Grafana, adding gRPC health checks, serving the various Services, and gracefully exiting if configured.

Shutdown Handler

A plugin can be configured to gracefully shutdown by calling the Plugin::shutdown_handler method. This will spawn a new task which simply waits for any TCP connection on the given address, and triggers a graceful shutdown of the underlying gRPC server. This is helpful during development using Docker: a tool such as cargo-watch can be used to rebuild on changes, then use nc to trigger a shutdown of the plugin. Grafana will automatically plugins when their process exits, so this avoids having to restart the Grafana server on every change. An example configuration for this can be seen in the grafana-sample-backend-plugin-rust repository.

Type parameters

The four type parameters of a Plugin represent the services that this plugin is configured to run. When created using Plugin::new the plugin has the NoopService associated with each type, which simply does not register anything for this piece of functionality. Calling the various methods on Plugin (such as Plugin::data_service and Plugin::stream_service) will return a new Plugin with your plugin implementation registered for that functionality.

The type parameters stand for:

  • D, the diagnostic service
  • Q, the data service (‘Q’ stands for ‘query’, here)
  • R, the resource service
  • S, the streaming service

Example:

use futures_util::stream::FuturesOrdered;
use grafana_plugin_sdk::{backend, prelude::*};
use thiserror::Error;

#[derive(Clone)]
struct MyPlugin;

/// An error that may occur during a query.
///
/// This must store the `ref_id` of the query so that Grafana can line it up.
#[derive(Debug, Error)]
#[error("Error querying backend for {ref_id}")]
struct QueryError {
    ref_id: String,
};

impl backend::DataQueryError for QueryError {
    fn ref_id(self) -> String {
        self.ref_id
    }
}

#[tonic::async_trait]
impl backend::DataService for MyPlugin {

    /// The type of JSON data sent from Grafana to our backend plugin.
    ///
    /// This will correspond to the `TQuery` type parameter of the frontend
    /// datasource.
    ///
    /// We can use `serde_json::Value` if we want to accept any JSON.
    type Query = serde_json::Value;

    /// The type of error that could be returned by an individual query.
    type QueryError = QueryError;

    /// The type of iterator we're returning.
    ///
    /// In general the concrete type will be impossible to name in advance,
    /// so the `backend::BoxDataResponseStream` type alias will be useful.
    type Stream = backend::BoxDataResponseStream<Self::QueryError>;

    /// Respond to a request for data from Grafana.
    ///
    /// This request will contain zero or more queries, as well as information
    /// about the datasource instance on behalf of which this request is made,
    /// such as address, credentials, etc.
    ///
    /// Our plugin must respond to each query and return an iterator of `DataResponse`s,
    /// which themselves can contain zero or more `Frame`s.
    async fn query_data(&self, request: backend::QueryDataRequest<Self::Query>) -> Self::Stream {
        Box::pin(
            request
                .queries
                .into_iter()
                .map(|x| async {
                    // Here we create a single response Frame for each query.
                    // Frames can be created from iterators of fields using [`IntoFrame`].
                    Ok(backend::DataResponse::new(
                        x.ref_id.clone(),
                        // Return zero or more frames.
                        // A real implementation would fetch this data from a database
                        // or something.
                        vec![[
                            [1_u32, 2, 3].into_field("x"),
                            ["a", "b", "c"].into_field("y"),
                        ]
                        .into_frame("foo")
                        .check()
                        .map_err(|source| QueryError {
                            ref_id: x.ref_id,
                        })?],
                    ))
                })
                .collect::<FuturesOrdered<_>>(),
        )
    }
}

#[grafana_plugin_sdk::main(
    services(data),
    shutdown_handler = "0.0.0.0:10001",
)]
async fn plugin() -> MyPlugin {
    MyPlugin
}

Implementations

Create a new Plugin with no registered services.

Add a shutdown handler to the plugin, listening on the specified address.

The shutdown handler waits for a TCP connection on the specified address and requests that the server gracefully shutdown when any connection is made.

Initialize a default tracing_subscriber::fmt::Subscriber upon starting.

If enabled, this will initialize a Subscriber emitting logs to stderr in a format compatible with Grafana, at a default max level of info.

This effectively causes the following to be called as part of Plugin::start:

use grafana_plugin_sdk::backend;
use tracing_subscriber::{prelude::*, EnvFilter};

let filter = EnvFilter::try_from_default_env()
    .unwrap_or_else(|_| EnvFilter::new("info"));
tracing_subscriber::registry()
    .with(filter)
    .with(backend::layer())
    .init()

Add a data service to this plugin.

Add a diagnostics service to this plugin.

Add a resource service to this plugin.

Add a streaming service to this plugin.

Start the plugin.

This adds all of the configured services, spawns a shutdown handler (if configured), and blocks while the plugin runs.

Panics

This will panic if init_subscriber(true) has been set and another global subscriber has already been installed. If you are initializing your own Subscriber, you should instead use the layer function to add a Grafana-compatible tokio_subscriber::fmt::Layer to your subscriber.

Trait Implementations

Returns the “default value” for a type. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Wrap the input message T in a tonic::Request
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more