mirror of
https://github.com/grafana/grafana.git
synced 2025-08-01 03:31:50 +08:00
Datasources: health check logging improvements (#96403)
* basic healthcheck logging * show detailed error for admins * fix linting errors * fix config summary keys * passing context to logger * tracking health check errors * remove tracking
This commit is contained in:
@ -260,17 +260,9 @@ func (t *postgresQueryResultTransformer) TransformQueryError(_ log.Logger, err e
|
|||||||
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||||
dsHandler, err := s.getDSInfo(ctx, req.PluginContext)
|
dsHandler, err := s.getDSInfo(ctx, req.PluginContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}, nil
|
||||||
}
|
}
|
||||||
|
return dsHandler.CheckHealth(ctx, req)
|
||||||
err = dsHandler.Ping()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Check health failed", "error", err)
|
|
||||||
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: dsHandler.TransformQueryError(s.logger, err).Error()}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *postgresQueryResultTransformer) GetConverterList() []sqlutil.StringConverter {
|
func (t *postgresQueryResultTransformer) GetConverterList() []sqlutil.StringConverter {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package sqleng
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||||
|
err := e.Ping()
|
||||||
|
if err != nil {
|
||||||
|
logCheckHealthError(ctx, e.dsInfo, err, e.log)
|
||||||
|
if req.PluginContext.User.Role == "Admin" {
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}, nil
|
||||||
|
}
|
||||||
|
errResponse := &backend.CheckHealthResult{
|
||||||
|
Status: backend.HealthStatusError,
|
||||||
|
Message: e.TransformQueryError(e.log, err).Error(),
|
||||||
|
}
|
||||||
|
return errResponse, nil
|
||||||
|
}
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func logCheckHealthError(ctx context.Context, dsInfo DataSourceInfo, err error, logger log.Logger) {
|
||||||
|
configSummary := map[string]any{
|
||||||
|
"config_url_length": len(dsInfo.URL),
|
||||||
|
"config_user_length": len(dsInfo.User),
|
||||||
|
"config_database_length": len(dsInfo.Database),
|
||||||
|
"config_json_data_database_length": len(dsInfo.JsonData.Database),
|
||||||
|
"config_max_open_conns": dsInfo.JsonData.MaxOpenConns,
|
||||||
|
"config_max_idle_conns": dsInfo.JsonData.MaxIdleConns,
|
||||||
|
"config_conn_max_life_time": dsInfo.JsonData.ConnMaxLifetime,
|
||||||
|
"config_conn_timeout": dsInfo.JsonData.ConnectionTimeout,
|
||||||
|
"config_timescaledb": dsInfo.JsonData.Timescaledb,
|
||||||
|
"config_ssl_mode": dsInfo.JsonData.Mode,
|
||||||
|
"config_tls_configuration_method": dsInfo.JsonData.ConfigurationMethod,
|
||||||
|
"config_tls_skip_verify": dsInfo.JsonData.TlsSkipVerify,
|
||||||
|
"config_timezone": dsInfo.JsonData.Timezone,
|
||||||
|
"config_time_interval": dsInfo.JsonData.TimeInterval,
|
||||||
|
"config_enable_secure_proxy": dsInfo.JsonData.SecureDSProxy,
|
||||||
|
"config_allow_clear_text_passwords": dsInfo.JsonData.AllowCleartextPasswords,
|
||||||
|
"config_authentication_type": dsInfo.JsonData.AuthenticationType,
|
||||||
|
"config_ssl_root_cert_file_length": len(dsInfo.JsonData.RootCertFile),
|
||||||
|
"config_ssl_cert_file_length": len(dsInfo.JsonData.CertFile),
|
||||||
|
"config_ssl_key_file_length": len(dsInfo.JsonData.CertKeyFile),
|
||||||
|
"config_encrypt_length": len(dsInfo.JsonData.Encrypt),
|
||||||
|
"config_server_name_length": len(dsInfo.JsonData.Servername),
|
||||||
|
"config_password_length": len(dsInfo.DecryptedSecureJSONData["password"]),
|
||||||
|
"config_tls_ca_cert_length": len(dsInfo.DecryptedSecureJSONData["tlsCACert"]),
|
||||||
|
"config_tls_client_cert_length": len(dsInfo.DecryptedSecureJSONData["tlsClientCert"]),
|
||||||
|
"config_tls_client_key_length": len(dsInfo.DecryptedSecureJSONData["tlsClientKey"]),
|
||||||
|
}
|
||||||
|
configSummaryJson, marshalError := json.Marshal(configSummary)
|
||||||
|
if marshalError != nil {
|
||||||
|
logger.Error("Check health failed", "error", err, "message_type", "ds_config_health_check_error", "plugin_id", "grafana-postgresql-datasource")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Error("Check health failed", "error", err, "message_type", "ds_config_health_check_error_detailed", "plugin_id", "grafana-postgresql-datasource", "details", string(configSummaryJson))
|
||||||
|
}
|
@ -37,7 +37,7 @@ func (s *Service) getDataSourceHandler(ctx context.Context, pluginCtx backend.Pl
|
|||||||
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||||
dsHandler, err := s.getDataSourceHandler(ctx, req.PluginContext)
|
dsHandler, err := s.getDataSourceHandler(ctx, req.PluginContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return dsHandler.CheckHealth(ctx, req)
|
return dsHandler.CheckHealth(ctx, req)
|
||||||
|
64
pkg/tsdb/mysql/sqleng/handler_checkhealth.go
Normal file
64
pkg/tsdb/mysql/sqleng/handler_checkhealth.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package sqleng
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||||
|
err := e.db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
logCheckHealthError(ctx, e.dsInfo, err, e.log)
|
||||||
|
if req.PluginContext.User.Role == "Admin" {
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: err.Error()}, nil
|
||||||
|
}
|
||||||
|
var driverErr *mysql.MySQLError
|
||||||
|
if errors.As(err, &driverErr) {
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: e.TransformQueryError(e.log, driverErr).Error()}, nil
|
||||||
|
}
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: e.TransformQueryError(e.log, err).Error()}, nil
|
||||||
|
}
|
||||||
|
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func logCheckHealthError(ctx context.Context, dsInfo DataSourceInfo, err error, logger log.Logger) {
|
||||||
|
configSummary := map[string]any{
|
||||||
|
"config_url_length": len(dsInfo.URL),
|
||||||
|
"config_user_length": len(dsInfo.User),
|
||||||
|
"config_database_length": len(dsInfo.Database),
|
||||||
|
"config_json_data_database_length": len(dsInfo.JsonData.Database),
|
||||||
|
"config_max_open_conns": dsInfo.JsonData.MaxOpenConns,
|
||||||
|
"config_max_idle_conns": dsInfo.JsonData.MaxIdleConns,
|
||||||
|
"config_conn_max_life_time": dsInfo.JsonData.ConnMaxLifetime,
|
||||||
|
"config_conn_timeout": dsInfo.JsonData.ConnectionTimeout,
|
||||||
|
"config_timescaledb": dsInfo.JsonData.Timescaledb,
|
||||||
|
"config_ssl_mode": dsInfo.JsonData.Mode,
|
||||||
|
"config_tls_configuration_method": dsInfo.JsonData.ConfigurationMethod,
|
||||||
|
"config_tls_skip_verify": dsInfo.JsonData.TlsSkipVerify,
|
||||||
|
"config_timezone": dsInfo.JsonData.Timezone,
|
||||||
|
"config_time_interval": dsInfo.JsonData.TimeInterval,
|
||||||
|
"config_enable_secure_proxy": dsInfo.JsonData.SecureDSProxy,
|
||||||
|
"config_allow_clear_text_passwords": dsInfo.JsonData.AllowCleartextPasswords,
|
||||||
|
"config_authentication_type": dsInfo.JsonData.AuthenticationType,
|
||||||
|
"config_ssl_root_cert_file_length": len(dsInfo.JsonData.RootCertFile),
|
||||||
|
"config_ssl_cert_file_length": len(dsInfo.JsonData.CertFile),
|
||||||
|
"config_ssl_key_file_length": len(dsInfo.JsonData.CertKeyFile),
|
||||||
|
"config_encrypt_length": len(dsInfo.JsonData.Encrypt),
|
||||||
|
"config_server_name_length": len(dsInfo.JsonData.Servername),
|
||||||
|
"config_password_length": len(dsInfo.DecryptedSecureJSONData["password"]),
|
||||||
|
"config_tls_ca_cert_length": len(dsInfo.DecryptedSecureJSONData["tlsCACert"]),
|
||||||
|
"config_tls_client_cert_length": len(dsInfo.DecryptedSecureJSONData["tlsClientCert"]),
|
||||||
|
"config_tls_client_key_length": len(dsInfo.DecryptedSecureJSONData["tlsClientKey"]),
|
||||||
|
}
|
||||||
|
configSummaryJson, marshalError := json.Marshal(configSummary)
|
||||||
|
if marshalError != nil {
|
||||||
|
logger.Error("Check health failed", "error", err, "message_type", "ds_config_health_check_error", "plugin_id", "mysql")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Error("Check health failed", "error", err, "message_type", "ds_config_health_check_error_detailed", "plugin_id", "mysql", "details", string(configSummaryJson))
|
||||||
|
}
|
@ -14,7 +14,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
|
||||||
@ -153,19 +152,6 @@ func (e *DataSourceHandler) Dispose() {
|
|||||||
e.log.Debug("DB disposed")
|
e.log.Debug("DB disposed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *DataSourceHandler) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
|
||||||
err := e.db.Ping()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
var driverErr *mysql.MySQLError
|
|
||||||
if errors.As(err, &driverErr) {
|
|
||||||
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: e.TransformQueryError(e.log, driverErr).Error()}, nil
|
|
||||||
}
|
|
||||||
return &backend.CheckHealthResult{Status: backend.HealthStatusError, Message: e.TransformQueryError(e.log, err).Error()}, nil
|
|
||||||
}
|
|
||||||
return &backend.CheckHealthResult{Status: backend.HealthStatusOk, Message: "Database Connection OK"}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *DataSourceHandler) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
func (e *DataSourceHandler) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||||
result := backend.NewQueryDataResponse()
|
result := backend.NewQueryDataResponse()
|
||||||
ch := make(chan DBDataResponse, len(req.Queries))
|
ch := make(chan DBDataResponse, len(req.Queries))
|
||||||
|
Reference in New Issue
Block a user