@ -27,51 +27,118 @@ import (
"gopkg.in/alecthomas/kingpin.v2"
)
func init ( ) {
prometheus . MustRegister ( version . NewCollector ( "node_exporter" ) )
// handler wraps an unfiltered http.Handler but uses a filtered handler,
// created on the fly, if filtering is requested. Create instances with
// newHandler.
type handler struct {
unfilteredHandler http . Handler
// exporterMetricsRegistry is a separate registry for the metrics about
// the exporter itself.
exporterMetricsRegistry * prometheus . Registry
includeExporterMetrics bool
}
func handler ( w http . ResponseWriter , r * http . Request ) {
func newHandler ( includeExporterMetrics bool ) * handler {
h := & handler {
exporterMetricsRegistry : prometheus . NewRegistry ( ) ,
includeExporterMetrics : includeExporterMetrics ,
}
if h . includeExporterMetrics {
h . exporterMetricsRegistry . MustRegister (
prometheus . NewProcessCollector ( prometheus . ProcessCollectorOpts { } ) ,
prometheus . NewGoCollector ( ) ,
)
}
if innerHandler , err := h . innerHandler ( ) ; err != nil {
log . Fatalf ( "Couldn't create metrics handler: %s" , err )
} else {
h . unfilteredHandler = innerHandler
}
return h
}
// ServeHTTP implements http.Handler.
func ( h * handler ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
filters := r . URL . Query ( ) [ "collect[]" ]
log . Debugln ( "collect query:" , filters )
nc , err := collector . NewNodeCollector ( filters ... )
if len ( filters ) == 0 {
// No filters, use the prepared unfiltered handler.
h . unfilteredHandler . ServeHTTP ( w , r )
return
}
// To serve filtered metrics, we create a filtering handler on the fly.
filteredHandler , err := h . innerHandler ( filters ... )
if err != nil {
log . Warnln ( "Couldn't create" , err )
log . Warnln ( "Couldn't create filtered metrics handler: " , err )
w . WriteHeader ( http . StatusBadRequest )
w . Write ( [ ] byte ( fmt . Sprintf ( "Couldn't create %s" , err ) ) )
w . Write ( [ ] byte ( fmt . Sprintf ( "Couldn't create filtered metrics handler: %s" , err ) ) )
return
}
filteredHandler . ServeHTTP ( w , r )
}
registry := prometheus . NewRegistry ( )
err = registry . Register ( nc )
// innerHandler is used to create buth the one unfiltered http.Handler to be
// wrapped by the outer handler and also the filtered handlers created on the
// fly. The former is accomplished by calling innerHandler without any arguments
// (in which case it will log all the collectors enabled via command-line
// flags).
func ( h * handler ) innerHandler ( filters ... string ) ( http . Handler , error ) {
nc , err := collector . NewNodeCollector ( filters ... )
if err != nil {
log . Errorln ( "Couldn't register collector:" , err )
w . WriteHeader ( http . StatusInternalServerError )
w . Write ( [ ] byte ( fmt . Sprintf ( "Couldn't register collector: %s" , err ) ) )
return
return nil , fmt . Errorf ( "couldn't create collector: %s" , err )
}
// Only log the creation of an unfiltered handler, which should happen
// only once upon startup.
if len ( filters ) == 0 {
log . Infof ( "Enabled collectors:" )
collectors := [ ] string { }
for n := range nc . Collectors {
collectors = append ( collectors , n )
}
sort . Strings ( collectors )
for _ , n := range collectors {
log . Infof ( " - %s" , n )
}
}
gatherers := prometheus . Gatherers {
prometheus . DefaultGatherer ,
registry ,
r := prometheus . NewRegistry ( )
r . MustRegister ( version . NewCollector ( "node_exporter" ) )
if err := r . Register ( nc ) ; err != nil {
return nil , fmt . Errorf ( "couldn't register node collector: %s" , err )
}
// Delegate http serving to Prometheus client library, which will call collector.Collect.
h := promhttp . InstrumentMetricHandler (
registry ,
promhttp . HandlerFor ( gatherers ,
handler := promhttp . HandlerFor (
prometheus . Gatherers { h . exporterMetricsRegistry , r } ,
promhttp . HandlerOpts {
ErrorLog : log . NewErrorLogger ( ) ,
ErrorHandling : promhttp . ContinueOnError ,
} ) ,
} ,
)
if h . includeExporterMetrics {
// Note that we have to use h.exporterMetricsRegistry here to
// use the same promhttp metrics for all expositions.
handler = promhttp . InstrumentMetricHandler (
h . exporterMetricsRegistry , handler ,
)
h . ServeHTTP ( w , r )
}
return handler , nil
}
func main ( ) {
var (
listenAddress = kingpin . Flag ( "web.listen-address" , "Address on which to expose metrics and web interface." ) . Default ( ":9100" ) . String ( )
metricsPath = kingpin . Flag ( "web.telemetry-path" , "Path under which to expose metrics." ) . Default ( "/metrics" ) . String ( )
listenAddress = kingpin . Flag (
"web.listen-address" ,
"Address on which to expose metrics and web interface." ,
) . Default ( ":9100" ) . String ( )
metricsPath = kingpin . Flag (
"web.telemetry-path" ,
"Path under which to expose metrics." ,
) . Default ( "/metrics" ) . String ( )
disableExporterMetrics = kingpin . Flag (
"web.disable-exporter-metrics" ,
"Exclude metrics about the exporter itself (promhttp_*, process_*, go_*)." ,
) . Bool ( )
)
log . AddFlags ( kingpin . CommandLine )
@ -82,22 +149,7 @@ func main() {
log . Infoln ( "Starting node_exporter" , version . Info ( ) )
log . Infoln ( "Build context" , version . BuildContext ( ) )
// This instance is only used to check collector creation and logging.
nc , err := collector . NewNodeCollector ( )
if err != nil {
log . Fatalf ( "Couldn't create collector: %s" , err )
}
log . Infof ( "Enabled collectors:" )
collectors := [ ] string { }
for n := range nc . Collectors {
collectors = append ( collectors , n )
}
sort . Strings ( collectors )
for _ , n := range collectors {
log . Infof ( " - %s" , n )
}
http . HandleFunc ( * metricsPath , handler )
http . Handle ( * metricsPath , newHandler ( ! * disableExporterMetrics ) )
http . HandleFunc ( "/" , func ( w http . ResponseWriter , r * http . Request ) {
w . Write ( [ ] byte ( ` < html >
< head > < title > Node Exporter < / title > < / head >
@ -109,8 +161,7 @@ func main() {
} )
log . Infoln ( "Listening on" , * listenAddress )
err = http . ListenAndServe ( * listenAddress , nil )
if err != nil {
if err := http . ListenAndServe ( * listenAddress , nil ) ; err != nil {
log . Fatal ( err )
}
}