Struct grafana_plugin_sdk::backend::Plugin
source · 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 Service
s, 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 serviceQ
, the data service (‘Q’ stands for ‘query’, here)R
, the resource serviceS
, 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§
source§impl<D, Q, R, S> Plugin<D, Q, R, S>
impl<D, Q, R, S> Plugin<D, Q, R, S>
sourcepub fn shutdown_handler(self, address: SocketAddr) -> Self
pub fn shutdown_handler(self, address: SocketAddr) -> Self
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.
Note that this only takes effect in debug mode. The shutdown handler is never added when the crate is compiled in release mode.
sourcepub fn init_subscriber(self, init_subscriber: bool) -> Self
pub fn init_subscriber(self, init_subscriber: bool) -> Self
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()
sourcepub fn data_service<T>(self, service: T) -> Plugin<D, T, R, S>
pub fn data_service<T>(self, service: T) -> Plugin<D, T, R, S>
Add a data service to this plugin.
sourcepub fn diagnostics_service<T>(self, service: T) -> Plugin<T, Q, R, S>
pub fn diagnostics_service<T>(self, service: T) -> Plugin<T, Q, R, S>
Add a diagnostics service to this plugin.
sourcepub fn resource_service<T>(self, service: T) -> Plugin<D, Q, T, S>
pub fn resource_service<T>(self, service: T) -> Plugin<D, Q, T, S>
Add a resource service to this plugin.
sourcepub fn stream_service<T>(self, service: T) -> Plugin<D, Q, R, T>
pub fn stream_service<T>(self, service: T) -> Plugin<D, Q, R, T>
Add a streaming service to this plugin.
source§impl<D, Q, R, S> Plugin<D, Q, R, S>where
D: DiagnosticsService + Send + Sync + 'static,
Q: DataService + Send + Sync + 'static,
R: ResourceService + Send + Sync + 'static,
S: StreamService + Send + Sync + 'static,
impl<D, Q, R, S> Plugin<D, Q, R, S>where
D: DiagnosticsService + Send + Sync + 'static,
Q: DataService + Send + Sync + 'static,
R: ResourceService + Send + Sync + 'static,
S: StreamService + Send + Sync + 'static,
sourcepub async fn start(self, listener: TcpListener) -> Result<(), Error>
pub async fn start(self, listener: TcpListener) -> Result<(), Error>
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§
Auto Trait Implementations§
impl<D, Q, R, S> RefUnwindSafe for Plugin<D, Q, R, S>
impl<D, Q, R, S> Send for Plugin<D, Q, R, S>
impl<D, Q, R, S> Sync for Plugin<D, Q, R, S>
impl<D, Q, R, S> Unpin for Plugin<D, Q, R, S>
impl<D, Q, R, S> UnwindSafe for Plugin<D, Q, R, S>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T
in a tonic::Request