diff --git a/cmd/poller/poller.go b/cmd/poller/poller.go index 783285554..19bc53178 100644 --- a/cmd/poller/poller.go +++ b/cmd/poller/poller.go @@ -97,43 +97,43 @@ type Poller struct { // Init starts Poller, reads parameters, opens zeroLog handler, initializes metadata, // starts collectors and exporters -func (me *Poller) Init() error { +func (p *Poller) Init() error { var err error // read options options.SetPathsAndHostname(&args) - me.options = &args - me.name = args.Poller + p.options = &args + p.name = args.Poller fileLoggingEnabled := false consoleLoggingEnabled := false - zeroLogLevel := logging.GetZerologLevel(me.options.LogLevel) + zeroLogLevel := logging.GetZerologLevel(p.options.LogLevel) // if we are daemon, use file logging - if me.options.Daemon { - logFileName = "poller_" + me.name + ".log" + if p.options.Daemon { + logFileName = "poller_" + p.name + ".log" fileLoggingEnabled = true } else { consoleLoggingEnabled = true } - if me.params, err = conf.GetPoller(me.options.Config, me.name); err != nil { + if p.params, err = conf.GetPoller(p.options.Config, p.name); err != nil { // separate logger is not yet configured as it depends on setting logMaxMegaBytes, logMaxFiles later // Using default instance of logger which logs below error to harvest.log - logging.SubLogger("Poller", me.name).Error().Stack().Err(err).Msg("read config") + logging.SubLogger("Poller", p.name).Error().Stack().Err(err).Msg("read config") return err } // log handling parameters // size of file before rotating - if s := me.params.GetChildContentS("log_max_bytes"); s != "" { + if s := p.params.GetChildContentS("log_max_bytes"); s != "" { if i, err := strconv.ParseInt(s, 10, 64); err == nil { logMaxMegaBytes = int(i / (1024 * 1024)) } } // maximum number of rotated files to keep - if s := me.params.GetChildContentS("log_max_files"); s != "" { + if s := p.params.GetChildContentS("log_max_files"); s != "" { if i, err := strconv.Atoi(s); err == nil { logMaxBackups = i } @@ -141,10 +141,10 @@ func (me *Poller) Init() error { logConfig := logging.LogConfig{ConsoleLoggingEnabled: consoleLoggingEnabled, PrefixKey: "Poller", - PrefixValue: me.name, + PrefixValue: p.name, LogLevel: zeroLogLevel, FileLoggingEnabled: fileLoggingEnabled, - Directory: me.options.LogPath, + Directory: p.options.LogPath, Filename: logFileName, MaxSize: logMaxMegaBytes, MaxBackups: logMaxBackups, @@ -152,11 +152,11 @@ func (me *Poller) Init() error { logger = logging.Configure(logConfig) logger.Info().Msgf("log level used: %s", zeroLogLevel.String()) - logger.Info().Msgf("options config: %s", me.options.Config) + logger.Info().Msgf("options config: %s", p.options.Config) // if profiling port > 0 start profiling service - if me.options.Profiling > 0 { - addr := fmt.Sprintf("localhost:%d", me.options.Profiling) + if p.options.Profiling > 0 { + addr := fmt.Sprintf("localhost:%d", p.options.Profiling) logger.Info().Msgf("profiling enabled on [%s]", addr) go func() { fmt.Println(http.ListenAndServe(addr, nil)) @@ -165,42 +165,42 @@ func (me *Poller) Init() error { // useful info for debugging logger.Debug().Msgf("* %s *s", version.String()) - logger.Debug().Msgf("options= %s", me.options.String()) + logger.Debug().Msgf("options= %s", p.options.String()) // set signal handler for graceful termination signalChannel := make(chan os.Signal, 1) signal.Notify(signalChannel, SIGNALS...) - go me.handleSignals(signalChannel) + go p.handleSignals(signalChannel) logger.Debug().Msgf("set signal handler for %v", SIGNALS) // announce startup - if me.options.Daemon { + if p.options.Daemon { logger.Info().Msgf("started as daemon [pid=%d]", os.Getpid()) } else { logger.Info().Msgf("started in foreground [pid=%d]", os.Getpid()) } // load parameters from config (harvest.yml) - logger.Debug().Msgf("importing config [%s]", me.options.Config) + logger.Debug().Msgf("importing config [%s]", p.options.Config) // each poller is associated with a remote host // if no address is specified, assume that is local host // @TODO: remove, redundant and error-prone - if me.target = me.params.GetChildContentS("addr"); me.target == "" { - me.target = "localhost" + if p.target = p.params.GetChildContentS("addr"); p.target == "" { + p.target = "localhost" } // check optional parameter auth_style // if certificates are missing use default paths - if me.params.GetChildContentS("auth_style") == "certificate_auth" { + if p.params.GetChildContentS("auth_style") == "certificate_auth" { filenames := [2]string{"ssl_cert", "ssl_key"} extensions := [2]string{".pem", ".key"} fp := "" for i := range filenames { - if fp = me.params.GetChildContentS(filenames[i]); fp == "" { + if fp = p.params.GetChildContentS(filenames[i]); fp == "" { // use default paths // example: /opt/harvest/cert/hostname.key, /opt/harvest/cert/hostname.pem - fp = path.Join(me.options.HomePath, "cert/", me.options.Hostname+extensions[i]) - me.params.SetChildContentS(filenames[i], fp) + fp = path.Join(p.options.HomePath, "cert/", p.options.Hostname+extensions[i]) + p.params.SetChildContentS(filenames[i], fp) logger.Debug().Msgf("using default [%s] path: [%s]", filenames[i], fp) } if _, err = os.Stat(fp); err != nil { @@ -212,9 +212,9 @@ func (me *Poller) Init() error { // initialize our metadata, the metadata will host status of our // collectors and exporters, as well as ping stats to target host - me.loadMetadata() + p.loadMetadata() - if me.exporterParams, err = conf.GetExporters(me.options.Config); err != nil { + if p.exporterParams, err = conf.GetExporters(p.options.Config); err != nil { logger.Warn().Msgf("read exporter params: %v", err) // @TODO just warn or abort? } @@ -222,16 +222,16 @@ func (me *Poller) Init() error { // iterate over list of collectors and initialize them // exporters are initialized on the fly, if at least one collector // has requested them - if collectors := me.params.GetChildS("collectors"); collectors == nil { + if collectors := p.params.GetChildS("collectors"); collectors == nil { logger.Warn().Msg("no collectors defined for this poller in config") return errors.New(errors.ERR_NO_COLLECTOR, "no collectors") } else { for _, c := range collectors.GetAllChildContentS() { ok := true // if requested, filter collectors - if len(me.options.Collectors) != 0 { + if len(p.options.Collectors) != 0 { ok = false - for _, x := range me.options.Collectors { + for _, x := range p.options.Collectors { if x == c { ok = true break @@ -243,36 +243,36 @@ func (me *Poller) Init() error { continue } - if err = me.loadCollector(c, ""); err != nil { + if err = p.loadCollector(c, ""); err != nil { logger.Error().Stack().Err(err).Msgf("load collector (%s):", c) } } } // at least one collector should successfully initialize - if len(me.collectors) == 0 { + if len(p.collectors) == 0 { logger.Warn().Msg("no collectors initialized, stopping") return errors.New(errors.ERR_NO_COLLECTOR, "no collectors") } - logger.Debug().Msgf("initialized %d collectors", len(me.collectors)) + logger.Debug().Msgf("initialized %d collectors", len(p.collectors)) // we are more tolerable against exporters, since we might only // want to debug collectors without actually exporting - if len(me.exporters) == 0 { + if len(p.exporters) == 0 { logger.Warn().Msg("no exporters initialized, continuing without exporters") } else { - logger.Debug().Msgf("initialized %d exporters", len(me.exporters)) + logger.Debug().Msgf("initialized %d exporters", len(p.exporters)) } // initialize a schedule for the poller, this is the interval at which // we will check the status of collectors, exporters and target system, // and send metadata to exporters - if s := me.params.GetChildContentS("poller_schedule"); s != "" { + if s := p.params.GetChildContentS("poller_schedule"); s != "" { pollerSchedule = s } - me.schedule = schedule.New() - if err = me.schedule.NewTaskString("poller", pollerSchedule, nil); err != nil { + p.schedule = schedule.New() + if err = p.schedule.NewTaskString("poller", pollerSchedule, nil); err != nil { logger.Error().Stack().Err(err).Msg("set schedule:") return err } @@ -288,7 +288,7 @@ func (me *Poller) Init() error { // Start will run the collectors and the poller itself // in separate goroutines, leaving the main goroutine // to the exporters -func (me *Poller) Start() { +func (p *Poller) Start() { var ( wg sync.WaitGroup @@ -296,29 +296,29 @@ func (me *Poller) Start() { ) // start collectors - for _, col = range me.collectors { + for _, col = range p.collectors { logger.Debug().Msgf("launching collector (%s:%s)", col.GetName(), col.GetObject()) wg.Add(1) go col.Start(&wg) } // run concurrently and update metadata - go me.Run() + go p.Run() wg.Wait() // ...until there are no collectors running anymore logger.Info().Msg("no active collectors -- terminating") - me.Stop() + p.Stop() } // Run will periodically check the status of collectors/exporters, // report metadata and do some housekeeping -func (me *Poller) Run() { +func (p *Poller) Run() { // poller schedule has just one task - task := me.schedule.GetTask("poller") + task := p.schedule.GetTask("poller") // number of collectors/exporters that are still up upCollectors := 0 @@ -331,26 +331,26 @@ func (me *Poller) Run() { task.Start() // flush metadata - me.status.Reset() - me.metadata.Reset() + p.status.Reset() + p.metadata.Reset() // ping target system - if ping, ok := me.ping(); ok { - me.status.LazySetValueUint8("status", "host", 0) - me.status.LazySetValueFloat32("ping", "host", ping) + if ping, ok := p.ping(); ok { + p.status.LazySetValueUint8("status", "host", 0) + p.status.LazySetValueFloat32("ping", "host", ping) } else { - me.status.LazySetValueUint8("status", "host", 1) + p.status.LazySetValueUint8("status", "host", 1) } // add number of goroutines to metadata // @TODO: cleanup, does not belong to "status" - me.status.LazySetValueInt("goroutines", "host", runtime.NumGoroutine()) + p.status.LazySetValueInt("goroutines", "host", runtime.NumGoroutine()) upc := 0 // up collectors upe := 0 // up exporters // update status of collectors - for _, c := range me.collectors { + for _, c := range p.collectors { code, status, msg := c.GetStatus() logger.Debug().Msgf("collector (%s:%s) status: (%d - %s) %s", c.GetName(), c.GetObject(), code, status, msg) @@ -360,18 +360,18 @@ func (me *Poller) Run() { key := c.GetName() + "." + c.GetObject() - me.metadata.LazySetValueUint64("count", key, c.GetCollectCount()) - me.metadata.LazySetValueUint8("status", key, code) + p.metadata.LazySetValueUint64("count", key, c.GetCollectCount()) + p.metadata.LazySetValueUint8("status", key, code) if msg != "" { - if instance := me.metadata.GetInstance(key); instance != nil { + if instance := p.metadata.GetInstance(key); instance != nil { instance.SetLabel("reason", msg) } } } // update status of exporters - for _, e := range me.exporters { + for _, e := range p.exporters { code, status, msg := e.GetStatus() logger.Debug().Msgf("exporter (%s) status: (%d - %s) %s", e.GetName(), code, status, msg) @@ -381,58 +381,58 @@ func (me *Poller) Run() { key := e.GetClass() + "." + e.GetName() - me.metadata.LazySetValueUint64("count", key, e.GetExportCount()) - me.metadata.LazySetValueUint8("status", key, code) + p.metadata.LazySetValueUint64("count", key, e.GetExportCount()) + p.metadata.LazySetValueUint8("status", key, code) if msg != "" { - if instance := me.metadata.GetInstance(key); instance != nil { + if instance := p.metadata.GetInstance(key); instance != nil { instance.SetLabel("reason", msg) } } } // @TODO if there are no "master" exporters, don't collect metadata - for _, e := range me.exporters { - if err := e.Export(me.metadata); err != nil { + for _, e := range p.exporters { + if err := e.Export(p.metadata); err != nil { logger.Error().Stack().Err(err).Msg("export component metadata:") } - if err := e.Export(me.status); err != nil { + if err := e.Export(p.status); err != nil { logger.Error().Stack().Err(err).Msg("export target metadata:") } } // only zeroLog when numbers have changes, since hopefully that happens rarely if upc != upCollectors || upe != upExporters { - logger.Info().Msgf("updated status, up collectors: %d (of %d), up exporters: %d (of %d)", upc, len(me.collectors), upe, len(me.exporters)) + logger.Info().Msgf("updated status, up collectors: %d (of %d), up exporters: %d (of %d)", upc, len(p.collectors), upe, len(p.exporters)) } upCollectors = upc upExporters = upe } - me.schedule.Sleep() + p.schedule.Sleep() } } // Stop gracefully exits the program by closing zeroLog -func (me *Poller) Stop() { +func (p *Poller) Stop() { logger.Info().Msgf("cleaning up and stopping [pid=%d]", os.Getpid()) } // set up signal disposition -func (me *Poller) handleSignals(signalChannel chan os.Signal) { +func (p *Poller) handleSignals(signalChannel chan os.Signal) { for { sig := <-signalChannel logger.Info().Msgf("caught signal [%s]", sig) - me.Stop() + p.Stop() os.Exit(0) } } // ping target system, report if it's available or not // and if available, response time -func (me *Poller) ping() (float32, bool) { +func (p *Poller) ping() (float32, bool) { - cmd := exec.Command("ping", me.target, "-w", "5", "-c", "1", "-q") + cmd := exec.Command("ping", p.target, "-w", "5", "-c", "1", "-q") if out, err := cmd.Output(); err == nil { if x := strings.Split(string(out), "mdev = "); len(x) > 1 { @@ -449,7 +449,7 @@ func (me *Poller) ping() (float32, bool) { // dynamically load and initialize a collector // if there are more than one objects defined for a collector, // then multiple collectors will be initialized -func (me *Poller) loadCollector(class, object string) error { +func (p *Poller) loadCollector(class, object string) error { var ( err error @@ -469,18 +469,18 @@ func (me *Poller) loadCollector(class, object string) error { // load the template file(s) of the collector where we expect to find // object name or list of objects - if template, err = collector.ImportTemplate(me.options.HomePath, "default.yaml", class); err != nil { + if template, err = collector.ImportTemplate(p.options.HomePath, "default.yaml", class); err != nil { return err } else if template == nil { // probably redundant return errors.New(errors.MISSING_PARAM, "collector template") } - if custom, err = collector.ImportTemplate(me.options.HomePath, "custom.yaml", class); err == nil && custom != nil { + if custom, err = collector.ImportTemplate(p.options.HomePath, "custom.yaml", class); err == nil && custom != nil { template.Merge(custom) logger.Debug().Msg("merged custom and default templates") } // add Poller's parameters to the collector parameters - template.Union(me.params) + template.Union(p.params) // if we don't know object, try load from template if object == "" { @@ -489,7 +489,7 @@ func (me *Poller) loadCollector(class, object string) error { // if object is defined, we only initialize 1 sub-collector / object if object != "" { - col, err = me.newCollector(class, object, template) + col, err = p.newCollector(class, object, template) if col != nil { if err != nil { logger.Error().Msgf("init collector (%s:%s): %v", class, object, err) @@ -505,9 +505,9 @@ func (me *Poller) loadCollector(class, object string) error { ok := true // if requested filter objects - if len(me.options.Objects) != 0 { + if len(p.options.Objects) != 0 { ok = false - for _, o := range me.options.Objects { + for _, o := range p.options.Objects { if o == object.GetNameS() { ok = true break @@ -520,7 +520,7 @@ func (me *Poller) loadCollector(class, object string) error { continue } - col, err = me.newCollector(class, object.GetNameS(), template) + col, err = p.newCollector(class, object.GetNameS(), template) if col == nil { logger.Warn().Msgf("collector is nil for collector-object (%s:%s)", class, object.GetNameS()) continue @@ -540,7 +540,7 @@ func (me *Poller) loadCollector(class, object string) error { return errors.New(errors.MISSING_PARAM, "collector object") } - me.collectors = append(me.collectors, collectors...) + p.collectors = append(p.collectors, collectors...) logger.Debug().Msgf("initialized (%s) with %d objects", class, len(collectors)) // link each collector with requested exporter & update metadata for _, col = range collectors { @@ -551,9 +551,9 @@ func (me *Poller) loadCollector(class, object string) error { name := col.GetName() obj := col.GetObject() - for _, expName := range col.WantedExporters(me.options.Config) { + for _, expName := range col.WantedExporters(p.options.Config) { logger.Trace().Msgf("expName %s", expName) - if exp := me.loadExporter(expName); exp != nil { + if exp := p.loadExporter(expName); exp != nil { col.LinkExporter(exp) logger.Debug().Msgf("linked (%s:%s) to exporter (%s)", name, obj, expName) } else { @@ -563,7 +563,7 @@ func (me *Poller) loadCollector(class, object string) error { // update metadata - if instance, err := me.metadata.NewInstance(name + "." + obj); err != nil { + if instance, err := p.metadata.NewInstance(name + "." + obj); err != nil { return err } else { instance.SetLabel("type", "collector") @@ -575,7 +575,7 @@ func (me *Poller) loadCollector(class, object string) error { return nil } -func (me *Poller) newCollector(class string, object string, template *node.Node) (collector.Collector, error) { +func (p *Poller) newCollector(class string, object string, template *node.Node) (collector.Collector, error) { name := "harvest.collector." + strings.ToLower(class) mod, err := plugin.GetModule(name) if err != nil { @@ -588,14 +588,14 @@ func (me *Poller) newCollector(class string, object string, template *node.Node) logger.Error().Msgf("collector '%s' is not a Collector", name) return nil, errors.New(errors.ERR_NO_COLLECTOR, "no collectors") } - delegate := collector.New(class, object, me.options, template.Copy()) + delegate := collector.New(class, object, p.options, template.Copy()) err = col.Init(delegate) return col, err } // returns exporter that matches to name, if exporter is not loaded // tries to load and return -func (me *Poller) loadExporter(name string) exporter.Exporter { +func (p *Poller) loadExporter(name string) exporter.Exporter { var ( err error @@ -605,11 +605,11 @@ func (me *Poller) loadExporter(name string) exporter.Exporter { ) // stop here if exporter is already loaded - if exp = me.getExporter(name); exp != nil { + if exp = p.getExporter(name); exp != nil { return exp } - if params = me.exporterParams.GetChildS(name); params == nil { + if params = p.exporterParams.GetChildS(name); params == nil { logger.Warn().Msgf("exporter (%s) not defined in config", name) return nil } @@ -619,7 +619,7 @@ func (me *Poller) loadExporter(name string) exporter.Exporter { return nil } - absExp := exporter.New(class, name, me.options, params) + absExp := exporter.New(class, name, p.options, params) switch class { case "Prometheus": exp = prometheus.New(absExp) @@ -634,11 +634,11 @@ func (me *Poller) loadExporter(name string) exporter.Exporter { return nil } - me.exporters = append(me.exporters, exp) + p.exporters = append(p.exporters, exp) logger.Debug().Msgf("initialized exporter (%s)", name) // update metadata - if instance, err := me.metadata.NewInstance(exp.GetClass() + "." + exp.GetName()); err != nil { + if instance, err := p.metadata.NewInstance(exp.GetClass() + "." + exp.GetName()); err != nil { logger.Error().Msgf("add metadata instance: %v", err) } else { instance.SetLabel("type", "exporter") @@ -649,8 +649,8 @@ func (me *Poller) loadExporter(name string) exporter.Exporter { } -func (me *Poller) getExporter(name string) exporter.Exporter { - for _, exp := range me.exporters { +func (p *Poller) getExporter(name string) exporter.Exporter { + for _, exp := range p.exporters { if exp.GetName() == name { return exp } @@ -659,28 +659,28 @@ func (me *Poller) getExporter(name string) exporter.Exporter { } // initialize matrices to be used as metadata -func (me *Poller) loadMetadata() { +func (p *Poller) loadMetadata() { - me.metadata = matrix.New("poller", "metadata_component") - me.metadata.NewMetricUint8("status") - me.metadata.NewMetricUint64("count") - me.metadata.SetGlobalLabel("poller", me.name) - me.metadata.SetGlobalLabel("version", me.options.Version) - me.metadata.SetGlobalLabel("hostname", me.options.Hostname) - me.metadata.SetExportOptions(matrix.DefaultExportOptions()) + p.metadata = matrix.New("poller", "metadata_component") + p.metadata.NewMetricUint8("status") + p.metadata.NewMetricUint64("count") + p.metadata.SetGlobalLabel("poller", p.name) + p.metadata.SetGlobalLabel("version", p.options.Version) + p.metadata.SetGlobalLabel("hostname", p.options.Hostname) + p.metadata.SetExportOptions(matrix.DefaultExportOptions()) // metadata for target system - me.status = matrix.New("poller", "metadata_target") - me.status.NewMetricUint8("status") - me.status.NewMetricFloat32("ping") - me.status.NewMetricUint32("goroutines") - - instance, _ := me.status.NewInstance("host") - instance.SetLabel("addr", me.target) - me.status.SetGlobalLabel("poller", me.name) - me.status.SetGlobalLabel("version", me.options.Version) - me.status.SetGlobalLabel("hostname", me.options.Hostname) - me.status.SetExportOptions(matrix.DefaultExportOptions()) + p.status = matrix.New("poller", "metadata_target") + p.status.NewMetricUint8("status") + p.status.NewMetricFloat32("ping") + p.status.NewMetricUint32("goroutines") + + instance, _ := p.status.NewInstance("host") + instance.SetLabel("addr", p.target) + p.status.SetGlobalLabel("poller", p.name) + p.status.SetGlobalLabel("version", p.options.Version) + p.status.SetGlobalLabel("hostname", p.options.Hostname) + p.status.SetExportOptions(matrix.DefaultExportOptions()) } var pollerCmd = &cobra.Command{