2016-11-28 22:39:17 -03: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 .
* /
package main
import (
2017-02-22 22:51:53 +01:00
"encoding/json"
2016-11-28 22:39:17 -03:00
"fmt"
2017-02-22 22:51:53 +01:00
"github.com/golang/glog"
2016-11-28 22:39:17 -03:00
"io/ioutil"
"net/http"
"regexp"
"strconv"
)
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+) ` )
)
type nginxStatus struct {
// 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 22:51:53 +01:00
// https://github.com/vozlt/nginx-module-vts
type Vts struct {
NginxVersion string ` json:"nginxVersion" `
LoadMsec int ` json:"loadMsec" `
NowMsec int ` json:"nowMsec" `
// Total connections and requests(same as stub_status_module in NGINX)
Connections Connections ` json:"connections" `
// Traffic(in/out) and request and response counts and cache hit ratio per each server zone
ServerZones map [ string ] ServerZone ` json:"serverZones" `
// 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
FilterZones map [ string ] map [ string ] FilterZone ` json:"filterZones" `
// Traffic(in/out) and request and response counts per server in each upstream group
UpstreamZones map [ string ] [ ] UpstreamZone ` json:"upstreamZones" `
}
type ServerZone struct {
RequestCounter float64 ` json:"requestCounter" `
InBytes float64 ` json:"inBytes" `
OutBytes float64 ` json:"outBytes" `
Responses Response ` json:"responses" `
Cache Cache ` json:"responses" `
}
type FilterZone struct {
RequestCounter float64 ` json:"requestCounter" `
InBytes float64 ` json:"inBytes" `
OutBytes float64 ` json:"outBytes" `
Cache Cache ` json:"responses" `
Responses Response ` json:"responses" `
}
type UpstreamZone struct {
Responses Response ` json:"responses" `
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" `
}
type Cache struct {
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" `
}
type Response struct {
OneXx float64 ` json:"1xx" `
TwoXx float64 ` json:"2xx" `
TheeXx float64 ` json:"3xx" `
FourXx float64 ` json:"4xx" `
FiveXx float64 ` json:"5xx" `
}
type Connections struct {
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
}
2016-11-28 22:39:17 -03:00
func getNginxStatus ( ) ( * nginxStatus , error ) {
2017-02-22 22:51:53 +01: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-28 22:39:17 -03:00
if err != nil {
return nil , fmt . Errorf ( "unexpected error scraping nginx status page: %v" , err )
}
2017-02-22 22:51:53 +01: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-28 22:39:17 -03:00
data , err := ioutil . ReadAll ( resp . Body )
if err != nil {
2017-02-22 22:51:53 +01:00
return nil , fmt . Errorf ( "unexpected error scraping nginx (%v)" , err )
2016-11-28 22:39:17 -03:00
}
defer resp . Body . Close ( )
if resp . StatusCode < 200 || resp . StatusCode >= 400 {
2017-02-22 22:51:53 +01:00
return nil , fmt . Errorf ( "unexpected error scraping nginx (status %v)" , resp . StatusCode )
2016-11-28 22:39:17 -03:00
}
2017-02-22 22:51:53 +01:00
return data , nil
}
func getNginxVtsMetrics ( ) ( * Vts , error ) {
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 )
}
var vts Vts
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 )
return & vts , nil
2016-11-28 22:39:17 -03:00
}
func parse ( data string ) * nginxStatus {
acr := ac . FindStringSubmatch ( data )
sahrr := sahr . FindStringSubmatch ( data )
readingr := reading . FindStringSubmatch ( data )
writingr := writing . FindStringSubmatch ( data )
waitingr := waiting . FindStringSubmatch ( data )
return & nginxStatus {
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
}