一、插件概述

1. 插件类型与结构简介
Grafana 插件主要分为三类:
- 数据源插件(Data Source Plugin):扩展数据源类型,实现自定义数据查询。
- 面板插件(Panel Plugin):扩展可视化图表类型,如自定义饼图、地图等。
- 应用插件(App Plugin):集成数据源、面板、路由等,适合完整业务集成。
插件目录结构示例:
my-plugin/
├── src/ # 源码目录
│ ├── module.tsx # 主入口
│ ├── ... # 其它组件/逻辑
├── plugin.json # 插件声明配置
├── README.md
├── package.json
├── tsconfig.json
└── ...
2. 开发环境准备
- Node.js(建议 v18+)
- yarn 或 npm
- Grafana 本地环境(建议用 Docker 快速搭建)
安装官方插件开发工具:
npm install -g @grafana/toolkit
3. 插件项目初始化
使用官方脚手架快速创建:
npx @grafana/create-plugin my-plugin
cd my-plugin
yarn install
根据提示选择插件类型(panel/data source/app)。
4. plugin.json 配置详解
plugin.json 是插件的声明文件,决定插件类型、入口、支持的功能等。
面板插件示例:
{
"type": "panel",
"name": "My Custom Panel",
"id": "myorg-my-custom-panel",
"info": {
"description": "A custom panel plugin",
"author": {
"name": "Your Name"
},
"keywords": ["panel", "custom"],
"version": "1.0.0"
},
"dependencies": {
"grafanaDependency": ">=10.0.0",
"plugins": []
},
"includes": [
{
"type": "panel",
"name": "My Custom Panel"
}
]
}
数据源插件示例:
{
"type": "datasource",
"name": "My Data Source",
"id": "myorg-my-datasource",
"info": {
"description": "A custom data source plugin",
"author": { "name": "Your Name" },
"version": "1.0.0"
},
"dependencies": {
"grafanaDependency": ">=10.0.0"
},
"includes": [
{ "type": "datasource", "name": "My Data Source" }
]
}
主要字段说明:
type:插件类型(panel/datasource/app)name/id:名称与唯一ID(发布到市场必须唯一)info:描述、作者、版本dependencies:Grafana 版本依赖includes:声明包含的功能
5. 前端与后端开发配置
前端(React/TypeScript)
src/module.tsx是主入口,导出插件组件。- 面板插件需实现
PanelPlugin,数据源插件需实现DataSourcePlugin。 - 配置项通过
PanelOptions或DataSourceOptions实现,支持自定义表单控件。
后端(可选,Go)
- 部分数据源插件支持后端安全代理,需实现 Go 后端服务。
- 后端入口配置在
plugin.json的backend字段。 - 官方 SDK:https://grafana.com/developers/plugin-tools/backend/
6. 插件本地调试与打包
本地开发调试
- 在 Grafana 配置文件
grafana.ini中启用开发模式:[plugins] allow_loading_unsigned_plugins = myorg-my-custom-panel - 启动 Grafana 服务(推荐用 Docker):
docker run -d -p 3000:3000 \ -v $(pwd)/my-plugin/dist:/var/lib/grafana/plugins/myorg-my-custom-panel \ -e "GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=myorg-my-custom-panel" \ grafana/grafana:latest - 启动插件开发服务器:
yarn dev或使用 toolkit:grafana-toolkit plugin:dev
打包发布
yarn build
打包后的文件在 dist/ 目录,将其拷贝到 Grafana 的插件目录即可。
7. 插件安装与部署
- 将
dist目录拷贝到 Grafana 的插件目录(如/var/lib/grafana/plugins/)。 - 修改配置文件允许加载未签名插件(生产环境建议签名)。
- 重启 Grafana 服务。
二、开发案例实战
1、面板插件开发详细代码案例(TypeScript + React)
1.1. 快速初始化项目
npx @grafana/create-plugin my-panel-plugin
cd my-panel-plugin
yarn install
选择 panel 类型。
1.2. plugin.json 配置(核心字段)
{
"type": "panel",
"name": "Demo Panel",
"id": "myorg-demo-panel",
"info": {
"description": "A demo panel plugin",
"author": { "name": "Your Name" },
"version": "1.0.0"
},
"dependencies": {
"grafanaDependency": ">=10.0.0"
},
"includes": [
{ "type": "panel", "name": "Demo Panel" }
]
}
1.3. 主入口代码(src/module.tsx)
import { PanelProps } from '@grafana/data';
import { SimpleOptions } from './types';
import React from 'react';
interface Props extends PanelProps<SimpleOptions> {}
export const DemoPanel: React.FC<Props> = ({ options, data, width, height }) => {
// 获取数据
const series = data.series[0];
const values = series ? series.fields[1].values.toArray() : [];
// 简单渲染
return (
<div style={{ width, height, padding: 20 }}>
<h2>{options.title || 'Demo Panel'}</h2>
<ul>
{values.map((v, i) => (
<li key={i}>{v}</li>
))}
</ul>
</div>
);
};
// 注册插件
import { PanelPlugin } from '@grafana/data';
export const plugin = new PanelPlugin<SimpleOptions>(DemoPanel)
.setPanelOptions(builder => {
builder
.addTextInput({
path: 'title',
name: 'Panel Title',
defaultValue: 'Demo Panel',
});
});
1.4. 配置项 types(src/types.ts)
export interface SimpleOptions {
title?: string;
}
1.5. 开发调试
yarn dev
Grafana 配置 allow_loading_unsigned_plugins = myorg-demo-panel,插件目录映射到 dist。
2、数据源插件开发流程与代码模板
2.1. 初始化项目
npx @grafana/create-plugin my-datasource-plugin
cd my-datasource-plugin
yarn install
选择 datasource 类型。
2.2. plugin.json 示例
{
"type": "datasource",
"name": "Demo DataSource",
"id": "myorg-demo-datasource",
"info": {
"description": "A demo data source plugin",
"author": { "name": "Your Name" },
"version": "1.0.0"
},
"dependencies": {
"grafanaDependency": ">=10.0.0"
},
"includes": [
{ "type": "datasource", "name": "Demo DataSource" }
]
}
2.3. 主入口代码(src/DataSource.ts)
import { DataSourceApi, DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings } from '@grafana/data';
import { DemoQuery, DemoDataSourceOptions } from './types';
export class DemoDataSource extends DataSourceApi<DemoQuery, DemoDataSourceOptions> {
constructor(instanceSettings: DataSourceInstanceSettings<DemoDataSourceOptions>) {
super(instanceSettings);
}
async query(request: DataQueryRequest<DemoQuery>): Promise<DataQueryResponse> {
// 这里可以调用外部API,或返回模拟数据
const data = [
{
fields: [
{ name: 'time', type: 'time', values: [Date.now()] },
{ name: 'value', type: 'number', values: [Math.random() * 100] }
],
length: 1
}
];
return { data };
}
async testDatasource() {
// 测试连接
return { status: 'success', message: 'Demo connection OK' };
}
}
2.4. 配置表单(src/ConfigEditor.tsx)
import React from 'react';
import { DataSourcePluginOptionsEditorProps } from '@grafana/data';
import { DemoDataSourceOptions } from './types';
export const ConfigEditor: React.FC<DataSourcePluginOptionsEditorProps<DemoDataSourceOptions>> = ({ onOptionsChange, options }) => {
return (
<div>
<label>API URL</label>
<input
type="text"
value={options.apiUrl || ''}
onChange={e => onOptionsChange({ ...options, apiUrl: e.target.value })}
/>
</div>
);
};
2.5. 类型定义(src/types.ts)
export interface DemoQuery {
queryText?: string;
}
export interface DemoDataSourceOptions {
apiUrl?: string;
}
2.6. 注册插件(src/module.ts)
import { DataSourcePlugin } from '@grafana/data';
import { DemoDataSource } from './DataSource';
import { ConfigEditor } from './ConfigEditor';
export const plugin = new DataSourcePlugin(DemoDataSource)
.setConfigEditor(ConfigEditor);
3、后端安全代理实现(Go)
3.1. 后端插件目录结构示例
my-datasource-plugin/
├── src/
├── plugin.json
├── backend/
│ └── main.go
3.2. Go 后端代码(backend/main.go)
package main
import (
"context"
"encoding/json"
"net/http"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
)
func main() {
datasource.Serve(newDemoDatasource)
}
func newDemoDatasource(_ backend.DataSourceInstanceSettings) (backend.DataSource, error) {
return &DemoDatasource{}, nil
}
type DemoDatasource struct{}
func (ds *DemoDatasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
res := backend.NewQueryDataResponse()
for _, q := range req.Queries {
// 这里可以安全地访问外部API
log.DefaultLogger.Info("Demo backend query", "query", string(q.JSON))
frame := backend.NewFrame("response")
frame.Fields = append(frame.Fields,
backend.NewField("time", nil, []int64{backend.TimeToEpochMilliseconds(ctx)}),
backend.NewField("value", nil, []float64{42.0}),
)
res.Responses[q.RefID] = backend.DataResponse{Frames: backend.Frames{frame}}
}
return res, nil
}
3.3. plugin.json 后端声明
{
...
"backend": true,
...
}
3.4. 本地运行与调试
- 后端插件需用 Go 编译,参考 官方后端SDK
- 前端与后端可一起开发,Grafana 会自动加载
4、实践建议与资源
- 开发调试建议:用 Docker 启 Grafana,插件目录映射本地,
yarn dev热更新前端。 - 代码结构建议:前端/后端分离,类型定义清晰,表单与主逻辑分离。
- 安全建议:后端代理用于对接私有API、加密认证信息,避免前端泄露密钥。
- 发布建议:生产环境建议插件签名,详见官方签名流程
1458

被折叠的 条评论
为什么被折叠?



