Frontend service: Add /-/fe-boot-error endpoint to track errors starting the frontend service (#112375)
* add /-/fe-boot-error endpoint to track errors starting the frontend service * use promauto package * move metric to root * check error of r.Body.Close()
This commit is contained in:
@@ -2,11 +2,13 @@ package frontend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -25,6 +27,14 @@ import (
|
||||
|
||||
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/frontend")
|
||||
|
||||
// Initialize metrics
|
||||
var bootErrorMetric = promauto.NewCounter(prometheus.CounterOpts{
|
||||
Namespace: "grafana",
|
||||
Subsystem: "frontend",
|
||||
Name: "boot_errors_total",
|
||||
Help: "Total number of frontend boot errors",
|
||||
})
|
||||
|
||||
type frontendService struct {
|
||||
*services.BasicService
|
||||
cfg *setting.Cfg
|
||||
@@ -117,6 +127,11 @@ func (s *frontendService) routeGet(m *web.Mux, pattern string, h ...web.Handler)
|
||||
m.Get(pattern, handlers...)
|
||||
}
|
||||
|
||||
func (s *frontendService) routePost(m *web.Mux, pattern string, h ...web.Handler) {
|
||||
handlers := append([]web.Handler{middleware.ProvideRouteOperationName(pattern)}, h...)
|
||||
m.Post(pattern, handlers...)
|
||||
}
|
||||
|
||||
// Apply the same middleware patterns as the main HTTP server
|
||||
func (s *frontendService) addMiddlewares(m *web.Mux) {
|
||||
loggermiddleware := loggermw.Provide(s.cfg, s.features)
|
||||
@@ -148,6 +163,38 @@ func (s *frontendService) registerRoutes(m *web.Mux) {
|
||||
}
|
||||
})
|
||||
|
||||
// Frontend boot error reporting endpoint
|
||||
s.routePost(m, "/-/fe-boot-error", s.handleBootError)
|
||||
|
||||
// All other requests return index.html
|
||||
s.routeGet(m, "/*", s.index.HandleRequest)
|
||||
}
|
||||
|
||||
// handleBootError handles frontend boot error reports
|
||||
func (s *frontendService) handleBootError(w http.ResponseWriter, r *http.Request) {
|
||||
// Read the request body
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
s.log.Error("failed to read boot error request body", "error", err)
|
||||
http.Error(w, "Bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := r.Body.Close(); err != nil {
|
||||
s.log.Warn("Failed to close response body", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Increment the Prometheus counter
|
||||
bootErrorMetric.Inc()
|
||||
|
||||
// Log the error details
|
||||
s.log.Error("frontend boot error reported", "error", body)
|
||||
|
||||
// Return success response
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if _, err := w.Write([]byte("OK")); err != nil {
|
||||
s.log.Error("failed to write boot error response", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,10 +169,23 @@
|
||||
// Wrap in an IIFE to avoid polluting the global scope. Intentionally global-scope properties
|
||||
// are explicitly assigned to the `window` object.
|
||||
(() => {
|
||||
window.__grafana_load_failed = function(...args) {
|
||||
console.error('Failed to load Grafana', ...args);
|
||||
window.__grafana_load_failed = function(err) {
|
||||
console.error('Failed to load Grafana', err);
|
||||
document.querySelector('.fs-variant-loader').classList.add('fs-hidden');
|
||||
document.querySelector('.fs-variant-error').classList.remove('fs-hidden');
|
||||
|
||||
// Report the error to the backend
|
||||
const errorMessage = err ? err.message : 'Unknown error';
|
||||
|
||||
fetch('/-/fe-boot-error', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: errorMessage,
|
||||
}).catch(err => {
|
||||
console.error('Failed to report boot error to backend: ', err);
|
||||
});
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
|
||||
Reference in New Issue
Block a user