Documentation
¶
Overview ¶
Package simplejson provides a Go implementation for Grafana's SimpleJSON datasource: https://grafana.com/grafana/plugins/grafana-simple-json-datasource
Overview ¶
A simplejson server is an HTTP server that supports one or more handlers. Each handler can support multiple targets, each of which can be supported by a timeseries or table query. Optionally tag can be used to alter the behaviour of the query (e.g. filtering what data should be returned). Finally, a handler can support annotation, i.e. a set of timestamps with associated text.
Server ¶
To create a SimpleJSON server, create a Server and run it:
s := simplejson.Server{
Handlers: map[string]simplejson.Handler{
"my-target": myHandler,
},
}
err := s.Run(8080)
This starts a server, listening on port 8080, with one target "my-target", served by myHandler.
Handler ¶
A handler serves incoming requests from Grafana, e.g. queries, requests for annotations or tag. The Handler interface contains all functions a handler needs to implement. It contains only one function (Endpoints). This function returns the Grafana SimpleJSON endpoints that the handler supports. Those can be:
- Query() implements the /query endpoint. handles both timeserie & table responses
- Annotations() implements the /annotation endpoint
- TagKeys() implements the /tag-keys endpoint
- TagValues() implements the /tag-values endpoint
Here's an example of a handler that supports timeseries queries:
type myHandler struct {
}
func (handler myHandler) Endpoints() simplejson.Endpoints {
return simplejson.Endpoints{
Query: handler.Query
}
}
func (handler *myHandler) Query(ctx context.Context, target string, target *simplejson.Args) (response *simplejson.QueryResponse, err error) {
// build response
return
}
Queries ¶
SimpleJSON supports two types of query responses: timeseries responses and table responses.
Timeseries queries return values as a list of timestamp/value tuples. Here's an example of a timeseries query handler:
func (handler *myHandler) Query(_ context.Context, _ string, _ query.Args) (response *query.TimeSeriesResponse, err error) {
response = &query.TimeSeriesResponse{
Name: "A",
DataPoints: []query.DataPoint{
{Timestamp: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), Value: 100},
{Timestamp: time.Date(2020, 1, 1, 0, 1, 0, 0, time.UTC), Value: 101},
{Timestamp: time.Date(2020, 1, 1, 0, 2, 0, 0, time.UTC), Value: 103},
},
}
return
}
Table Queries, on the other hand, return data organized in columns and rows. Each column needs to have the same number of rows:
func (handler *myHandler) TableQuery(_ context.Context, _ string, _ query.Args) (response *query.TableResponse, err error) {
response = &query.TableResponse{
Columns: []query.Column{
{ Text: "Time", Data: query.TimeColumn{time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2020, 1, 1, 0, 1, 0, 0, time.UTC)} },
{ Text: "Label", Data: query.StringColumn{"foo", "bar"}},
{ Text: "Series A", Data: query.NumberColumn{42, 43}},
{ Text: "Series B", Data: query.NumberColumn{64.5, 100.0}},
},
}
return
}
Annotations ¶
The /annotations endpoint returns Annotations:
func (h *handler) Annotations(_ annotation.Request) (annotations []annotation.Annotation, err error) {
annotations = []annotation.Annotation{
{
Time: time.Now().Add(-5 * time.Minute),
Title: "foo",
Text: "bar",
Tags: []string{"A", "B"},
},
}
return
}
NOTE: this is only called when using the SimpleJSON datasource. simPod / GrafanaJsonDatasource does not use the /annotations endpoint. Instead, it will call a regular /query and allows to configure its response as annotations instead.
Tags ¶
The /tag-keys and /tag-values endpoints return supported keys and key values respectively for your data source. A Grafana dashboard can then be confirmed to show those keys and its possible values as a filter.
The following sets up a key & key value handler:
func (h *handler) TagKeys(_ context.Context) (keys []string) {
return []string{"some-key"}
}
func (h *handler) TagValues(_ context.Context, key string) (values []string, err error) {
if key != "some-key" {
return nil, fmt.Errorf("invalid key: %s", key)
}
return []string{"A", "B", "C"}, nil
}
When the dashboard performs a query with a tag selected, that tag & value will be added in the request's AdHocFilters.
Metrics ¶
simplejson exports two Prometheus metrics for performance analytics:
simplejson_query_duration_seconds: duration of query requests by target, in seconds simplejson_query_failed_count: number of failed query requests
Other topics ¶
For information on query arguments and tags, refer to the documentation for those data structures.
Example ¶
package main
import (
"context"
"fmt"
"github.com/clambin/simplejson/v3"
"github.com/clambin/simplejson/v3/annotation"
"github.com/clambin/simplejson/v3/query"
"time"
)
func main() {
s, err := simplejson.New("test", map[string]simplejson.Handler{
"A": &handler{},
"B": &handler{table: true},
})
if err == nil {
_ = s.Run()
}
}
type handler struct{ table bool }
func (h *handler) Endpoints() simplejson.Endpoints {
return simplejson.Endpoints{
Query: h.Query,
Annotations: h.Annotations,
TagKeys: h.TagKeys,
TagValues: h.TagValues,
}
}
func (h *handler) Query(ctx context.Context, req query.Request) (query.Response, error) {
if h.table == false {
return h.timeSeriesQuery(ctx, req)
}
return h.tableQuery(ctx, req)
}
func (h *handler) timeSeriesQuery(_ context.Context, _ query.Request) (*query.TimeSeriesResponse, error) {
dataPoints := make([]query.DataPoint, 60)
timestamp := time.Now().Add(-1 * time.Hour)
for i := 0; i < 60; i++ {
dataPoints[i] = query.DataPoint{
Timestamp: timestamp,
Value: int64(i),
}
timestamp = timestamp.Add(1 * time.Minute)
}
return &query.TimeSeriesResponse{
DataPoints: dataPoints,
}, nil
}
func (h *handler) tableQuery(_ context.Context, _ query.Request) (*query.TableResponse, error) {
timestamps := make(query.TimeColumn, 60)
seriesA := make(query.NumberColumn, 60)
seriesB := make(query.NumberColumn, 60)
timestamp := time.Now().Add(-1 * time.Hour)
for i := 0; i < 60; i++ {
timestamps[i] = timestamp
seriesA[i] = float64(i)
seriesB[i] = float64(-i)
timestamp = timestamp.Add(1 * time.Minute)
}
return &query.TableResponse{
Columns: []query.Column{
{Text: "timestamp", Data: timestamps},
{Text: "series A", Data: seriesA},
{Text: "series B", Data: seriesB},
},
}, nil
}
func (h *handler) Annotations(_ annotation.Request) ([]annotation.Annotation, error) {
return []annotation.Annotation{{
Time: time.Now().Add(-5 * time.Minute),
Title: "foo",
Text: "bar",
}}, nil
}
func (h *handler) TagKeys(_ context.Context) []string {
return []string{"some-key"}
}
func (h *handler) TagValues(_ context.Context, key string) ([]string, error) {
if key != "some-key" {
return nil, fmt.Errorf("invalid key: %s", key)
}
return []string{"A", "B", "C"}, nil
}
Index ¶
- type AnnotationsFunc
- type Endpoints
- type Handler
- type QueryFunc
- type QueryMetrics
- type Server
- func (s *Server) Annotations(w http.ResponseWriter, req *http.Request)
- func (s *Server) Query(w http.ResponseWriter, req *http.Request)
- func (s *Server) Run() error
- func (s *Server) Search(w http.ResponseWriter, _ *http.Request)
- func (s *Server) Shutdown(timeout time.Duration) (err error)
- func (s *Server) TagKeys(w http.ResponseWriter, req *http.Request)
- func (s *Server) TagValues(w http.ResponseWriter, req *http.Request)
- func (s *Server) Targets() (targets []string)
- type TagKeysFunc
- type TagValuesFunc
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AnnotationsFunc ¶
type AnnotationsFunc func(req annotation.Request) ([]annotation.Annotation, error)
AnnotationsFunc handles requests for annotation
type Endpoints ¶
type Endpoints struct {
Query QueryFunc // /query endpoint: handles queries
Annotations AnnotationsFunc // /annotation endpoint: handles requests for annotation
TagKeys TagKeysFunc // /tag-keys endpoint: returns all supported tag names
TagValues TagValuesFunc // /tag-values endpoint: returns all supported values for the specified tag name
}
Endpoints contains the functions that implement each of the SimpleJson endpoints
type Handler ¶
type Handler interface {
Endpoints() Endpoints
}
Handler implements the different Grafana SimpleJSON endpoints. The interface only contains a single Endpoints() function, so that a handler only has to implement the endpoint functions (query, annotation, etc.) that it needs.
type QueryMetrics ¶ added in v3.7.0
type QueryMetrics struct {
Duration *prometheus.HistogramVec
Errors *prometheus.CounterVec
}
func NewQueryMetrics ¶ added in v3.7.0
func NewQueryMetrics(name string) QueryMetrics
func (QueryMetrics) Register ¶ added in v3.7.0
func (qm QueryMetrics) Register(r prometheus.Registerer)
type Server ¶
Server receives SimpleJSON requests from Grafana and dispatches them to the handler that serves the specified target.
func NewWithRegisterer ¶ added in v3.7.0
func NewWithRegisterer(name string, handlers map[string]Handler, r prometheus.Registerer, options ...httpserver.Option) (s *Server, err error)
func (*Server) Annotations ¶ added in v3.5.0
func (s *Server) Annotations(w http.ResponseWriter, req *http.Request)
func (*Server) Query ¶ added in v3.5.0
func (s *Server) Query(w http.ResponseWriter, req *http.Request)
func (*Server) Search ¶ added in v3.5.0
func (s *Server) Search(w http.ResponseWriter, _ *http.Request)
func (*Server) TagKeys ¶ added in v3.5.0
func (s *Server) TagKeys(w http.ResponseWriter, req *http.Request)
type TagKeysFunc ¶
TagKeysFunc returns supported tag names
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dataset makes it easier to produce time-based responses when dealing with data that may not necessarily be sequential.
|
Package dataset makes it easier to produce time-based responses when dealing with data that may not necessarily be sequential. |
|
pkg
|
|