2015-09-26 08:36:40 -07:00
// Copyright 2015 The Prometheus 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.
2013-05-07 07:40:10 -07:00
package main
import (
"flag"
2014-06-04 06:09:33 -07:00
"fmt"
2014-02-18 03:35:11 -08:00
"net/http"
2015-03-05 08:02:17 -08:00
_ "net/http/pprof"
2014-02-18 03:35:11 -08:00
"os"
"os/signal"
2015-05-12 08:04:08 -07:00
"sort"
2014-06-04 04:12:34 -07:00
"strings"
2014-02-18 03:35:11 -08:00
"sync"
"syscall"
"time"
2014-02-07 08:09:39 -08:00
2014-02-18 03:35:11 -08:00
"github.com/prometheus/client_golang/prometheus"
2015-05-28 12:21:44 -07:00
"github.com/prometheus/log"
2014-02-18 03:35:11 -08:00
"github.com/prometheus/node_exporter/collector"
2013-05-07 07:40:10 -07:00
)
2014-06-26 10:20:36 -07:00
const subsystem = "exporter"
2013-05-07 07:40:10 -07:00
var (
2015-10-16 12:17:44 -07:00
// Version of node_exporter. Set at build time.
2015-04-15 20:51:09 -07:00
Version = "0.0.0.dev"
2015-02-08 15:21:57 -08:00
memProfile = flag . String ( "debug.memprofile-file" , "" , "Write memory profile to this file upon receipt of SIGUSR1." )
listenAddress = flag . String ( "web.listen-address" , ":9100" , "Address on which to expose metrics and web interface." )
metricsPath = flag . String ( "web.telemetry-path" , "/metrics" , "Path under which to expose metrics." )
2015-09-07 06:49:30 -07:00
enabledCollectors = flag . String ( "collectors.enabled" , "diskstats,filefd,filesystem,loadavg,mdadm,meminfo,netdev,netstat,sockstat,stat,textfile,time,uname" , "Comma-separated list of collectors to use." )
2015-02-08 15:21:57 -08:00
printCollectors = flag . Bool ( "collectors.print" , false , "If true, print available collectors and exit." )
2014-12-17 09:59:06 -08:00
authUser = flag . String ( "auth.user" , "" , "Username for basic auth." )
authPass = flag . String ( "auth.pass" , "" , "Password for basic auth." )
2014-06-26 10:20:36 -07:00
collectorLabelNames = [ ] string { "collector" , "result" }
scrapeDurations = prometheus . NewSummaryVec (
prometheus . SummaryOpts {
Namespace : collector . Namespace ,
Subsystem : subsystem ,
Name : "scrape_duration_seconds" ,
Help : "node_exporter: Duration of a scrape job." ,
} ,
collectorLabelNames ,
)
2013-05-07 07:40:10 -07:00
)
2015-10-16 12:17:44 -07:00
// NodeCollector implements the prometheus.Collector interface.
2014-10-29 07:16:43 -07:00
type NodeCollector struct {
collectors map [ string ] collector . Collector
}
2015-10-16 12:17:44 -07:00
// Describe implements the prometheus.Collector interface.
2014-10-29 07:16:43 -07:00
func ( n NodeCollector ) Describe ( ch chan <- * prometheus . Desc ) {
scrapeDurations . Describe ( ch )
}
2015-10-16 12:17:44 -07:00
// Collect implements the prometheus.Collector interface.
2014-10-29 07:16:43 -07:00
func ( n NodeCollector ) Collect ( ch chan <- prometheus . Metric ) {
wg := sync . WaitGroup { }
wg . Add ( len ( n . collectors ) )
for name , c := range n . collectors {
go func ( name string , c collector . Collector ) {
2015-10-16 12:17:44 -07:00
execute ( name , c , ch )
2014-10-29 07:16:43 -07:00
wg . Done ( )
} ( name , c )
}
wg . Wait ( )
scrapeDurations . Collect ( ch )
}
2014-12-17 09:59:06 -08:00
type basicAuthHandler struct {
handler http . HandlerFunc
user string
password string
}
func ( h * basicAuthHandler ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
user , password , ok := r . BasicAuth ( )
if ! ok || password != h . password || user != h . user {
w . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=\"metrics\"" )
http . Error ( w , "Invalid username or password" , http . StatusUnauthorized )
return
}
h . handler ( w , r )
return
}
2015-10-16 12:17:44 -07:00
func execute ( name string , c collector . Collector , ch chan <- prometheus . Metric ) {
2014-10-29 07:16:43 -07:00
begin := time . Now ( )
err := c . Update ( ch )
duration := time . Since ( begin )
var result string
if err != nil {
2015-06-22 04:32:08 -07:00
log . Errorf ( "ERROR: %s collector failed after %fs: %s" , name , duration . Seconds ( ) , err )
2014-10-29 07:16:43 -07:00
result = "error"
} else {
2015-06-22 04:32:08 -07:00
log . Debugf ( "OK: %s collector succeeded after %fs." , name , duration . Seconds ( ) )
2014-10-29 07:16:43 -07:00
result = "success"
}
scrapeDurations . WithLabelValues ( name , result ) . Observe ( duration . Seconds ( ) )
}
2015-05-20 11:04:49 -07:00
func loadCollectors ( ) ( map [ string ] collector . Collector , error ) {
2014-10-29 07:16:43 -07:00
collectors := map [ string ] collector . Collector { }
for _ , name := range strings . Split ( * enabledCollectors , "," ) {
fn , ok := collector . Factories [ name ]
if ! ok {
2014-11-24 14:15:13 -08:00
return nil , fmt . Errorf ( "collector '%s' not available" , name )
2014-10-29 07:16:43 -07:00
}
2015-05-20 11:04:49 -07:00
c , err := fn ( )
2014-10-29 07:16:43 -07:00
if err != nil {
return nil , err
}
collectors [ name ] = c
}
return collectors , nil
}
2013-05-07 07:40:10 -07:00
func main ( ) {
flag . Parse ( )
2015-04-15 20:51:09 -07:00
2014-06-04 06:09:33 -07:00
if * printCollectors {
2015-05-12 08:04:08 -07:00
collectorNames := make ( sort . StringSlice , 0 , len ( collector . Factories ) )
2015-10-16 12:17:44 -07:00
for n := range collector . Factories {
2015-05-12 08:04:08 -07:00
collectorNames = append ( collectorNames , n )
}
collectorNames . Sort ( )
fmt . Printf ( "Available collectors:\n" )
for _ , n := range collectorNames {
2014-06-04 06:09:33 -07:00
fmt . Printf ( " - %s\n" , n )
}
return
}
2015-05-20 11:04:49 -07:00
collectors , err := loadCollectors ( )
2013-05-07 07:40:10 -07:00
if err != nil {
2015-05-28 12:21:44 -07:00
log . Fatalf ( "Couldn't load collectors: %s" , err )
2013-05-07 07:40:10 -07:00
}
2014-02-18 03:35:11 -08:00
2015-05-28 12:21:44 -07:00
log . Infof ( "Enabled collectors:" )
2015-10-16 12:17:44 -07:00
for n := range collectors {
2015-05-28 12:21:44 -07:00
log . Infof ( " - %s" , n )
2014-02-07 08:09:39 -08:00
}
2014-02-18 03:35:11 -08:00
2014-10-29 07:16:43 -07:00
nodeCollector := NodeCollector { collectors : collectors }
prometheus . MustRegister ( nodeCollector )
2014-02-18 03:35:11 -08:00
sigUsr1 := make ( chan os . Signal )
signal . Notify ( sigUsr1 , syscall . SIGUSR1 )
2014-12-17 09:59:06 -08:00
handler := prometheus . Handler ( )
if * authUser != "" || * authPass != "" {
if * authUser == "" || * authPass == "" {
2015-05-28 12:21:44 -07:00
log . Fatal ( "You need to specify -auth.user and -auth.pass to enable basic auth" )
2014-12-17 09:59:06 -08:00
}
handler = & basicAuthHandler {
handler : prometheus . Handler ( ) . ServeHTTP ,
user : * authUser ,
password : * authPass ,
}
}
2014-02-18 03:35:11 -08:00
2015-03-05 08:02:17 -08:00
http . Handle ( * metricsPath , handler )
http . HandleFunc ( "/" , func ( w http . ResponseWriter , r * http . Request ) {
w . Write ( [ ] byte ( ` < html >
< head > < title > Node Exporter < / title > < / head >
< body >
< h1 > Node Exporter < / h1 >
< p > < a href = "` + *metricsPath + `" > Metrics < / a > < / p >
< / body >
< / html > ` ) )
} )
2015-04-15 20:51:09 -07:00
2015-05-28 12:21:44 -07:00
log . Infof ( "Starting node_exporter v%s at %s" , Version , * listenAddress )
2015-03-05 08:02:17 -08:00
err = http . ListenAndServe ( * listenAddress , nil )
if err != nil {
2015-05-28 12:21:44 -07:00
log . Fatal ( err )
2015-03-05 08:02:17 -08:00
}
2014-02-18 03:35:11 -08:00
}