Log error and exit instead of panicking

Following the lead of prometheus/prometheus, we prefer to log-and-exit
instead of an unstructured panic.

cf. https://github.com/prometheus/prometheus/pull/3061/files#diff-4a3ccbb3ebdcd530af96f0105fe833c2R182

Signed-off-by: Matthias Rampke <mr@soundcloud.com>
This commit is contained in:
Matthias Rampke 2020-01-10 14:02:59 +00:00
parent 0d72309324
commit 53f732d480
No known key found for this signature in database
GPG key ID: 2CDE413A9BD0A5BC
2 changed files with 55 additions and 27 deletions

View file

@ -234,7 +234,8 @@ func (b *Exporter) handleEvent(event Event) {
}
default:
panic(fmt.Sprintf("unknown timer type '%s'", t))
level.Error(b.logger).Log("msg", "unknown timer type", "type", t)
os.Exit(1)
}
default:
@ -531,7 +532,8 @@ func (l *StatsDTCPListener) Listen() {
if strings.HasSuffix(err.Error(), "use of closed network connection") {
return
}
panic(fmt.Sprintf("AcceptTCP failed: %s", err))
level.Error(l.logger).Log("msg", "AcceptTCP failed", "error", err)
os.Exit(1)
}
go l.handleConn(c)
}

76
main.go
View file

