mirror of
https://github.com/TwiN/gatus.git
synced 2025-01-03 12:39:39 +01:00
Add endpoints.onErrorAdd
Add endpoints[].onErrorAdd to add response body or parts of it as error, when the conditions fail for easier debugging. Implements https://github.com/TwiN/gatus/issues/742
This commit is contained in:
parent
28339684bf
commit
01c473415e
@ -272,6 +272,7 @@ You can then configure alerts to be triggered when an endpoint is unhealthy once
|
|||||||
| `endpoints[].ui.hide-url` | Whether to ensure the URL is not displayed in the results. Useful if the URL contains a token. | `false` |
|
| `endpoints[].ui.hide-url` | Whether to ensure the URL is not displayed in the results. Useful if the URL contains a token. | `false` |
|
||||||
| `endpoints[].ui.dont-resolve-failed-conditions` | Whether to resolve failed conditions for the UI. | `false` |
|
| `endpoints[].ui.dont-resolve-failed-conditions` | Whether to resolve failed conditions for the UI. | `false` |
|
||||||
| `endpoints[].ui.badge.reponse-time` | List of response time thresholds. Each time a threshold is reached, the badge has a different color. | `[50, 200, 300, 500, 750]` |
|
| `endpoints[].ui.badge.reponse-time` | List of response time thresholds. Each time a threshold is reached, the badge has a different color. | `[50, 200, 300, 500, 750]` |
|
||||||
|
| `endpoints[].onErrorAdd` | Add whole response Body ("[BODY]") or part of JSON ("[BODY].error") when an error occurs. | `""` |
|
||||||
|
|
||||||
|
|
||||||
### External Endpoints
|
### External Endpoints
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/TwiN/gatus/v5/jsonpath"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -120,6 +121,9 @@ type Endpoint struct {
|
|||||||
|
|
||||||
// NumberOfSuccessesInARow is the number of successful evaluations in a row
|
// NumberOfSuccessesInARow is the number of successful evaluations in a row
|
||||||
NumberOfSuccessesInARow int `yaml:"-"`
|
NumberOfSuccessesInARow int `yaml:"-"`
|
||||||
|
|
||||||
|
// OnErrorAdd is the message or [BODY] or part of [BODY].json to add to the result's errors if the endpoint fails
|
||||||
|
OnErrorAdd string `yaml:"onErrorAdd,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEnabled returns whether the endpoint is enabled or not
|
// IsEnabled returns whether the endpoint is enabled or not
|
||||||
@ -302,6 +306,25 @@ func (endpoint *Endpoint) EvaluateHealth() *Result {
|
|||||||
if endpoint.UIConfig.HideConditions {
|
if endpoint.UIConfig.HideConditions {
|
||||||
result.ConditionResults = nil
|
result.ConditionResults = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if endpoint.OnErrorAdd != "" && !result.Success {
|
||||||
|
if endpoint.OnErrorAdd == BodyPlaceholder && len(result.Body) > 0 {
|
||||||
|
result.Errors = append(result.Errors, string(result.Body))
|
||||||
|
} else if strings.Contains(endpoint.OnErrorAdd, BodyPlaceholder) && len(result.Body) > 0 {
|
||||||
|
resolvedElement, resolvedElementLength, err := jsonpath.Eval(strings.TrimPrefix(
|
||||||
|
strings.TrimPrefix(endpoint.OnErrorAdd, BodyPlaceholder), "."), result.Body)
|
||||||
|
if err != nil {
|
||||||
|
result.Errors = append(result.Errors, "Decoding of endpoint.OnErrorAdd failed: "+err.Error())
|
||||||
|
} else {
|
||||||
|
if resolvedElementLength > 0 {
|
||||||
|
result.Errors = append(result.Errors, resolvedElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.Errors = append(result.Errors, endpoint.OnErrorAdd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +440,9 @@ func (endpoint *Endpoint) buildHTTPRequest() *http.Request {
|
|||||||
|
|
||||||
// needsToReadBody checks if there's any condition that requires the response Body to be read
|
// needsToReadBody checks if there's any condition that requires the response Body to be read
|
||||||
func (endpoint *Endpoint) needsToReadBody() bool {
|
func (endpoint *Endpoint) needsToReadBody() bool {
|
||||||
|
if strings.Contains(endpoint.OnErrorAdd, BodyPlaceholder) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
for _, condition := range endpoint.Conditions {
|
for _, condition := range endpoint.Conditions {
|
||||||
if condition.hasBodyPlaceholder() {
|
if condition.hasBodyPlaceholder() {
|
||||||
return true
|
return true
|
||||||
|
@ -204,6 +204,29 @@ func TestEndpoint(t *testing.T) {
|
|||||||
},
|
},
|
||||||
MockRoundTripper: nil,
|
MockRoundTripper: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "failed-status-with-error-from-body",
|
||||||
|
Endpoint: Endpoint{
|
||||||
|
Name: "website-health",
|
||||||
|
URL: "https://twin.sh/health",
|
||||||
|
Conditions: []Condition{"[STATUS] == 200"},
|
||||||
|
OnErrorAdd: "[BODY].error",
|
||||||
|
},
|
||||||
|
ExpectedResult: &Result{
|
||||||
|
Success: false,
|
||||||
|
Connected: true,
|
||||||
|
Hostname: "twin.sh",
|
||||||
|
ConditionResults: []*ConditionResult{
|
||||||
|
{Condition: "[STATUS] (502) == 200", Success: false},
|
||||||
|
},
|
||||||
|
DomainExpiration: 0, // Because there's no [DOMAIN_EXPIRATION] condition, this is not resolved, so it should be 0.
|
||||||
|
Errors: []string{"something went wrong"},
|
||||||
|
},
|
||||||
|
MockRoundTripper: test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||||
|
return &http.Response{StatusCode: http.StatusBadGateway, Body: io.NopCloser(strings.NewReader(
|
||||||
|
"{\"error\":\"something went wrong\"}"))}
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(scenario.Name, func(t *testing.T) {
|
t.Run(scenario.Name, func(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user