diff --git a/rpcserver/jsonrpc_server.go b/rpcserver/jsonrpc_server.go index 4789123..aece06d 100644 --- a/rpcserver/jsonrpc_server.go +++ b/rpcserver/jsonrpc_server.go @@ -76,6 +76,8 @@ type Methods map[string]any type JSONRPCHandlerOpts struct { // Logger, can be nil Log *slog.Logger + // Server name. Used to separate logs and metrics when having multiple servers in one binary. + ServerName string // Max size of the request payload MaxRequestBodySizeBytes int64 // If true payload signature from X-Flashbots-Signature will be verified @@ -121,10 +123,10 @@ func (h *JSONRPCHandler) writeJSONRPCResponse(w http.ResponseWriter, response js w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { if h.Log != nil { - h.Log.Error("failed to marshall response", slog.Any("error", err)) + h.Log.Error("failed to marshall response", slog.Any("error", err), slog.String("serverName", h.ServerName)) } http.Error(w, errMarshalResponse, http.StatusInternalServerError) - incInternalErrors() + incInternalErrors(h.ServerName) return } } @@ -150,19 +152,19 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() defer func() { - incRequestCount(methodForMetrics) - incRequestDuration(methodForMetrics, time.Since(startAt).Milliseconds()) + incRequestCount(methodForMetrics, h.ServerName) + incRequestDuration(methodForMetrics, time.Since(startAt).Milliseconds(), h.ServerName) }() if r.Method != http.MethodPost { http.Error(w, errMethodNotAllowed, http.StatusMethodNotAllowed) - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } if r.Header.Get("Content-Type") != "application/json" { http.Error(w, errWrongContentType, http.StatusUnsupportedMediaType) - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } @@ -171,7 +173,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err != nil { msg := fmt.Sprintf("request body is too big, max size: %d", h.MaxRequestBodySizeBytes) h.writeJSONRPCError(w, nil, CodeInvalidRequest, msg) - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } @@ -180,7 +182,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { signer, err := signature.Verify(signatureHeader, body) if err != nil { h.writeJSONRPCError(w, nil, CodeInvalidRequest, err.Error()) - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } ctx = context.WithValue(ctx, signerKey{}, signer) @@ -190,13 +192,13 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { var req jsonRPCRequest if err := json.Unmarshal(body, &req); err != nil { h.writeJSONRPCError(w, nil, CodeParseError, err.Error()) - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } if req.JSONRPC != "2.0" { h.writeJSONRPCError(w, req.ID, CodeParseError, "invalid jsonrpc version") - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } if req.ID != nil { @@ -205,7 +207,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { case string, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64: default: h.writeJSONRPCError(w, req.ID, CodeParseError, "invalid id type") - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } } @@ -228,7 +230,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if origin != "" { if len(origin) > maxOriginIDLength { h.writeJSONRPCError(w, req.ID, CodeInvalidRequest, "x-flashbots-origin header is too long") - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } ctx = context.WithValue(ctx, originKey{}, origin) @@ -239,7 +241,7 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method, ok := h.methods[req.Method] if !ok { h.writeJSONRPCError(w, req.ID, CodeMethodNotFound, "method not found") - incIncorrectRequest() + incIncorrectRequest(h.ServerName) return } methodForMetrics = req.Method @@ -248,14 +250,14 @@ func (h *JSONRPCHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { result, err := method.call(ctx, req.Params) if err != nil { h.writeJSONRPCError(w, req.ID, CodeCustomError, err.Error()) - incRequestErrorCount(methodForMetrics) + incRequestErrorCount(methodForMetrics, h.ServerName) return } marshaledResult, err := json.Marshal(result) if err != nil { h.writeJSONRPCError(w, req.ID, CodeInternalError, err.Error()) - incInternalErrors() + incInternalErrors(h.ServerName) return } diff --git a/rpcserver/metrics.go b/rpcserver/metrics.go index 86f11eb..828aee5 100644 --- a/rpcserver/metrics.go +++ b/rpcserver/metrics.go @@ -6,45 +6,46 @@ import ( "github.com/VictoriaMetrics/metrics" ) -var ( - // incremented when user made incorrect request - incorrectRequestCounter = metrics.NewCounter("goutils_rpcserver_incorrect_request_total") - // incremented when server has a bug (e.g. can't marshall response) - internalErrorsCounter = metrics.NewCounter("goutils_rpcserver_internal_errors_total") -) - const ( // we use unknown method label for methods that server does not support because otherwise // users can create arbitrary number of metrics unknownMethodLabel = "unknown" + // incremented when user made incorrect request + incorrectRequestCounter = `goutils_rpcserver_incorrect_request_total{server_name="%s"}` + + // incremented when server has a bug (e.g. can't marshall response) + internalErrorsCounter = `goutils_rpcserver_internal_errors_total{server_name="%s"}` + // incremented when request comes in - requestCountLabel = `goutils_rpcserver_request_count{method="%s"}` + requestCountLabel = `goutils_rpcserver_request_count{method="%s",server_name="%s"}` // incremented when handler method returns JSONRPC error - errorCountLabel = `goutils_rpcserver_error_count{method="%s"}` + errorCountLabel = `goutils_rpcserver_error_count{method="%s",server_name="%s"}` // total duration of the request - requestDurationLabel = `goutils_rpcserver_request_duration_milliseconds{method="%s"}` + requestDurationLabel = `goutils_rpcserver_request_duration_milliseconds{method="%s",server_name="%s"}` ) -func incRequestCount(method string) { - l := fmt.Sprintf(requestCountLabel, method) +func incRequestCount(method, serverName string) { + l := fmt.Sprintf(requestCountLabel, method, serverName) metrics.GetOrCreateCounter(l).Inc() } -func incIncorrectRequest() { - incorrectRequestCounter.Inc() +func incIncorrectRequest(serverName string) { + l := fmt.Sprintf(incorrectRequestCounter, serverName) + metrics.GetOrCreateCounter(l).Inc() } -func incRequestErrorCount(method string) { - l := fmt.Sprintf(errorCountLabel, method) +func incRequestErrorCount(method, serverName string) { + l := fmt.Sprintf(errorCountLabel, method, serverName) metrics.GetOrCreateCounter(l).Inc() } -func incRequestDuration(method string, duration int64) { - l := fmt.Sprintf(requestDurationLabel, method) +func incRequestDuration(method string, duration int64, serverName string) { + l := fmt.Sprintf(requestDurationLabel, method, serverName) metrics.GetOrCreateSummary(l).Update(float64(duration)) } -func incInternalErrors() { - internalErrorsCounter.Inc() +func incInternalErrors(serverName string) { + l := fmt.Sprintf(internalErrorsCounter, serverName) + metrics.GetOrCreateCounter(l).Inc() }