Commit a08cbbcc by Carl Bergquist Committed by GitHub

Instrument backend plugin requests (#23346)

parent 1f0e1b33
...@@ -8,7 +8,8 @@ import ( ...@@ -8,7 +8,8 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
const exporterName = "grafana" // ExporterName is used as namespace for exposing prometheus metrics
const ExporterName = "grafana"
var ( var (
// MInstanceStart is a metric counter for started instances // MInstanceStart is a metric counter for started instances
...@@ -159,28 +160,28 @@ func init() { ...@@ -159,28 +160,28 @@ func init() {
MInstanceStart = prometheus.NewCounter(prometheus.CounterOpts{ MInstanceStart = prometheus.NewCounter(prometheus.CounterOpts{
Name: "instance_start_total", Name: "instance_start_total",
Help: "counter for started instances", Help: "counter for started instances",
Namespace: exporterName, Namespace: ExporterName,
}) })
MPageStatus = newCounterVecStartingAtZero( MPageStatus = newCounterVecStartingAtZero(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "page_response_status_total", Name: "page_response_status_total",
Help: "page http response status", Help: "page http response status",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"code"}, httpStatusCodes...) }, []string{"code"}, httpStatusCodes...)
MApiStatus = newCounterVecStartingAtZero( MApiStatus = newCounterVecStartingAtZero(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "api_response_status_total", Name: "api_response_status_total",
Help: "api http response status", Help: "api http response status",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"code"}, httpStatusCodes...) }, []string{"code"}, httpStatusCodes...)
MProxyStatus = newCounterVecStartingAtZero( MProxyStatus = newCounterVecStartingAtZero(
prometheus.CounterOpts{ prometheus.CounterOpts{
Name: "proxy_response_status_total", Name: "proxy_response_status_total",
Help: "proxy http response status", Help: "proxy http response status",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"code"}, httpStatusCodes...) }, []string{"code"}, httpStatusCodes...)
MHttpRequestTotal = prometheus.NewCounterVec( MHttpRequestTotal = prometheus.NewCounterVec(
...@@ -203,241 +204,241 @@ func init() { ...@@ -203,241 +204,241 @@ func init() {
MApiUserSignUpStarted = newCounterStartingAtZero(prometheus.CounterOpts{ MApiUserSignUpStarted = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_user_signup_started_total", Name: "api_user_signup_started_total",
Help: "amount of users who started the signup flow", Help: "amount of users who started the signup flow",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiUserSignUpCompleted = newCounterStartingAtZero(prometheus.CounterOpts{ MApiUserSignUpCompleted = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_user_signup_completed_total", Name: "api_user_signup_completed_total",
Help: "amount of users who completed the signup flow", Help: "amount of users who completed the signup flow",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiUserSignUpInvite = newCounterStartingAtZero(prometheus.CounterOpts{ MApiUserSignUpInvite = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_user_signup_invite_total", Name: "api_user_signup_invite_total",
Help: "amount of users who have been invited", Help: "amount of users who have been invited",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardSave = prometheus.NewSummary(prometheus.SummaryOpts{ MApiDashboardSave = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "api_dashboard_save_milliseconds", Name: "api_dashboard_save_milliseconds",
Help: "summary for dashboard save duration", Help: "summary for dashboard save duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardGet = prometheus.NewSummary(prometheus.SummaryOpts{ MApiDashboardGet = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "api_dashboard_get_milliseconds", Name: "api_dashboard_get_milliseconds",
Help: "summary for dashboard get duration", Help: "summary for dashboard get duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardSearch = prometheus.NewSummary(prometheus.SummaryOpts{ MApiDashboardSearch = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "api_dashboard_search_milliseconds", Name: "api_dashboard_search_milliseconds",
Help: "summary for dashboard search duration", Help: "summary for dashboard search duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiAdminUserCreate = newCounterStartingAtZero(prometheus.CounterOpts{ MApiAdminUserCreate = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_admin_user_created_total", Name: "api_admin_user_created_total",
Help: "api admin user created counter", Help: "api admin user created counter",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiLoginPost = newCounterStartingAtZero(prometheus.CounterOpts{ MApiLoginPost = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_login_post_total", Name: "api_login_post_total",
Help: "api login post counter", Help: "api login post counter",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiLoginOAuth = newCounterStartingAtZero(prometheus.CounterOpts{ MApiLoginOAuth = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_login_oauth_total", Name: "api_login_oauth_total",
Help: "api login oauth counter", Help: "api login oauth counter",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiLoginSAML = newCounterStartingAtZero(prometheus.CounterOpts{ MApiLoginSAML = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_login_saml_total", Name: "api_login_saml_total",
Help: "api login saml counter", Help: "api login saml counter",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiOrgCreate = newCounterStartingAtZero(prometheus.CounterOpts{ MApiOrgCreate = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_org_create_total", Name: "api_org_create_total",
Help: "api org created counter", Help: "api org created counter",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardSnapshotCreate = newCounterStartingAtZero(prometheus.CounterOpts{ MApiDashboardSnapshotCreate = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_dashboard_snapshot_create_total", Name: "api_dashboard_snapshot_create_total",
Help: "dashboard snapshots created", Help: "dashboard snapshots created",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardSnapshotExternal = newCounterStartingAtZero(prometheus.CounterOpts{ MApiDashboardSnapshotExternal = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_dashboard_snapshot_external_total", Name: "api_dashboard_snapshot_external_total",
Help: "external dashboard snapshots created", Help: "external dashboard snapshots created",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardSnapshotGet = newCounterStartingAtZero(prometheus.CounterOpts{ MApiDashboardSnapshotGet = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_dashboard_snapshot_get_total", Name: "api_dashboard_snapshot_get_total",
Help: "loaded dashboards", Help: "loaded dashboards",
Namespace: exporterName, Namespace: ExporterName,
}) })
MApiDashboardInsert = newCounterStartingAtZero(prometheus.CounterOpts{ MApiDashboardInsert = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "api_models_dashboard_insert_total", Name: "api_models_dashboard_insert_total",
Help: "dashboards inserted ", Help: "dashboards inserted ",
Namespace: exporterName, Namespace: ExporterName,
}) })
MAlertingResultState = prometheus.NewCounterVec(prometheus.CounterOpts{ MAlertingResultState = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "alerting_result_total", Name: "alerting_result_total",
Help: "alert execution result counter", Help: "alert execution result counter",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"state"}) }, []string{"state"})
MAlertingNotificationSent = prometheus.NewCounterVec(prometheus.CounterOpts{ MAlertingNotificationSent = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "alerting_notification_sent_total", Name: "alerting_notification_sent_total",
Help: "counter for how many alert notifications have been sent", Help: "counter for how many alert notifications have been sent",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"type"}) }, []string{"type"})
MAlertingNotificationFailed = prometheus.NewCounterVec(prometheus.CounterOpts{ MAlertingNotificationFailed = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "alerting_notification_failed_total", Name: "alerting_notification_failed_total",
Help: "counter for how many alert notifications have failed", Help: "counter for how many alert notifications have failed",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"type"}) }, []string{"type"})
MAwsCloudWatchGetMetricStatistics = newCounterStartingAtZero(prometheus.CounterOpts{ MAwsCloudWatchGetMetricStatistics = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "aws_cloudwatch_get_metric_statistics_total", Name: "aws_cloudwatch_get_metric_statistics_total",
Help: "counter for getting metric statistics from aws", Help: "counter for getting metric statistics from aws",
Namespace: exporterName, Namespace: ExporterName,
}) })
MAwsCloudWatchListMetrics = newCounterStartingAtZero(prometheus.CounterOpts{ MAwsCloudWatchListMetrics = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "aws_cloudwatch_list_metrics_total", Name: "aws_cloudwatch_list_metrics_total",
Help: "counter for getting list of metrics from aws", Help: "counter for getting list of metrics from aws",
Namespace: exporterName, Namespace: ExporterName,
}) })
MAwsCloudWatchGetMetricData = newCounterStartingAtZero(prometheus.CounterOpts{ MAwsCloudWatchGetMetricData = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "aws_cloudwatch_get_metric_data_total", Name: "aws_cloudwatch_get_metric_data_total",
Help: "counter for getting metric data time series from aws", Help: "counter for getting metric data time series from aws",
Namespace: exporterName, Namespace: ExporterName,
}) })
MDBDataSourceQueryByID = newCounterStartingAtZero(prometheus.CounterOpts{ MDBDataSourceQueryByID = newCounterStartingAtZero(prometheus.CounterOpts{
Name: "db_datasource_query_by_id_total", Name: "db_datasource_query_by_id_total",
Help: "counter for getting datasource by id", Help: "counter for getting datasource by id",
Namespace: exporterName, Namespace: ExporterName,
}) })
LDAPUsersSyncExecutionTime = prometheus.NewSummary(prometheus.SummaryOpts{ LDAPUsersSyncExecutionTime = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "ldap_users_sync_execution_time", Name: "ldap_users_sync_execution_time",
Help: "summary for LDAP users sync execution duration", Help: "summary for LDAP users sync execution duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MDataSourceProxyReqTimer = prometheus.NewSummary(prometheus.SummaryOpts{ MDataSourceProxyReqTimer = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "api_dataproxy_request_all_milliseconds", Name: "api_dataproxy_request_all_milliseconds",
Help: "summary for dataproxy request duration", Help: "summary for dataproxy request duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MAlertingExecutionTime = prometheus.NewSummary(prometheus.SummaryOpts{ MAlertingExecutionTime = prometheus.NewSummary(prometheus.SummaryOpts{
Name: "alerting_execution_time_milliseconds", Name: "alerting_execution_time_milliseconds",
Help: "summary of alert exeuction duration", Help: "summary of alert exeuction duration",
Objectives: objectiveMap, Objectives: objectiveMap,
Namespace: exporterName, Namespace: ExporterName,
}) })
MAlertingActiveAlerts = prometheus.NewGauge(prometheus.GaugeOpts{ MAlertingActiveAlerts = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "alerting_active_alerts", Name: "alerting_active_alerts",
Help: "amount of active alerts", Help: "amount of active alerts",
Namespace: exporterName, Namespace: ExporterName,
}) })
MStatTotalDashboards = prometheus.NewGauge(prometheus.GaugeOpts{ MStatTotalDashboards = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_dashboard", Name: "stat_totals_dashboard",
Help: "total amount of dashboards", Help: "total amount of dashboards",
Namespace: exporterName, Namespace: ExporterName,
}) })
MStatTotalUsers = prometheus.NewGauge(prometheus.GaugeOpts{ MStatTotalUsers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_total_users", Name: "stat_total_users",
Help: "total amount of users", Help: "total amount of users",
Namespace: exporterName, Namespace: ExporterName,
}) })
MStatActiveUsers = prometheus.NewGauge(prometheus.GaugeOpts{ MStatActiveUsers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_active_users", Name: "stat_active_users",
Help: "number of active users", Help: "number of active users",
Namespace: exporterName, Namespace: ExporterName,
}) })
MStatTotalOrgs = prometheus.NewGauge(prometheus.GaugeOpts{ MStatTotalOrgs = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_total_orgs", Name: "stat_total_orgs",
Help: "total amount of orgs", Help: "total amount of orgs",
Namespace: exporterName, Namespace: ExporterName,
}) })
MStatTotalPlaylists = prometheus.NewGauge(prometheus.GaugeOpts{ MStatTotalPlaylists = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_total_playlists", Name: "stat_total_playlists",
Help: "total amount of playlists", Help: "total amount of playlists",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalViewers = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalViewers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_viewers", Name: "stat_totals_viewers",
Help: "total amount of viewers", Help: "total amount of viewers",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalEditors = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalEditors = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_editors", Name: "stat_totals_editors",
Help: "total amount of editors", Help: "total amount of editors",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalAdmins = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalAdmins = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_admins", Name: "stat_totals_admins",
Help: "total amount of admins", Help: "total amount of admins",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalActiveViewers = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalActiveViewers = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_active_viewers", Name: "stat_totals_active_viewers",
Help: "total amount of viewers", Help: "total amount of viewers",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalActiveEditors = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalActiveEditors = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_active_editors", Name: "stat_totals_active_editors",
Help: "total amount of active editors", Help: "total amount of active editors",
Namespace: exporterName, Namespace: ExporterName,
}) })
StatsTotalActiveAdmins = prometheus.NewGauge(prometheus.GaugeOpts{ StatsTotalActiveAdmins = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "stat_totals_active_admins", Name: "stat_totals_active_admins",
Help: "total amount of active admins", Help: "total amount of active admins",
Namespace: exporterName, Namespace: ExporterName,
}) })
grafanaBuildVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{ grafanaBuildVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "build_info", Name: "build_info",
Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built", Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"version", "revision", "branch", "goversion", "edition"}) }, []string{"version", "revision", "branch", "goversion", "edition"})
grafanPluginBuildInfoDesc = prometheus.NewGaugeVec(prometheus.GaugeOpts{ grafanPluginBuildInfoDesc = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "plugin_build_info", Name: "plugin_build_info",
Help: "A metric with a constant '1' value labeled by pluginId, pluginType and version from which Grafana plugin was built", Help: "A metric with a constant '1' value labeled by pluginId, pluginType and version from which Grafana plugin was built",
Namespace: exporterName, Namespace: ExporterName,
}, []string{"plugin_id", "plugin_type", "version"}) }, []string{"plugin_id", "plugin_type", "version"})
} }
......
...@@ -141,7 +141,14 @@ func (p *BackendPlugin) CollectMetrics(ctx context.Context) (*pluginv2.CollectMe ...@@ -141,7 +141,14 @@ func (p *BackendPlugin) CollectMetrics(ctx context.Context) (*pluginv2.CollectMe
}, nil }, nil
} }
res, err := p.diagnostics.CollectMetrics(ctx, &pluginv2.CollectMetricsRequest{}) var res *pluginv2.CollectMetricsResponse
err := InstrumentPluginRequest(p.id, "metrics", func() error {
var innerErr error
res, innerErr = p.diagnostics.CollectMetrics(ctx, &pluginv2.CollectMetricsRequest{})
return innerErr
})
if err != nil { if err != nil {
if st, ok := status.FromError(err); ok { if st, ok := status.FromError(err); ok {
if st.Code() == codes.Unimplemented { if st.Code() == codes.Unimplemented {
...@@ -197,7 +204,13 @@ func (p *BackendPlugin) checkHealth(ctx context.Context, config *PluginConfig) ( ...@@ -197,7 +204,13 @@ func (p *BackendPlugin) checkHealth(ctx context.Context, config *PluginConfig) (
} }
} }
res, err := p.diagnostics.CheckHealth(ctx, &pluginv2.CheckHealthRequest{Config: pconfig}) var res *pluginv2.CheckHealthResponse
err = InstrumentPluginRequest(p.id, "checkhealth", func() error {
var innerErr error
res, innerErr = p.diagnostics.CheckHealth(ctx, &pluginv2.CheckHealthRequest{Config: pconfig})
return innerErr
})
if err != nil { if err != nil {
if st, ok := status.FromError(err); ok { if st, ok := status.FromError(err); ok {
if st.Code() == codes.Unimplemented { if st.Code() == codes.Unimplemented {
......
package backendplugin
import (
"time"
"github.com/prometheus/client_golang/prometheus"
)
var (
pluginRequestCounter *prometheus.CounterVec
pluginRequestDuration *prometheus.SummaryVec
)
func init() {
pluginRequestCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "grafana",
Name: "plugin_request_total",
Help: "The total amount of plugin requests",
}, []string{"plugin_id", "endpoint", "status"})
pluginRequestDuration = prometheus.NewSummaryVec(prometheus.SummaryOpts{
Namespace: "grafana",
Name: "plugin_request_duration_milliseconds",
Help: "Plugin request duration",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
}, []string{"plugin_id", "endpoint"})
prometheus.MustRegister(pluginRequestCounter, pluginRequestDuration)
}
// InstrumentPluginRequest instruments success rate and latency of `fn`
func InstrumentPluginRequest(pluginID string, endpoint string, fn func() error) error {
status := "ok"
start := time.Now()
err := fn()
if err != nil {
status = "error"
}
elapsed := time.Since(start) / time.Millisecond
pluginRequestDuration.WithLabelValues(pluginID, endpoint).Observe(float64(elapsed))
pluginRequestCounter.WithLabelValues(pluginID, endpoint, status).Inc()
return err
}
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util/errutil"
"github.com/grafana/grafana/pkg/util/proxyutil" "github.com/grafana/grafana/pkg/util/proxyutil"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
...@@ -187,17 +188,17 @@ func (m *manager) CheckHealth(ctx context.Context, pluginConfig *PluginConfig) ( ...@@ -187,17 +188,17 @@ func (m *manager) CheckHealth(ctx context.Context, pluginConfig *PluginConfig) (
} }
// CallResource calls a plugin resource. // CallResource calls a plugin resource.
func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path string) { func (m *manager) CallResource(config PluginConfig, reqCtx *models.ReqContext, path string) {
m.pluginsMu.RLock() m.pluginsMu.RLock()
p, registered := m.plugins[config.PluginID] p, registered := m.plugins[config.PluginID]
m.pluginsMu.RUnlock() m.pluginsMu.RUnlock()
if !registered { if !registered {
c.JsonApiErr(404, "Plugin not registered", nil) reqCtx.JsonApiErr(404, "Plugin not registered", nil)
return return
} }
clonedReq := c.Req.Clone(c.Req.Context()) clonedReq := reqCtx.Req.Clone(reqCtx.Req.Context())
keepCookieNames := []string{} keepCookieNames := []string{}
if config.JSONData != nil { if config.JSONData != nil {
if keepCookies := config.JSONData.Get("keepCookies"); keepCookies != nil { if keepCookies := config.JSONData.Get("keepCookies"); keepCookies != nil {
...@@ -208,9 +209,9 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s ...@@ -208,9 +209,9 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s
proxyutil.ClearCookieHeader(clonedReq, keepCookieNames) proxyutil.ClearCookieHeader(clonedReq, keepCookieNames)
proxyutil.PrepareProxyRequest(clonedReq) proxyutil.PrepareProxyRequest(clonedReq)
body, err := c.Req.Body().Bytes() body, err := reqCtx.Req.Body().Bytes()
if err != nil { if err != nil {
c.JsonApiErr(500, "Failed to read request body", err) reqCtx.JsonApiErr(500, "Failed to read request body", err)
return return
} }
...@@ -221,32 +222,41 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s ...@@ -221,32 +222,41 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s
URL: clonedReq.URL.String(), URL: clonedReq.URL.String(),
Headers: clonedReq.Header, Headers: clonedReq.Header,
Body: body, Body: body,
User: c.SignedInUser, User: reqCtx.SignedInUser,
} }
err = InstrumentPluginRequest(p.id, "resource", func() error {
stream, err := p.callResource(clonedReq.Context(), req) stream, err := p.callResource(clonedReq.Context(), req)
if err != nil { if err != nil {
c.JsonApiErr(500, "Failed to call resource", err) return errutil.Wrap("Failed to call resource", err)
return }
return flushStream(p, stream, reqCtx)
})
if err != nil {
reqCtx.JsonApiErr(500, "Failed to ", err)
} }
}
func flushStream(plugin *BackendPlugin, stream callResourceResultStream, reqCtx *models.ReqContext) error {
processedStreams := 0 processedStreams := 0
for { for {
resp, err := stream.Recv() resp, err := stream.Recv()
if err == io.EOF { if err == io.EOF {
if processedStreams == 0 { if processedStreams == 0 {
c.JsonApiErr(500, "Received empty resource response ", nil) return errors.New("Received empty resource response")
} }
return return nil
} }
if err != nil { if err != nil {
if processedStreams == 0 { if processedStreams == 0 {
c.JsonApiErr(500, "Failed to receive response from resource call", err) return errutil.Wrap("Failed to receive response from resource call", err)
} else {
p.logger.Error("Failed to receive response from resource call", "error", err)
} }
return
plugin.logger.Error("Failed to receive response from resource call", "error", err)
return nil
} }
// Expected that headers and status are only part of first stream // Expected that headers and status are only part of first stream
...@@ -264,18 +274,18 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s ...@@ -264,18 +274,18 @@ func (m *manager) CallResource(config PluginConfig, c *models.ReqContext, path s
} }
for _, v := range values { for _, v := range values {
c.Resp.Header().Add(k, v) reqCtx.Resp.Header().Add(k, v)
} }
} }
c.WriteHeader(resp.Status) reqCtx.WriteHeader(resp.Status)
} }
if _, err := c.Write(resp.Body); err != nil { if _, err := reqCtx.Write(resp.Body); err != nil {
p.logger.Error("Failed to write resource response", "error", err) plugin.logger.Error("Failed to write resource response", "error", err)
} }
c.Resp.Flush() reqCtx.Resp.Flush()
processedStreams++ processedStreams++
} }
} }
......
...@@ -77,7 +77,14 @@ func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataS ...@@ -77,7 +77,14 @@ func (tw *DatasourcePluginWrapperV2) Query(ctx context.Context, ds *models.DataS
}) })
} }
pbRes, err := tw.DataPlugin.QueryData(ctx, pbQuery) var pbRes *pluginv2.QueryDataResponse
err = backendplugin.InstrumentPluginRequest(ds.Type, "dataquery", func() error {
var err error
pbRes, err = tw.DataPlugin.QueryData(ctx, pbQuery)
return err
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment