예제 #1
0
func main() {
	peers := &stringset{}
	var (
		showVersion = flag.Bool("version", false, "Print version information.")

		configFile = flag.String("config.file", "alertmanager.yml", "Alertmanager configuration file name.")
		dataDir    = flag.String("storage.path", "data/", "Base path for data storage.")
		retention  = flag.Duration("data.retention", 5*24*time.Hour, "How long to keep data for.")

		externalURL   = flag.String("web.external-url", "", "The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). Used for generating relative and absolute links back to Alertmanager itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Alertmanager. If omitted, relevant URL components will be derived automatically.")
		listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")

		meshListen = flag.String("mesh.listen-address", net.JoinHostPort("0.0.0.0", strconv.Itoa(mesh.Port)), "mesh listen address")
		hwaddr     = flag.String("mesh.hardware-address", mustHardwareAddr(), "MAC address, i.e. mesh peer ID")
		nickname   = flag.String("mesh.nickname", mustHostname(), "peer nickname")
	)
	flag.Var(peers, "mesh.peer", "initial peers (may be repeated)")
	flag.Parse()

	if len(flag.Args()) > 0 {
		log.Fatalln("Received unexpected and unparsed arguments: ", strings.Join(flag.Args(), ", "))
	}

	if *showVersion {
		fmt.Fprintln(os.Stdout, version.Print("alertmanager"))
		os.Exit(0)
	}

	log.Infoln("Starting alertmanager", version.Info())
	log.Infoln("Build context", version.BuildContext())

	err := os.MkdirAll(*dataDir, 0777)
	if err != nil {
		log.Fatal(err)
	}

	logger := log.NewLogger(os.Stderr)
	mrouter := initMesh(*meshListen, *hwaddr, *nickname)

	stopc := make(chan struct{})
	var wg sync.WaitGroup
	wg.Add(1)

	notificationLog, err := nflog.New(
		nflog.WithMesh(func(g mesh.Gossiper) mesh.Gossip {
			return mrouter.NewGossip("nflog", g)
		}),
		nflog.WithRetention(*retention),
		nflog.WithSnapshot(filepath.Join(*dataDir, "nflog")),
		nflog.WithMaintenance(15*time.Minute, stopc, wg.Done),
		nflog.WithLogger(logger.With("component", "nflog")),
	)
	if err != nil {
		log.Fatal(err)
	}

	marker := types.NewMarker()

	silences, err := silence.New(silence.Options{
		SnapshotFile: filepath.Join(*dataDir, "silences"),
		Retention:    *retention,
		Logger:       logger.With("component", "silences"),
		Gossip: func(g mesh.Gossiper) mesh.Gossip {
			return mrouter.NewGossip("silences", g)
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	// Start providers before router potentially sends updates.
	wg.Add(1)
	go func() {
		silences.Maintenance(15*time.Minute, filepath.Join(*dataDir, "silences"), stopc)
		wg.Done()
	}()

	mrouter.Start()

	defer func() {
		close(stopc)
		// Stop receiving updates from router before shutting down.
		mrouter.Stop()
		wg.Wait()
	}()

	mrouter.ConnectionMaker.InitiateConnections(peers.slice(), true)

	alerts, err := mem.NewAlerts(*dataDir)
	if err != nil {
		log.Fatal(err)
	}
	defer alerts.Close()

	var (
		inhibitor *inhibit.Inhibitor
		tmpl      *template.Template
		pipeline  notify.Stage
		disp      *dispatch.Dispatcher
	)
	defer disp.Stop()

	apiv := api.New(alerts, silences, func() dispatch.AlertOverview {
		return disp.Groups()
	})

	amURL, err := extURL(*listenAddress, *externalURL)
	if err != nil {
		log.Fatal(err)
	}

	waitFunc := meshWait(mrouter, 5*time.Second)
	timeoutFunc := func(d time.Duration) time.Duration {
		if d < notify.MinTimeout {
			d = notify.MinTimeout
		}
		return d + waitFunc()
	}

	reload := func() (err error) {
		log.With("file", *configFile).Infof("Loading configuration file")
		defer func() {
			if err != nil {
				log.With("file", *configFile).Errorf("Loading configuration file failed: %s", err)
				configSuccess.Set(0)
			} else {
				configSuccess.Set(1)
				configSuccessTime.Set(float64(time.Now().Unix()))
			}
		}()

		conf, err := config.LoadFile(*configFile)
		if err != nil {
			return err
		}

		apiv.Update(conf.String(), time.Duration(conf.Global.ResolveTimeout))

		tmpl, err = template.FromGlobs(conf.Templates...)
		if err != nil {
			return err
		}
		tmpl.ExternalURL = amURL

		inhibitor.Stop()
		disp.Stop()

		inhibitor = inhibit.NewInhibitor(alerts, conf.InhibitRules, marker)
		pipeline = notify.BuildPipeline(
			conf.Receivers,
			tmpl,
			waitFunc,
			inhibitor,
			silences,
			notificationLog,
			marker,
		)
		disp = dispatch.NewDispatcher(alerts, dispatch.NewRoute(conf.Route, nil), pipeline, marker, timeoutFunc)

		go disp.Run()
		go inhibitor.Run()

		return nil
	}

	if err := reload(); err != nil {
		os.Exit(1)
	}

	router := route.New()

	webReload := make(chan struct{})
	ui.Register(router.WithPrefix(amURL.Path), webReload)
	apiv.Register(router.WithPrefix(path.Join(amURL.Path, "/api")))

	log.Infoln("Listening on", *listenAddress)
	go listen(*listenAddress, router)

	var (
		hup      = make(chan os.Signal)
		hupReady = make(chan bool)
		term     = make(chan os.Signal)
	)
	signal.Notify(hup, syscall.SIGHUP)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-hupReady
		for {
			select {
			case <-hup:
			case <-webReload:
			}
			reload()
		}
	}()

	// Wait for reload or termination signals.
	close(hupReady) // Unblock SIGHUP handler.

	<-term

	log.Infoln("Received SIGTERM, exiting gracefully...")
}
예제 #2
0
func main() {
	flag.Parse()
	http.Handle(*metricsPath, prometheus.Handler())

	cfg, err := config.LoadFile(*configFile)
	if err != nil {
		log.Fatalln("Configuration file could not be read.", err)
	}

	c := newTailCollector(cfg)
	prometheus.MustRegister(c)

	// If args then start file/fifo collectors
	if len(flag.Args()) > 0 {
		for _, filename := range flag.Args() {
			go func(filename string) {
				var isPipe bool
				st, err := os.Stat(filename)
				if err == nil {
					if st.Mode()&os.ModeNamedPipe == os.ModeNamedPipe {
						isPipe = true
					}
				} else {
					isPipe = false
				}

				t, err := tail.TailFile(filename, tail.Config{
					Location: &tail.SeekInfo{0, os.SEEK_END},
					ReOpen:   true,
					Follow:   isPipe,
				})

				for line := range t.Lines {
					c.IngestLine(line.Text)
				}

			}(filename)
		}
	}

	// If collector address present, then start port collector.
	if *collectorAddress != "" {
		tcpSock, err := net.Listen("tcp", *collectorAddress)
		if err != nil {
			log.Fatalf("Error binding to TCP socket: %s", err)
		}
		go func() {
			for {
				conn, err := tcpSock.Accept()
				if err != nil {
					log.Errorf("Error accepting TCP connection: %s", err)
					continue
				}
				go func() {
					defer conn.Close()
					c.processReader(conn)
				}()
			}
		}()

		udpAddress, err := net.ResolveUDPAddr("udp", *collectorAddress)
		if err != nil {
			log.Fatalf("Error resolving UDP address: %s", err)
		}
		udpSock, err := net.ListenUDP("udp", udpAddress)
		if err != nil {
			log.Fatalf("Error listening to UDP address: %s", err)
		}
		go func() {
			defer udpSock.Close()
			for {
				buf := make([]byte, 65536)
				chars, srcAddress, err := udpSock.ReadFromUDP(buf)
				if err != nil {
					log.Errorf("Error reading UDP packet from %s: %s", srcAddress, err)
					continue
				}
				go c.processReader(bytes.NewReader(buf[0:chars]))
			}
		}()
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`<html>
      <head><title>Tail Exporter</title></head>
      <body>
      <h1>TCP/UDP Tail Exporter</h1>
      <p>Accepting raw lines over TCP and UDP on ` + *collectorAddress + `</p>
      <p>Watching files for lines:` + strings.Join(flag.Args(), ", ") + `</p>
      <p><a href="` + *metricsPath + `">Metrics</a></p>
      <h1>Config</h1>
      <pre>` +
			cfg.Original +
			`</pre>
      </body>
      </html>`))
	})

	log.Infof("Starting Server: %s", *listeningAddress)
	http.ListenAndServe(*listeningAddress, nil)
}
예제 #3
0
func main() {
	rand.Seed(time.Now().Unix())
	flag.Parse()

	// This is only used when we're running in -dev mode with bindata
	rootDir, _ = osext.ExecutableFolder()
	rootDir = path.Join(rootDir, "web")

	// Parse configuration
	cfg, err := config.LoadFromFile(*configFile)
	if err != nil {
		log.Fatalln("Error loading config", err)
	}

	// Templates
	amberTmpl, err := Asset("templates/index.amber")
	if err != nil {
		log.Fatalln("Could not load index template:", err)
	}
	tmpl := amber.MustCompile(string(amberTmpl), amber.Options{})

	// Setup the web UI
	router := httprouter.New()
	router.Handler("GET", *metricsPath, prometheus.Handler()) // Prometheus
	// Static asset handling
	router.GET("/static/*filepath", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		reqpath := ps.ByName("filepath")
		realpath := path.Join("static", reqpath)
		b, err := Asset(realpath)
		if err != nil {
			log.Debugln("Could not find asset: ", err)
			return
		} else {
			w.Write(b)
		}

	})

	var monitoredHosts []*pollers.Host

	router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		data := struct {
			Cfg   *config.Config
			Hosts *[]*pollers.Host
		}{
			Cfg:   cfg,
			Hosts: &monitoredHosts,
		}
		err := tmpl.Execute(w, &data)
		if err != nil {
			log.Errorln("Error rendering template", err)
		}
	})

	// Initialize the host pollers
	monitoredHosts = make([]*pollers.Host, len(cfg.Hosts))

	// We don't allow duplicate hosts, but also don't want to panic just due
	// to a typo, so keep track and skip duplicates here.
	seenHosts := make(map[string]bool)

	realidx := 0
	for _, hostCfg := range cfg.Hosts {
		log.Debugln("Setting up poller for: ", hostCfg.Hostname)
		if *skipPing {
			hostCfg.PingDisable = true
		}
		if _, ok := seenHosts[hostCfg.Hostname]; ok {
			log.Warnln("Discarding repeat configuration of same hostname", hostCfg.Hostname)
			continue
		}
		host := pollers.NewHost(hostCfg)
		monitoredHosts[realidx] = host
		prometheus.MustRegister(host)

		seenHosts[hostCfg.Hostname] = true
		realidx++
	}

	// Trim monitoredHosts to the number we actually used
	monitoredHosts = monitoredHosts[0:realidx]

	// This is the dispatcher. It is responsible for invoking the doPoll method
	// of hosts.
	connectionLimiter := pollers.NewLimiter(*maxConnections)
	hostQueue := make(chan *pollers.Host)

	// Start the host dispatcher
	go func() {
		for host := range hostQueue {
			go host.Poll(connectionLimiter, hostQueue)
		}
	}()

	// Do the initial host dispatch
	go func() {
		for _, host := range monitoredHosts {
			log.Debugln("Starting polling for hosts")
			hostQueue <- host
		}
	}()

	var handler http.Handler

	// If basic auth is requested, enable it for the interface.
	if cfg.BasicAuthUsername != "" && cfg.BasicAuthPassword != "" {
		basicauth := httpauth.SimpleBasicAuth(cfg.BasicAuthUsername,
			cfg.BasicAuthPassword)
		handler = basicauth(router)
	} else {
		handler = router
	}

	// If TLS certificates are specificed, use TLS
	if cfg.TLSCertificatePath != "" && cfg.TLSKeyPath != "" {
		log.Infof("Listening on (TLS-enabled) %s", *listenAddress)
		err = http.ListenAndServeTLS(*listenAddress,
			cfg.TLSCertificatePath, cfg.TLSKeyPath, handler)
	} else {
		log.Infof("Listening on %s", *listenAddress)
		err = http.ListenAndServe(*listenAddress, handler)
	}

	if err != nil {
		log.Fatal(err)
	}
}