2016-11-29 01:39:17 +00:00
/ *
Copyright 2016 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2017-03-10 13:01:26 +00:00
package collector
2016-11-29 01:39:17 +00:00
import (
2017-02-22 21:51:53 +00:00
"encoding/json"
2016-11-29 01:39:17 +00:00
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
2017-03-10 13:01:26 +00:00
"github.com/golang/glog"
2016-11-29 01:39:17 +00:00
)
var (
ac = regexp . MustCompile ( ` Active connections: (\d+) ` )
sahr = regexp . MustCompile ( ` (\d+)\s(\d+)\s(\d+) ` )
reading = regexp . MustCompile ( ` Reading: (\d+) ` )
writing = regexp . MustCompile ( ` Writing: (\d+) ` )
waiting = regexp . MustCompile ( ` Waiting: (\d+) ` )
)
2017-03-10 13:01:26 +00:00
type basicStatus struct {
2016-11-29 01:39:17 +00:00
// Active total number of active connections
Active int
// Accepted total number of accepted client connections
Accepted int
// Handled total number of handled connections. Generally, the parameter value is the same as accepts unless some resource limits have been reached (for example, the worker_connections limit).
Handled int
// Requests total number of client requests.
Requests int
// Reading current number of connections where nginx is reading the request header.
Reading int
// Writing current number of connections where nginx is writing the response back to the client.
Writing int
// Waiting current number of idle client connections waiting for a request.
Waiting int
}
2017-02-22 21:51:53 +00:00
// https://github.com/vozlt/nginx-module-vts
2017-03-10 13:01:26 +00:00
type vts struct {
2017-02-22 21:51:53 +00:00
NginxVersion string ` json:"nginxVersion" `
LoadMsec int ` json:"loadMsec" `
NowMsec int ` json:"nowMsec" `
// Total connections and requests(same as stub_status_module in NGINX)
2017-03-10 13:01:26 +00:00
Connections connections ` json:"connections" `
2017-02-22 21:51:53 +00:00
// Traffic(in/out) and request and response counts and cache hit ratio per each server zone
2017-03-10 13:01:26 +00:00
ServerZones map [ string ] serverZone ` json:"serverZones" `
2017-02-22 21:51:53 +00:00
// Traffic(in/out) and request and response counts and cache hit ratio per each server zone filtered through
// the vhost_traffic_status_filter_by_set_key directive
2017-03-10 13:01:26 +00:00
FilterZones map [ string ] map [ string ] filterZone ` json:"filterZones" `
2017-02-22 21:51:53 +00:00
// Traffic(in/out) and request and response counts per server in each upstream group
2017-03-10 13:01:26 +00:00
UpstreamZones map [ string ] [ ] upstreamZone ` json:"upstreamZones" `
2017-02-22 21:51:53 +00:00
}
2017-03-10 13:01:26 +00:00
type serverZone struct {
2017-02-22 21:51:53 +00:00
RequestCounter float64 ` json:"requestCounter" `
InBytes float64 ` json:"inBytes" `
OutBytes float64 ` json:"outBytes" `
2017-03-10 13:01:26 +00:00
Responses response ` json:"responses" `
Cache cache ` json:"responses" `
2017-02-22 21:51:53 +00:00
}
2017-03-10 13:01:26 +00:00
type filterZone struct {
2017-02-22 21:51:53 +00:00
RequestCounter float64 ` json:"requestCounter" `
InBytes float64 ` json:"inBytes" `
OutBytes float64 ` json:"outBytes" `
2017-03-10 13:01:26 +00:00
Cache cache ` json:"responses" `
Responses response ` json:"responses" `
2017-02-22 21:51:53 +00:00
}
2017-03-10 13:01:26 +00:00
type upstreamZone struct {
Responses response ` json:"responses" `
2017-02-22 21:51:53 +00:00
Server string ` json:"server" `
RequestCounter float64 ` json:"requestCounter" `
InBytes float64 ` json:"inBytes" `
OutBytes float64 ` json:"outBytes" `
ResponseMsec float64 ` json:"responseMsec" `
Weight float64 ` json:"weight" `
MaxFails float64 ` json:"maxFails" `
FailTimeout float64 ` json:"failTimeout" `
Backup BoolToFloat64 ` json:"backup" `
Down BoolToFloat64 ` json:"down" `
}
2017-03-10 13:01:26 +00:00
type cache struct {
2017-02-22 21:51:53 +00:00
Miss float64 ` json:"miss" `
Bypass float64 ` json:"bypass" `
Expired float64 ` json:"expired" `
Stale float64 ` json:"stale" `
Updating float64 ` json:"updating" `
Revalidated float64 ` json:"revalidated" `
Hit float64 ` json:"hit" `
Scarce float64 ` json:"scarce" `
}
2017-03-10 13:01:26 +00:00
type response struct {
2017-02-22 21:51:53 +00:00
OneXx float64 ` json:"1xx" `
TwoXx float64 ` json:"2xx" `
TheeXx float64 ` json:"3xx" `
FourXx float64 ` json:"4xx" `
FiveXx float64 ` json:"5xx" `
}
2017-03-10 13:01:26 +00:00
type connections struct {
2017-02-22 21:51:53 +00:00
Active float64 ` json:"active" `
Reading float64 ` json:"reading" `
Writing float64 ` json:"writing" `
Waiting float64 ` json:"waiting" `
Accepted float64 ` json:"accepted" `
Handled float64 ` json:"handled" `
Requests float64 ` json:"requests" `
}
type BoolToFloat64 float64
func ( bit BoolToFloat64 ) UnmarshalJSON ( data [ ] byte ) error {
asString := string ( data )
if asString == "1" || asString == "true" {
bit = 1
} else if asString == "0" || asString == "false" {
bit = 0
} else {
return fmt . Errorf ( fmt . Sprintf ( "Boolean unmarshal error: invalid input %s" , asString ) )
}
return nil
}
2017-03-10 13:01:26 +00:00
func getNginxStatus ( ) ( * basicStatus , error ) {
2017-02-22 21:51:53 +00:00
url := fmt . Sprintf ( "http://localhost:%v%v" , ngxHealthPort , ngxStatusPath )
glog . V ( 3 ) . Infof ( "start scrapping url: %v" , url )
data , err := httpBody ( url )
2016-11-29 01:39:17 +00:00
if err != nil {
return nil , fmt . Errorf ( "unexpected error scraping nginx status page: %v" , err )
}
2017-02-22 21:51:53 +00:00
return parse ( string ( data ) ) , nil
}
func httpBody ( url string ) ( [ ] byte , error ) {
resp , err := http . DefaultClient . Get ( url )
if err != nil {
return nil , fmt . Errorf ( "unexpected error scraping nginx : %v" , err )
}
2016-11-29 01:39:17 +00:00
data , err := ioutil . ReadAll ( resp . Body )
if err != nil {
2017-02-22 21:51:53 +00:00
return nil , fmt . Errorf ( "unexpected error scraping nginx (%v)" , err )
2016-11-29 01:39:17 +00:00
}
defer resp . Body . Close ( )
if resp . StatusCode < 200 || resp . StatusCode >= 400 {
2017-02-22 21:51:53 +00:00
return nil , fmt . Errorf ( "unexpected error scraping nginx (status %v)" , resp . StatusCode )
2016-11-29 01:39:17 +00:00
}
2017-02-22 21:51:53 +00:00
return data , nil
}
2017-03-10 13:01:26 +00:00
func getNginxVtsMetrics ( ) ( * vts , error ) {
2017-02-22 21:51:53 +00:00
url := fmt . Sprintf ( "http://localhost:%v%v" , ngxHealthPort , ngxVtsPath )
glog . V ( 3 ) . Infof ( "start scrapping url: %v" , url )
data , err := httpBody ( url )
if err != nil {
return nil , fmt . Errorf ( "unexpected error scraping nginx vts (%v)" , err )
}
2017-03-10 13:01:26 +00:00
var vts * vts
2017-02-22 21:51:53 +00:00
err = json . Unmarshal ( data , & vts )
if err != nil {
return nil , fmt . Errorf ( "unexpected error json unmarshal (%v)" , err )
}
glog . V ( 3 ) . Infof ( "scrap returned : %v" , vts )
2017-03-10 13:01:26 +00:00
return vts , nil
2016-11-29 01:39:17 +00:00
}
2017-03-10 13:01:26 +00:00
func parse ( data string ) * basicStatus {
2016-11-29 01:39:17 +00:00
acr := ac . FindStringSubmatch ( data )
sahrr := sahr . FindStringSubmatch ( data )
readingr := reading . FindStringSubmatch ( data )
writingr := writing . FindStringSubmatch ( data )
waitingr := waiting . FindStringSubmatch ( data )
2017-03-10 13:01:26 +00:00
return & basicStatus {
2016-11-29 01:39:17 +00:00
toInt ( acr , 1 ) ,
toInt ( sahrr , 1 ) ,
toInt ( sahrr , 2 ) ,
toInt ( sahrr , 3 ) ,
toInt ( readingr , 1 ) ,
toInt ( writingr , 1 ) ,
toInt ( waitingr , 1 ) ,
}
}
func toInt ( data [ ] string , pos int ) int {
if len ( data ) == 0 {
return 0
}
if pos > len ( data ) {
return 0
}
if v , err := strconv . Atoi ( data [ pos ] ) ; err == nil {
return v
}
return 0
}