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 query targets, which can be either timeseries or table query. A handler may support tag key/value pairs, which can be passed to the query to adapt its behaviour (e.g. filtering what data should be returned) through 'ad hoc filters'. Finally, a handler can support annotations, i.e. a set of timestamps with associated text.
Server ¶
To create a SimpleJSON server, create a Server and run it:
handlers := map[string]simplejson.Handler{
"A": &handler{},
"B": &handler{table: true},
}
s, err := simplejson.New(handlers, simplejson.WithHTTPServerOption{Option: httpserver.WithPort{Port: 8080}})
if err == nil {
err = s.Serve()
}
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.QueryArgs) (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.QueryArgs) (response *simplejson.TimeSeriesResponse, err error) {
response = &query.TimeSeriesResponse{
Name: "A",
DataPoints: []simplejson.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.QueryArgs) (response *simplejson.TableResponse, err error) {
response = &simplejson.TableResponse{
Columns: []simplejson.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(_ simplejson.QueryRequest) (annotations []simplejson.Annotation, err error) {
annotations = []simplejson.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
The underlying http server is implemented by github.com/clambin/go-common/httpserver, which exports its own set of metrics.
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/go-common/httpserver"
"github.com/clambin/simplejson/v5"
"time"
)
func main() {
handlers := map[string]simplejson.Handler{
"A": &handler{},
"B": &handler{table: true},
}
s, err := simplejson.New(handlers, simplejson.WithHTTPServerOption{Option: httpserver.WithPort{Port: 8080}})
if err == nil {
_ = s.Serve()
}
}
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 simplejson.QueryRequest) (simplejson.Response, error) {
if h.table == false {
return h.timeSeriesQuery(ctx, req)
}
return h.tableQuery(ctx, req)
}
func (h *handler) timeSeriesQuery(_ context.Context, _ simplejson.QueryRequest) (simplejson.TimeSeriesResponse, error) {
dataPoints := make([]simplejson.DataPoint, 60)
timestamp := time.Now().Add(-1 * time.Hour)
for i := 0; i < 60; i++ {
dataPoints[i] = simplejson.DataPoint{
Timestamp: timestamp,
Value: float64(i),
}
timestamp = timestamp.Add(1 * time.Minute)
}
return simplejson.TimeSeriesResponse{
DataPoints: dataPoints,
}, nil
}
func (h *handler) tableQuery(_ context.Context, _ simplejson.QueryRequest) (simplejson.TableResponse, error) {
timestamps := make(simplejson.TimeColumn, 60)
seriesA := make(simplejson.NumberColumn, 60)
seriesB := make(simplejson.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 simplejson.TableResponse{Columns: []simplejson.Column{
{Text: "timestamp", Data: timestamps},
{Text: "series A", Data: seriesA},
{Text: "series B", Data: seriesB},
}}, nil
}
func (h *handler) Annotations(_ simplejson.AnnotationRequest) ([]simplejson.Annotation, error) {
return []simplejson.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 AdHocFilter
- type Annotation
- type AnnotationRequest
- type AnnotationsFunc
- type Args
- type Column
- type DataPoint
- type Endpoints
- type Handler
- type NumberColumn
- type Option
- type QueryArgs
- type QueryFunc
- type QueryMetrics
- type QueryRequest
- type Range
- type RequestDetails
- type Response
- type Server
- func (s *Server) Annotations(w http.ResponseWriter, req *http.Request)
- func (s *Server) Collect(metrics chan<- prometheus.Metric)
- func (s *Server) Describe(descs chan<- *prometheus.Desc)
- func (s *Server) Query(w http.ResponseWriter, req *http.Request)
- func (s *Server) Search(w http.ResponseWriter, _ *http.Request)
- func (s *Server) Serve() error
- func (s *Server) Shutdown(timeout time.Duration) 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() []string
- type StringColumn
- type TableResponse
- type TagKeysFunc
- type TagValuesFunc
- type Target
- type TimeColumn
- type TimeSeriesResponse
- type WithHTTPServerOption
- type WithQueryMetrics
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AdHocFilter ¶
type AdHocFilter struct {
Value string `json:"value"`
Operator string `json:"operator"`
Condition string `json:"condition"`
Key string `json:"key"`
}
AdHocFilter specifies the ad hoc filters, whose keys & values are returned by the /tag-key and /tag-values endpoints.
type Annotation ¶
type Annotation struct {
Time time.Time
TimeEnd time.Time
Title string
Text string
Tags []string
Request RequestDetails
}
Annotation response. The annotation endpoint returns a slice of these.
func (Annotation) MarshalJSON ¶
func (annotation Annotation) MarshalJSON() (output []byte, err error)
MarshalJSON converts an Annotation to JSON.
type AnnotationRequest ¶
type AnnotationRequest struct {
Annotation RequestDetails `json:"annotation"`
Args
}
AnnotationRequest is a query for annotation.
func (*AnnotationRequest) UnmarshalJSON ¶
func (r *AnnotationRequest) UnmarshalJSON(b []byte) (err error)
UnmarshalJSON unmarshalls a AnnotationRequest from JSON
type AnnotationsFunc ¶
type AnnotationsFunc func(req AnnotationRequest) ([]Annotation, error)
AnnotationsFunc handles requests for annotation
type Args ¶
type Args struct {
Range Range `json:"range"`
AdHocFilters []AdHocFilter
}
Args contains common arguments used by endpoints.
type Column ¶
type Column struct {
Text string
Data interface{}
}
Column is a column returned by a TableQuery. Text holds the column's header, Data holds the slice of values and should be a TimeColumn, a StringColumn or a NumberColumn.
func (Column) MarshalEasyJSON ¶
MarshalEasyJSON supports easyjson.Marshaler interface
func (Column) MarshalJSON ¶
MarshalJSON supports json.Marshaler interface
func (*Column) UnmarshalEasyJSON ¶
UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (*Column) UnmarshalJSON ¶
UnmarshalJSON supports json.Unmarshaler interface
type DataPoint ¶
DataPoint contains one entry returned by a Query.
func (DataPoint) MarshalJSON ¶
MarshalJSON converts a DataPoint to JSON.
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 NumberColumn ¶
type NumberColumn []float64
NumberColumn holds a slice of number values (one per row).
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option specified configuration options for Server
type QueryFunc ¶
type QueryFunc func(ctx context.Context, req QueryRequest) (Response, error)
QueryFunc handles queries
type QueryMetrics ¶
type QueryMetrics struct {
// contains filtered or unexported fields
}
func (QueryMetrics) Collect ¶
func (qm QueryMetrics) Collect(ch chan<- prometheus.Metric)
func (QueryMetrics) Describe ¶
func (qm QueryMetrics) Describe(ch chan<- *prometheus.Desc)
type QueryRequest ¶
QueryRequest is a Query request. For each specified Target, the server will call the appropriate handler's Query or TableQuery function with the provided QueryArgs.
func (*QueryRequest) UnmarshalJSON ¶
func (r *QueryRequest) UnmarshalJSON(b []byte) (err error)
UnmarshalJSON unmarshalls a QueryRequest from JSON
type RequestDetails ¶
type RequestDetails struct {
Name string `json:"name"`
Datasource string `json:"datasource"`
Enable bool `json:"enable"`
Query string `json:"query"`
}
RequestDetails specifies which annotation should be returned.
type Server ¶
Server receives SimpleJSON requests from Grafana and dispatches them to the handler that serves the specified target.
func (*Server) Annotations ¶
func (s *Server) Annotations(w http.ResponseWriter, req *http.Request)
func (*Server) Collect ¶ added in v5.1.0
func (s *Server) Collect(metrics chan<- prometheus.Metric)
Collect implements the prometheus.Collector interface
func (*Server) Describe ¶ added in v5.1.0
func (s *Server) Describe(descs chan<- *prometheus.Desc)
Describe implements the prometheus.Collector interface
type StringColumn ¶
type StringColumn []string
StringColumn holds a slice of string values (one per row).
type TableResponse ¶
type TableResponse struct {
Columns []Column
}
TableResponse is returned by a TableQuery, i.e. a slice of Column structures.
func (TableResponse) MarshalJSON ¶
func (t TableResponse) MarshalJSON() (output []byte, err error)
MarshalJSON converts a TableResponse to JSON.
type TagKeysFunc ¶
TagKeysFunc returns supported tag names
type TagValuesFunc ¶
TagValuesFunc returns supported values for the specified tag name
type Target ¶
type Target struct {
Name string `json:"target"` // name of the target.
Type string `json:"type"` // "timeserie" or "" for timeseries. "table" for table queries.
}
Target specifies the requested target name and type.
type TimeColumn ¶
TimeColumn holds a slice of time.Time values (one per row).
type TimeSeriesResponse ¶
type TimeSeriesResponse struct {
Target string `json:"target"`
DataPoints []DataPoint `json:"datapoints"`
}
TimeSeriesResponse is the response from a timeseries Query.
func (TimeSeriesResponse) MarshalEasyJSON ¶
func (v TimeSeriesResponse) MarshalEasyJSON(w *jwriter.Writer)
MarshalEasyJSON supports easyjson.Marshaler interface
func (TimeSeriesResponse) MarshalJSON ¶
func (v TimeSeriesResponse) MarshalJSON() ([]byte, error)
MarshalJSON supports json.Marshaler interface
func (*TimeSeriesResponse) UnmarshalEasyJSON ¶
func (v *TimeSeriesResponse) UnmarshalEasyJSON(l *jlexer.Lexer)
UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (*TimeSeriesResponse) UnmarshalJSON ¶
func (v *TimeSeriesResponse) UnmarshalJSON(data []byte) error
UnmarshalJSON supports json.Unmarshaler interface
type WithHTTPServerOption ¶
type WithHTTPServerOption struct {
Option httpserver.Option
}
WithHTTPServerOption will pass the provided option to the underlying HTTP Server
type WithQueryMetrics ¶
type WithQueryMetrics struct {
Name string
}
WithQueryMetrics will collect the specified metrics to instrument the Server's Handlers.