http_response: match on status code (#8032)
This commit is contained in:
parent
78cf0b7ea6
commit
527a11a656
|
|
@ -51,6 +51,12 @@ This input plugin checks HTTP/HTTPS connections.
|
|||
# response_string_match = "ok"
|
||||
# response_string_match = "\".*_status\".?:.?\"up\""
|
||||
|
||||
## Expected response status code.
|
||||
## The status code of the response is compared to this value. If they match, the field
|
||||
## "response_status_code_match" will be 1, otherwise it will be 0. If the
|
||||
## expected status code is 0, the check is disabled and the field won't be added.
|
||||
# response_status_code = 0
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
|
|
@ -83,6 +89,7 @@ This input plugin checks HTTP/HTTPS connections.
|
|||
- response_time (float, seconds)
|
||||
- content_length (int, response body length)
|
||||
- response_string_match (int, 0 = mismatch / body read error, 1 = match)
|
||||
- response_status_code_match (int, 0 = mismatch, 1 = match)
|
||||
- http_response_code (int, response status code)
|
||||
- result_type (string, deprecated in 1.6: use `result` tag and `result_code` field)
|
||||
- result_code (int, [see below](#result--result_code))
|
||||
|
|
@ -93,14 +100,15 @@ Upon finishing polling the target server, the plugin registers the result of the
|
|||
|
||||
This tag is used to expose network and plugin errors. HTTP errors are considered a successful connection.
|
||||
|
||||
|Tag value |Corresponding field value|Description|
|
||||
--------------------------|-------------------------|-----------|
|
||||
|success | 0 |The HTTP request completed, even if the HTTP code represents an error|
|
||||
|response_string_mismatch | 1 |The option `response_string_match` was used, and the body of the response didn't match the regex. HTTP errors with content in their body (like 4xx, 5xx) will trigger this error|
|
||||
|body_read_error | 2 |The option `response_string_match` was used, but the plugin wasn't able to read the body of the response. Responses with empty bodies (like 3xx, HEAD, etc) will trigger this error. Or the option `response_body_field` was used and the content of the response body was not a valid utf-8. Or the size of the body of the response exceeded the `response_body_max_size` |
|
||||
|connection_failed | 3 |Catch all for any network error not specifically handled by the plugin|
|
||||
|timeout | 4 |The plugin timed out while awaiting the HTTP connection to complete|
|
||||
|dns_error | 5 |There was a DNS error while attempting to connect to the host|
|
||||
|Tag value |Corresponding field value|Description|
|
||||
-------------------------------|-------------------------|-----------|
|
||||
|success | 0 |The HTTP request completed, even if the HTTP code represents an error|
|
||||
|response_string_mismatch | 1 |The option `response_string_match` was used, and the body of the response didn't match the regex. HTTP errors with content in their body (like 4xx, 5xx) will trigger this error|
|
||||
|body_read_error | 2 |The option `response_string_match` was used, but the plugin wasn't able to read the body of the response. Responses with empty bodies (like 3xx, HEAD, etc) will trigger this error. Or the option `response_body_field` was used and the content of the response body was not a valid utf-8. Or the size of the body of the response exceeded the `response_body_max_size` |
|
||||
|connection_failed | 3 |Catch all for any network error not specifically handled by the plugin|
|
||||
|timeout | 4 |The plugin timed out while awaiting the HTTP connection to complete|
|
||||
|dns_error | 5 |There was a DNS error while attempting to connect to the host|
|
||||
|response_status_code_mismatch | 6 |The option `response_status_code_match` was used, and the status code of the response didn't match the value.|
|
||||
|
||||
|
||||
### Example Output:
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ type HTTPResponse struct {
|
|||
ResponseBodyField string `toml:"response_body_field"`
|
||||
ResponseBodyMaxSize internal.Size `toml:"response_body_max_size"`
|
||||
ResponseStringMatch string
|
||||
ResponseStatusCode int
|
||||
Interface string
|
||||
// HTTP Basic Auth Credentials
|
||||
Username string `toml:"username"`
|
||||
|
|
@ -106,6 +107,12 @@ var sampleConfig = `
|
|||
# response_string_match = "ok"
|
||||
# response_string_match = "\".*_status\".?:.?\"up\""
|
||||
|
||||
## Expected response status code.
|
||||
## The status code of the response is compared to this value. If they match, the field
|
||||
## "response_status_code_match" will be 1, otherwise it will be 0. If the
|
||||
## expected status code is 0, the check is disabled and the field won't be added.
|
||||
# response_status_code = 0
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
|
|
@ -208,12 +215,13 @@ func localAddress(interfaceName string) (net.Addr, error) {
|
|||
|
||||
func setResult(result_string string, fields map[string]interface{}, tags map[string]string) {
|
||||
result_codes := map[string]int{
|
||||
"success": 0,
|
||||
"response_string_mismatch": 1,
|
||||
"body_read_error": 2,
|
||||
"connection_failed": 3,
|
||||
"timeout": 4,
|
||||
"dns_error": 5,
|
||||
"success": 0,
|
||||
"response_string_mismatch": 1,
|
||||
"body_read_error": 2,
|
||||
"connection_failed": 3,
|
||||
"timeout": 4,
|
||||
"dns_error": 5,
|
||||
"response_status_code_mismatch": 6,
|
||||
}
|
||||
|
||||
tags["result"] = result_string
|
||||
|
|
@ -352,16 +360,31 @@ func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]
|
|||
}
|
||||
fields["content_length"] = len(bodyBytes)
|
||||
|
||||
// Check the response for a regex match.
|
||||
var success = true
|
||||
|
||||
// Check the response for a regex
|
||||
if h.ResponseStringMatch != "" {
|
||||
if h.compiledStringMatch.Match(bodyBytes) {
|
||||
setResult("success", fields, tags)
|
||||
fields["response_string_match"] = 1
|
||||
} else {
|
||||
success = false
|
||||
setResult("response_string_mismatch", fields, tags)
|
||||
fields["response_string_match"] = 0
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
// Check the response status code
|
||||
if h.ResponseStatusCode > 0 {
|
||||
if resp.StatusCode == h.ResponseStatusCode {
|
||||
fields["response_status_code_match"] = 1
|
||||
} else {
|
||||
success = false
|
||||
setResult("response_status_code_mismatch", fields, tags)
|
||||
fields["response_status_code_match"] = 0
|
||||
}
|
||||
}
|
||||
|
||||
if success {
|
||||
setResult("success", fields, tags)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ func setUpTestMux() http.Handler {
|
|||
time.Sleep(time.Second * 2)
|
||||
return
|
||||
})
|
||||
mux.HandleFunc("/nocontent", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
return mux
|
||||
}
|
||||
|
||||
|
|
@ -1110,3 +1113,139 @@ func TestBasicAuth(t *testing.T) {
|
|||
absentFields := []string{"response_string_match"}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
||||
}
|
||||
|
||||
func TestStatusCodeMatchFail(t *testing.T) {
|
||||
mux := setUpTestMux()
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
h := &HTTPResponse{
|
||||
Log: testutil.Logger{},
|
||||
Address: ts.URL + "/nocontent",
|
||||
ResponseStatusCode: http.StatusOK,
|
||||
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
"response_status_code_match": 0,
|
||||
"result_type": "response_status_code_mismatch",
|
||||
"result_code": 6,
|
||||
"response_time": nil,
|
||||
"content_length": nil,
|
||||
}
|
||||
expectedTags := map[string]interface{}{
|
||||
"server": nil,
|
||||
"method": http.MethodGet,
|
||||
"status_code": "204",
|
||||
"result": "response_status_code_mismatch",
|
||||
}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, nil, nil)
|
||||
}
|
||||
|
||||
func TestStatusCodeMatch(t *testing.T) {
|
||||
mux := setUpTestMux()
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
h := &HTTPResponse{
|
||||
Log: testutil.Logger{},
|
||||
Address: ts.URL + "/nocontent",
|
||||
ResponseStatusCode: http.StatusNoContent,
|
||||
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
"response_status_code_match": 1,
|
||||
"result_type": "success",
|
||||
"result_code": 0,
|
||||
"response_time": nil,
|
||||
"content_length": nil,
|
||||
}
|
||||
expectedTags := map[string]interface{}{
|
||||
"server": nil,
|
||||
"method": http.MethodGet,
|
||||
"status_code": "204",
|
||||
"result": "success",
|
||||
}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, nil, nil)
|
||||
}
|
||||
|
||||
func TestStatusCodeAndStringMatch(t *testing.T) {
|
||||
mux := setUpTestMux()
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
h := &HTTPResponse{
|
||||
Log: testutil.Logger{},
|
||||
Address: ts.URL + "/good",
|
||||
ResponseStatusCode: http.StatusOK,
|
||||
ResponseStringMatch: "hit the good page",
|
||||
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
"response_status_code_match": 1,
|
||||
"response_string_match": 1,
|
||||
"result_type": "success",
|
||||
"result_code": 0,
|
||||
"response_time": nil,
|
||||
"content_length": nil,
|
||||
}
|
||||
expectedTags := map[string]interface{}{
|
||||
"server": nil,
|
||||
"method": http.MethodGet,
|
||||
"status_code": "200",
|
||||
"result": "success",
|
||||
}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, nil, nil)
|
||||
}
|
||||
|
||||
func TestStatusCodeAndStringMatchFail(t *testing.T) {
|
||||
mux := setUpTestMux()
|
||||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
h := &HTTPResponse{
|
||||
Log: testutil.Logger{},
|
||||
Address: ts.URL + "/nocontent",
|
||||
ResponseStatusCode: http.StatusOK,
|
||||
ResponseStringMatch: "hit the good page",
|
||||
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
"response_status_code_match": 0,
|
||||
"response_string_match": 0,
|
||||
"result_type": "response_status_code_mismatch",
|
||||
"result_code": 6,
|
||||
"response_time": nil,
|
||||
"content_length": nil,
|
||||
}
|
||||
expectedTags := map[string]interface{}{
|
||||
"server": nil,
|
||||
"method": http.MethodGet,
|
||||
"status_code": "204",
|
||||
"result": "response_status_code_mismatch",
|
||||
}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, nil, nil)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue