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 = "ok"
|
||||||
# response_string_match = "\".*_status\".?:.?\"up\""
|
# 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
|
## Optional TLS Config
|
||||||
# tls_ca = "/etc/telegraf/ca.pem"
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
# tls_cert = "/etc/telegraf/cert.pem"
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
|
@ -83,6 +89,7 @@ This input plugin checks HTTP/HTTPS connections.
|
||||||
- response_time (float, seconds)
|
- response_time (float, seconds)
|
||||||
- content_length (int, response body length)
|
- content_length (int, response body length)
|
||||||
- response_string_match (int, 0 = mismatch / body read error, 1 = match)
|
- 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)
|
- http_response_code (int, response status code)
|
||||||
- result_type (string, deprecated in 1.6: use `result` tag and `result_code` field)
|
- result_type (string, deprecated in 1.6: use `result` tag and `result_code` field)
|
||||||
- result_code (int, [see below](#result--result_code))
|
- result_code (int, [see below](#result--result_code))
|
||||||
|
|
@ -94,13 +101,14 @@ 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.
|
This tag is used to expose network and plugin errors. HTTP errors are considered a successful connection.
|
||||||
|
|
||||||
|Tag value |Corresponding field value|Description|
|
|Tag value |Corresponding field value|Description|
|
||||||
--------------------------|-------------------------|-----------|
|
-------------------------------|-------------------------|-----------|
|
||||||
|success | 0 |The HTTP request completed, even if the HTTP code represents an error|
|
|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|
|
|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` |
|
|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|
|
|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|
|
|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|
|
|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:
|
### Example Output:
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ type HTTPResponse struct {
|
||||||
ResponseBodyField string `toml:"response_body_field"`
|
ResponseBodyField string `toml:"response_body_field"`
|
||||||
ResponseBodyMaxSize internal.Size `toml:"response_body_max_size"`
|
ResponseBodyMaxSize internal.Size `toml:"response_body_max_size"`
|
||||||
ResponseStringMatch string
|
ResponseStringMatch string
|
||||||
|
ResponseStatusCode int
|
||||||
Interface string
|
Interface string
|
||||||
// HTTP Basic Auth Credentials
|
// HTTP Basic Auth Credentials
|
||||||
Username string `toml:"username"`
|
Username string `toml:"username"`
|
||||||
|
|
@ -106,6 +107,12 @@ var sampleConfig = `
|
||||||
# response_string_match = "ok"
|
# response_string_match = "ok"
|
||||||
# response_string_match = "\".*_status\".?:.?\"up\""
|
# 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
|
## Optional TLS Config
|
||||||
# tls_ca = "/etc/telegraf/ca.pem"
|
# tls_ca = "/etc/telegraf/ca.pem"
|
||||||
# tls_cert = "/etc/telegraf/cert.pem"
|
# tls_cert = "/etc/telegraf/cert.pem"
|
||||||
|
|
@ -214,6 +221,7 @@ func setResult(result_string string, fields map[string]interface{}, tags map[str
|
||||||
"connection_failed": 3,
|
"connection_failed": 3,
|
||||||
"timeout": 4,
|
"timeout": 4,
|
||||||
"dns_error": 5,
|
"dns_error": 5,
|
||||||
|
"response_status_code_mismatch": 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
tags["result"] = result_string
|
tags["result"] = result_string
|
||||||
|
|
@ -352,16 +360,31 @@ func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]
|
||||||
}
|
}
|
||||||
fields["content_length"] = len(bodyBytes)
|
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.ResponseStringMatch != "" {
|
||||||
if h.compiledStringMatch.Match(bodyBytes) {
|
if h.compiledStringMatch.Match(bodyBytes) {
|
||||||
setResult("success", fields, tags)
|
|
||||||
fields["response_string_match"] = 1
|
fields["response_string_match"] = 1
|
||||||
} else {
|
} else {
|
||||||
|
success = false
|
||||||
setResult("response_string_mismatch", fields, tags)
|
setResult("response_string_mismatch", fields, tags)
|
||||||
fields["response_string_match"] = 0
|
fields["response_string_match"] = 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the response status code
|
||||||
|
if h.ResponseStatusCode > 0 {
|
||||||
|
if resp.StatusCode == h.ResponseStatusCode {
|
||||||
|
fields["response_status_code_match"] = 1
|
||||||
} else {
|
} else {
|
||||||
|
success = false
|
||||||
|
setResult("response_status_code_mismatch", fields, tags)
|
||||||
|
fields["response_status_code_match"] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if success {
|
||||||
setResult("success", fields, tags)
|
setResult("success", fields, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,9 @@ func setUpTestMux() http.Handler {
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
|
mux.HandleFunc("/nocontent", func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
})
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1110,3 +1113,139 @@ func TestBasicAuth(t *testing.T) {
|
||||||
absentFields := []string{"response_string_match"}
|
absentFields := []string{"response_string_match"}
|
||||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
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