@ -40,7 +40,7 @@ func init() {
prometheus.MustRegister(version.NewCollector("statsd_exporter"))
}
func serveHTTP(listenAddress, metricsEndpoint string) {
func serveHTTP(listenAddress, metricsEndpoint string, logger log.Logger) {
http.Handle(metricsEndpoint, promhttp.Handler())
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<html>
@ -51,13 +51,14 @@ func serveHTTP(listenAddress, metricsEndpoint string) {
</body>
</html>`))
})
panic(http.ListenAndServe(listenAddress, nil))
level.Error(logger).Log("msg", http.ListenAndServe(listenAddress, nil))
os.Exit(1)
}
func ipPortFromString(addr string) (*net.IPAddr, int) {
func ipPortFromString(addr string) (*net.IPAddr, int, error) {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
panic(fmt.Sprintf("Bad StatsD listening address: %s", addr))
return nil, 0, fmt.Errorf("bad StatsD listening address: %s", addr)
}
if host == "" {
@ -65,33 +66,39 @@ func ipPortFromString(addr string) (*net.IPAddr, int) {
}
ip, err := net.ResolveIPAddr("ip", host)
if err != nil {
panic(fmt.Sprintf("Unable to resolve %s: %s", host, err))
return nil, 0, fmt.Errorf("Unable to resolve %s: %s", host, err)
}
port, err := strconv.Atoi(portStr)
if err != nil || port < 0 || port > 65535 {
panic(fmt.Sprintf("Bad port %s: %s", portStr, err))
return nil, 0, fmt.Errorf("Bad port %s: %s", portStr, err)
}
return ip, port
return ip, port, nil
}
func udpAddrFromString(addr string) *net.UDPAddr {
ip, port := ipPortFromString(addr)
func udpAddrFromString(addr string) (*net.UDPAddr, error) {
ip, port, err := ipPortFromString(addr)
if err != nil {
return nil, err
}
return &net.UDPAddr{
IP: ip.IP,
Port: port,
Zone: ip.Zone,
}
}, nil
}
func tcpAddrFromString(addr string) *net.TCPAddr {
ip, port := ipPortFromString(addr)
func tcpAddrFromString(addr string) (*net.TCPAddr, error) {
ip, port, err := ipPortFromString(addr)
if err != nil {
return nil, err
}
return &net.TCPAddr{
IP: ip.IP,
Port: port,
Zone: ip.Zone,
}
}, nil
}
func configReloader(fileName string, mapper *mapper.MetricMapper, cacheSize int, logger log.Logger) {
@ -156,7 +163,8 @@ func main() {
logger := promlog.New(promlogConfig)
if *statsdListenUDP == "" && *statsdListenTCP == "" && *statsdListenUnixgram == "" {
panic("At least one of UDP/TCP/Unixgram listeners must be specified.")
level.Error(logger).Log("At least one of UDP/TCP/Unixgram listeners must be specified.")
os.Exit(1)
}
level.Info(logger).Log("msg", "Starting StatsD -> Prometheus Exporter", "version", version.Info())
@ -164,23 +172,29 @@ func main() {
level.Info(logger).Log("msg", "Accepting StatsD Traffic", "udp", *statsdListenUDP, "tcp", *statsdListenTCP, "unixgram", *statsdListenUnixgram)
level.Info(logger).Log("msg", "Accepting Prometheus Requests", "addr", *listenAddress)
go serveHTTP(*listenAddress, *metricsEndpoint)
go serveHTTP(*listenAddress, *metricsEndpoint, logger)
events := make(chan Events, *eventQueueSize)
defer close(events)
eventQueue := newEventQueue(events, *eventFlushThreshold, *eventFlushInterval)
if *statsdListenUDP != "" {
udpListenAddr := udpAddrFromString(*statsdListenUDP)
udpListenAddr, err := udpAddrFromString(*statsdListenUDP)
if err != nil {
level.Error(logger).Log("msg", "invalid UDP listen address", "address", *statsdListenUDP, "error", err)
os.Exit(1)
}
uconn, err := net.ListenUDP("udp", udpListenAddr)
if err != nil {
panic(err)
level.Error(logger).Log("msg", "failed to start UDP listener", "error", err)
os.Exit(1)
}
if *readBuffer != 0 {
err = uconn.SetReadBuffer(*readBuffer)
if err != nil {
panic(fmt.Sprintf("Error setting UDP read buffer: %s", err))
level.Error(logger).Log("msg", "error setting UDP read buffer", "error", err)
os.Exit(1)
}
}
@ -189,10 +203,15 @@ func main() {
}
if *statsdListenTCP != "" {
tcpListenAddr := tcpAddrFromString(*statsdListenTCP)
tcpListenAddr, err := tcpAddrFromString(*statsdListenTCP)
if err != nil {
level.Error(logger).Log("msg", "invalid TCP listen address", "address", *statsdListenUDP, "error", err)
os.Exit(1)
}
tconn, err := net.ListenTCP("tcp", tcpListenAddr)
if err != nil {
panic(err)
level.Error(logger).Log("msg", err)
os.Exit(1)
}
defer tconn.Close()
@ -203,14 +222,16 @@ func main() {
if *statsdListenUnixgram != "" {
var err error
if _, err = os.Stat(*statsdListenUnixgram); !os.IsNotExist(err) {
panic(fmt.Sprintf("Unixgram socket \"%s\" already exists", *statsdListenUnixgram))
level.Error(logger).Log("msg", "Unixgram socket already exists", "socket_name", *statsdListenUnixgram)
os.Exit(1)
}
uxgconn, err := net.ListenUnixgram("unixgram", &net.UnixAddr{
Net: "unixgram",
Name: *statsdListenUnixgram,
})
if err != nil {
panic(err)
level.Error(logger).Log("msg", "failed to listen on Unixgram socket", "error", err)
os.Exit(1)
}
defer uxgconn.Close()
@ -218,7 +239,8 @@ func main() {
if *readBuffer != 0 {
err = uxgconn.SetReadBuffer(*readBuffer)
if err != nil {
panic(fmt.Sprintf("Error setting Unixgram read buffer: %s", err))
level.Error(logger).Log("msg", "error setting Unixgram read buffer", "error", err)
os.Exit(1)
}
}
@ -248,12 +270,16 @@ func main() {
if *mappingConfig != "" {
err := mapper.InitFromFile(*mappingConfig, *cacheSize)
if err != nil {
panic(fmt.Sprintf("Error loading config: %s", err))
level.Error(logger).Log("msg", "error loading config", "error", err)
os.Exit(1)
}
if *dumpFSMPath != "" {
err := dumpFSM(mapper, *dumpFSMPath, logger)
if err != nil {
panic(fmt.Sprintf("Error dumping FSM: %s", err))
level.Error(logger).Log("msg", "error dumping FSM", "error", err)
// Failure to dump the FSM is an error (the user asked for it and it
// didn't happen) but not fatal (the exporter is fully functional
// afterwards).
}
}
} else {