Repository: NetApp/harvest
Pull request #79 (2021-05-28 02:01:22)

Handle special characters in passwords
This change only addresses passwords in Pollers and Defaults. The bigger refactor is to use HarvestConfig through out the codebase, but that was too big a change at the moment. That change touches a lot more code.

When that change is made, the code in conf.LoadConfig can be removed.

vgratian: I have a slight concern with the Poller and Exporter structs. (Sorry, this actually refers back to PR #70 which I  wasn't able to review): hardcoding poller and exporter parameters there, goes against a core architectural feature of Havest: isolation of collectors/exporters from the core modules. This is not a problem right now, since - if I understand correctly - collectors/exporters still get the untyped tree structure as their parameters, but might be a breaching the isolation principle in the future. 
cgrinds: Comment added

Patch:
diff --git a/Makefile b/Makefile
index d9a3c60b..6237ae91 100644
--- a/Makefile
+++ b/Makefile
@@ -150,7 +150,7 @@ dist-tar:
 	@mkdir ${TMP}
 	@mkdir ${DIST}
 	@cp -a bin conf docs grafana README.md LICENSE ${TMP}
-	@cp -a harvest.yml ${TMP}/harvest.yml
+	@cp -a harvest.example.yml ${TMP}/harvest.yml
 	@tar --directory /tmp --create --gzip --file ${DIST}/${HARVEST_PACKAGE}.tar.gz ${HARVEST_PACKAGE}
 	@rm -rf ${TMP}
 	@echo "tar artifact @" ${DIST}/${HARVEST_PACKAGE}.tar.gz
diff --git a/cmd/collectors/unix/main.go b/cmd/collectors/unix/main.go
index 7bb32175..f72af59c 100644
--- a/cmd/collectors/unix/main.go
+++ b/cmd/collectors/unix/main.go
@@ -7,7 +7,7 @@ import (
 	"goharvest2/cmd/poller/collector"
 	"goharvest2/pkg/conf"
 	"goharvest2/pkg/errors"
-	"goharvest2/pkg/logging"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/set"
 	"goharvest2/pkg/tree/node"
@@ -158,7 +158,7 @@ func (me *Unix) Init() error {
 	// load list of counters from template
 	if counters := me.Params.GetChildS("counters"); counters != nil {
 		if err = me.loadMetrics(counters); err != nil {
-			me.Logger.Error().Stack().Err(err).Msg("load metrics")
+			logger.Error(me.Prefix, "load metrics: %v", err)
 			return err
 		}
 	} else {
@@ -167,14 +167,14 @@ func (me *Unix) Init() error {
 
 	getClockTicks()
 	if me.system, err = NewSystem(); err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("load system")
+		logger.Error(me.Prefix, "load system: %v", err)
 		return err
 	}
 
 	me.Matrix.SetGlobalLabel("hostname", me.Options.Hostname)
 	me.Matrix.SetGlobalLabel("datacenter", me.Params.GetChildContentS("datacenter"))
 
-	me.Logger.Debug().Msg("initialized")
+	logger.Debug(me.Prefix, "initialized")
 	return nil
 }
 
@@ -186,7 +186,7 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 		err            error
 	)
 
-	me.Logger.Debug().Msg("initializing metric cache")
+	logger.Debug(me.Prefix, "initializing metric cache")
 
 	me.processes = make(map[string]*Process)
 	me.histogramLabels = make(map[string][]string)
@@ -207,7 +207,7 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 
 		dtype := _DTYPES[name]
 
-		me.Logger.Trace().Msgf("handling (%s) (%s) dtype=%s", name, display, dtype)
+		logger.Trace(me.Prefix, "handling (%s) (%s) dtype=%s", name, display, dtype)
 
 		// counter is scalar metric
 		if _, has := _METRICS[name]; has {
@@ -216,7 +216,7 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 				return err
 			}
 			metric.SetName(display)
-			me.Logger.Debug().Msgf("(%s) added metric (%s)", name, display)
+			logger.Debug(me.Prefix, "(%s) added metric (%s)", name, display)
 
 			// counter is histogram
 		} else if _, has := _HISTOGRAMS[name]; has {
@@ -240,7 +240,7 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 				label, ldisplay := parseMetricName(w)
 
 				if !labels.Has(label) {
-					me.Logger.Warn().Msgf("invalid histogram metric [%s]", label)
+					logger.Warn(me.Prefix, "invalid histogram metric [%s]", label)
 					wanted.Delete(w)
 					continue
 				}
@@ -253,11 +253,11 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 				me.histogramLabels[name] = append(me.histogramLabels[name], label)
 			}
 
-			me.Logger.Debug().Msgf("(%s) added histogram (%s) with %d submetrics", name, display, len(me.histogramLabels[name]))
+			logger.Debug(me.Prefix, "(%s) added histogram (%s) with %d submetrics", name, display, len(me.histogramLabels[name]))
 
 			// invalid counter
 		} else {
-			me.Logger.Warn().Msgf("(%s) skipped unknown metric", name)
+			logger.Warn(me.Prefix, "(%s) skipped unknown metric", name)
 		}
 	}
 
@@ -265,7 +265,7 @@ func (me *Unix) loadMetrics(counters *node.Node) error {
 		return err
 	}
 
-	me.Logger.Debug().Msgf("initialized cache with %d metrics", len(me.Matrix.GetMetrics()))
+	logger.Debug(me.Prefix, "initialized cache with %d metrics", len(me.Matrix.GetMetrics()))
 	return nil
 }
 
@@ -296,23 +296,23 @@ func (me *Unix) PollInstance() (*matrix.Matrix, error) {
 			}
 			instance.SetLabel("poller", name)
 			instance.SetLabel("pid", pid)
-			me.Logger.Debug().Msgf("add instance (%s) with PID (%s)", name, pid)
+			logger.Debug(me.Prefix, "add instance (%s) with PID (%s)", name, pid)
 		} else {
 			currInstances.Delete(name)
 			instance.SetLabel("pid", pid)
-			me.Logger.Debug().Msgf("update instance (%s) with PID (%s)", name, pid)
+			logger.Debug(me.Prefix, "update instance (%s) with PID (%s)", name, pid)
 		}
 	}
 
 	for name := range currInstances.Iter() {
 		me.Matrix.RemoveInstance(name)
-		me.Logger.Debug().Msgf("remove instance (%s)", name)
+		logger.Debug(me.Prefix, "remove instance (%s)", name)
 	}
 
 	t := len(me.Matrix.GetInstances())
 	r := currInstances.Size()
 	a := t - (currSize - r)
-	me.Logger.Debug().Msgf("added %d, removed %d, total instances %d", a, r, t)
+	logger.Debug(me.Prefix, "added %d, removed %d, total instances %d", a, r, t)
 
 	return nil, nil
 }
@@ -339,7 +339,7 @@ func (me *Unix) PollData() (*matrix.Matrix, error) {
 		// assume not running
 		err = me.Matrix.LazySetValueUint8("status", key, 1)
 		if err != nil {
-			me.Logger.Error().Stack().Err(err).Msgf("error while parsing metric key [%s]", key)
+			logger.Error(me.Prefix, "error while parsing metric key [%s], %v", key, err)
 		}
 
 		if proc, ok = me.processes[key]; ok {
@@ -351,15 +351,15 @@ func (me *Unix) PollData() (*matrix.Matrix, error) {
 
 		if proc == nil {
 			if instance.GetLabel("pid") == "" {
-				me.Logger.Debug().Msgf("skip instance [%s]: not running", key)
+				logger.Debug(me.Prefix, "skip instance [%s]: not running", key)
 				continue
 			}
 			if pid, err = strconv.Atoi(instance.GetLabel("pid")); err != nil {
-				me.Logger.Warn().Msgf("skip instance [%s], invalid PID: %v", key, err)
+				logger.Warn(me.Prefix, "skip instance [%s], invalid PID: %v", key, err)
 				continue
 			}
 			if proc, err = NewProcess(pid); err != nil {
-				me.Logger.Warn().Msgf("skip instance [%s], process: %v", key, err)
+				logger.Warn(me.Prefix, "skip instance [%s], process: %v", key, err)
 				continue
 			}
 			me.processes[key] = proc
@@ -369,17 +369,17 @@ func (me *Unix) PollData() (*matrix.Matrix, error) {
 		cmd := proc.Cmdline()
 
 		if !set.NewFrom(strings.Fields(cmd)).Has(poller) {
-			me.Logger.Debug().Msgf("skip instance [%s]: PID (%d) not matched with [%s]", key, pid, cmd)
+			logger.Debug(me.Prefix, "skip instance [%s]: PID (%d) not matched with [%s]", key, pid, cmd)
 			continue
 		}
 
 		// if we got here poller is running
 		err = me.Matrix.LazySetValueUint32("status", key, 0)
 		if err != nil {
-			me.Logger.Error().Stack().Err(err).Msgf("error while parsing metric key [%s]", key)
+			logger.Error(me.Prefix, "error while parsing metric key [%s], %v", key, err)
 		}
 
-		me.Logger.Debug().Msgf("populating instance [%s]: PID (%d) with [%s]\n", key, pid, cmd)
+		logger.Debug(me.Prefix, "populating instance [%s]: PID (%d) with [%s]\n", key, pid, cmd)
 
 		// process scalar metrics
 		for key, foo := range _METRICS {
@@ -405,35 +405,35 @@ func (me *Unix) PollData() (*matrix.Matrix, error) {
 	}
 
 	me.AddCollectCount(count)
-	me.Logger.Debug().Msgf("poll complete, added %d data points", count)
+	logger.Debug(me.Prefix, "poll complete, added %d data points", count)
 	return me.Matrix, nil
 }
 
 func setStartTime(m matrix.Metric, i *matrix.Instance, p *Process, s *System) {
 	err := m.SetValueFloat64(i, p.startTime+s.bootTime)
 	if err != nil {
-		logging.Get().Error().Stack().Err(err).Msg("error")
+		logger.Error("", "error %v", err)
 	}
 }
 
 func setNumThreads(m matrix.Metric, i *matrix.Instance, p *Process, s *System) {
 	err := m.SetValueUint64(i, p.numThreads)
 	if err != nil {
-		logging.Get().Error().Stack().Err(err).Msg("error")
+		logger.Error("", "error %v", err)
 	}
 }
 
 func setNumFds(m matrix.Metric, i *matrix.Instance, p *Process, s *System) {
 	err := m.SetValueUint64(i, p.numFds)
 	if err != nil {
-		logging.Get().Error().Stack().Err(err).Msg("error")
+		logger.Error("", "error %v", err)
 	}
 }
 
 func setMemoryPercent(m matrix.Metric, i *matrix.Instance, p *Process, s *System) {
 	err := m.SetValueFloat64(i, float64(p.mem["rss"])/float64(s.memTotal)*100)
 	if err != nil {
-		logging.Get().Error().Stack().Err(err).Msg("error")
+		logger.Error("", "error %v", err)
 	}
 }
 
@@ -441,12 +441,12 @@ func setCpuPercent(m matrix.Metric, i *matrix.Instance, p *Process, s *System) {
 	if p.elapsedTime != 0 {
 		err := m.SetValueFloat64(i, p.cpuTotal/p.elapsedTime*100)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	} else {
 		err := m.SetValueFloat64(i, p.cpuTotal/(float64(time.Now().Unix())-p.startTime)*100)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
@@ -455,7 +455,7 @@ func setCpu(m matrix.Metric, l string, i *matrix.Instance, p *Process) {
 	if value, ok := p.cpu[l]; ok {
 		err := m.SetValueFloat64(i, value)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
@@ -464,7 +464,7 @@ func setMemory(m matrix.Metric, l string, i *matrix.Instance, p *Process) {
 	if value, ok := p.mem[l]; ok {
 		err := m.SetValueUint64(i, value)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
@@ -473,7 +473,7 @@ func setIo(m matrix.Metric, l string, i *matrix.Instance, p *Process) {
 	if value, ok := p.io[l]; ok {
 		err := m.SetValueUint64(i, value)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
@@ -482,7 +482,7 @@ func setNet(m matrix.Metric, l string, i *matrix.Instance, p *Process) {
 	if value, ok := p.net[l]; ok {
 		err := m.SetValueUint64(i, value)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
@@ -491,7 +491,7 @@ func setCtx(m matrix.Metric, l string, i *matrix.Instance, p *Process) {
 	if value, ok := p.ctx[l]; ok {
 		err := m.SetValueUint64(i, value)
 		if err != nil {
-			logging.Get().Error().Stack().Err(err).Msg("error")
+			logger.Error("", "error %v", err)
 		}
 	}
 }
diff --git a/cmd/collectors/zapi/collector/parsers.go b/cmd/collectors/zapi/collector/parsers.go
index 5ab95fd7..82d7540d 100644
--- a/cmd/collectors/zapi/collector/parsers.go
+++ b/cmd/collectors/zapi/collector/parsers.go
@@ -5,6 +5,7 @@ package zapi
 
 import (
 	"goharvest2/pkg/color"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"goharvest2/pkg/util"
@@ -115,20 +116,20 @@ func (me *Zapi) HandleCounter(path []string, content string) string {
 	if content[0] == '^' {
 		me.instanceLabelPaths[key] = display
 		//data.AddLabel(key, display)
-		me.Logger.Trace().Msgf("%sadd (%s) as label [%s]%s => %v", color.Yellow, key, display, color.End, fullPath)
+		logger.Trace(me.Prefix, "%sadd (%s) as label [%s]%s => %v", color.Yellow, key, display, color.End, fullPath)
 		if content[1] == '^' {
 			copied := make([]string, len(fullPath))
 			copy(copied, fullPath)
 			me.instanceKeyPaths = append(me.instanceKeyPaths, copied)
-			me.Logger.Trace().Msgf("%sadd (%s) as instance key [%s]%s => %v", color.Red, key, display, color.End, fullPath)
+			logger.Trace(me.Prefix, "%sadd (%s) as instance key [%s]%s => %v", color.Red, key, display, color.End, fullPath)
 		}
 	} else {
 		metric, err := me.Matrix.NewMetricUint64(key)
 		if err != nil {
-			me.Logger.Error().Stack().Err(err).Msgf("add as metric (%s) [%s]: %v", key, display)
+			logger.Error(me.Prefix, "add ass metric (%s) [%s]: %v", key, display, err)
 		} else {
 			metric.SetName(display)
-			me.Logger.Trace().Msgf("%sadd as metric (%s) [%s]%s => %v", color.Blue, key, display, color.End, fullPath)
+			logger.Trace(me.Prefix, "%sadd as metric (%s) [%s]%s => %v", color.Blue, key, display, color.End, fullPath)
 		}
 	}
 
diff --git a/cmd/collectors/zapi/collector/zapi.go b/cmd/collectors/zapi/collector/zapi.go
index 9bfe1fbc..7e4e0c77 100644
--- a/cmd/collectors/zapi/collector/zapi.go
+++ b/cmd/collectors/zapi/collector/zapi.go
@@ -11,6 +11,7 @@ import (
 	"goharvest2/cmd/poller/collector"
 	"goharvest2/pkg/color"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 
@@ -60,7 +61,7 @@ func (me *Zapi) Init() error {
 		return err
 	}
 
-	me.Logger.Debug().Msg("initialized")
+	logger.Debug(me.Prefix, "initialized")
 	return nil
 }
 
@@ -75,7 +76,7 @@ func (me *Zapi) InitVars() error {
 	if err = me.Client.Init(5); err != nil { // 5 retries before giving up to connect
 		return errors.New(errors.ERR_CONNECTION, err.Error())
 	}
-	me.Logger.Debug().Msgf("connected to: %s", me.Client.Info())
+	logger.Debug(me.Prefix, "connected to: %s", me.Client.Info())
 
 	me.TemplateFn = me.Params.GetChildS("objects").GetChildContentS(me.Object) // @TODO err handling
 
@@ -86,7 +87,7 @@ func (me *Zapi) InitVars() error {
 
 	template, err := me.ImportSubTemplate(model, me.TemplateFn, me.Client.Version())
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msgf("Error importing subtemplate: %s", me.TemplateFn)
+		logger.Error(me.Prefix, "Error importing subtemplate: %s", err)
 		return err
 	}
 	me.Params.Union(template)
@@ -108,15 +109,15 @@ func (me *Zapi) InitCache() error {
 	if me.Client.IsClustered() {
 		if b := me.Params.GetChildContentS("batch_size"); b != "" {
 			if _, err := strconv.Atoi(b); err == nil {
-				me.Logger.Trace().Msgf("using batch-size [%s]", me.batchSize)
+				logger.Trace(me.Prefix, "using batch-size [%s]", me.batchSize)
 				me.batchSize = b
 			}
 		}
 		if me.batchSize == "" && me.Params.GetChildContentS("no_max_records") != "true" {
-			me.Logger.Trace().Msgf("using default batch-size [%s]", BatchSize)
+			logger.Trace(me.Prefix, "using default batch-size [%s]", BatchSize)
 			me.batchSize = BatchSize
 		} else {
-			me.Logger.Trace().Msg("using default no batch-size")
+			logger.Trace(me.Prefix, "using default no batch-size")
 		}
 	}
 
@@ -129,7 +130,7 @@ func (me *Zapi) InitCache() error {
 
 	var ok bool
 
-	me.Logger.Debug().Msgf("Parsing counters: %d values", len(counters.GetChildren()))
+	logger.Debug(me.Prefix, "Parsing counters: %d values", len(counters.GetChildren()))
 
 	if ok, me.desiredAttributes = me.LoadCounters(counters); !ok {
 		if me.Params.GetChildContentS("collect_only_labels") != "true" {
@@ -137,7 +138,7 @@ func (me *Zapi) InitCache() error {
 		}
 	}
 
-	me.Logger.Debug().Msgf("initialized cache with %d metrics and %d labels", len(me.Matrix.GetInstances()), len(me.instanceLabelPaths))
+	logger.Debug(me.Prefix, "initialized cache with %d metrics and %d labels", len(me.Matrix.GetInstances()), len(me.instanceLabelPaths))
 
 	if len(me.instanceKeyPaths) == 0 && me.Params.GetChildContentS("only_cluster_instance") != "true" {
 		return errors.New(errors.INVALID_PARAM, "no instance keys indicated")
@@ -145,8 +146,8 @@ func (me *Zapi) InitCache() error {
 
 	// @TODO validate
 	me.shortestPathPrefix = ParseShortestPath(me.Matrix, me.instanceLabelPaths)
-	me.Logger.Debug().Msgf("Parsed Instance Keys: %v", me.instanceKeyPaths)
-	me.Logger.Debug().Msgf("Parsed Instance Key Prefix: %v", me.shortestPathPrefix)
+	logger.Debug(me.Prefix, "Parsed Instance Keys: %v", me.instanceKeyPaths)
+	logger.Debug(me.Prefix, "Parsed Instance Key Prefix: %v", me.shortestPathPrefix)
 	return nil
 
 }
@@ -175,7 +176,7 @@ func (me *Zapi) PollInstance() (*matrix.Matrix, error) {
 		err               error
 	)
 
-	me.Logger.Debug().Msg("starting instance poll")
+	logger.Debug(me.Prefix, "starting instance poll")
 
 	oldCount = uint64(len(me.Matrix.GetInstances()))
 	me.Matrix.PurgeInstances()
@@ -215,22 +216,22 @@ func (me *Zapi) PollInstance() (*matrix.Matrix, error) {
 				return nil, errors.New(errors.ERR_NO_INSTANCE, "no instances in server response")
 			}
 
-			me.Logger.Debug().Msgf("fetching %d instances", len(instances))
+			logger.Debug(me.Prefix, "fetching %d instances", len(instances))
 
 			for _, instance := range instances {
 				//c.logger.Printf(c.Prefix, "Handling instance element <%v> [%s]", &instance, instance.GetName())
 				keys, found = instance.SearchContent(me.shortestPathPrefix, me.instanceKeyPaths)
 
-				me.Logger.Debug().Msgf("keys=%v keypaths=%v found=%v", keys, me.instanceKeyPaths, found)
-				me.Logger.Debug().Msgf("fetched instance keys (%v): %v", me.instanceKeyPaths, keys)
+				logger.Debug(me.Prefix, "keys=%v keypaths=%v found=%v", keys, me.instanceKeyPaths, found)
+				logger.Debug(me.Prefix, "fetched instance keys (%v): %v", me.instanceKeyPaths, keys)
 
 				if !found {
-					me.Logger.Debug().Msg("skipping element, no instance keys found")
+					logger.Debug(me.Prefix, "skipping element, no instance keys found")
 				} else {
 					if _, err = me.Matrix.NewInstance(strings.Join(keys, ".")); err != nil {
-						me.Logger.Error().Stack().Err(err).Msg("")
+						logger.Error(me.Prefix, err.Error())
 					} else {
-						me.Logger.Debug().Msgf("added instance [%s]", strings.Join(keys, "."))
+						logger.Debug(me.Prefix, "added instance [%s]", strings.Join(keys, "."))
 						count += 1
 					}
 				}
@@ -240,9 +241,9 @@ func (me *Zapi) PollInstance() (*matrix.Matrix, error) {
 
 	err = me.Metadata.LazySetValueUint64("count", "instance", count)
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
-	me.Logger.Debug().Msgf("added %d instances to cache (old cache had %d)", count, oldCount)
+	logger.Debug(me.Prefix, "added %d instances to cache (old cache had %d)", count, oldCount)
 
 	if len(me.Matrix.GetInstances()) == 0 {
 		return nil, errors.New(errors.ERR_NO_INSTANCE, "no instances fetched")
@@ -268,7 +269,7 @@ func (me *Zapi) PollData() (*matrix.Matrix, error) {
 	fetch = func(instance *matrix.Instance, node *node.Node, path []string) {
 
 		newpath := append(path, node.GetNameS())
-		me.Logger.Debug().Msgf(" > %s(%s)%s <%s%d%s> name=[%s%s%s%s] value=[%s%s%s]", color.Grey, newpath, color.End, color.Red, len(node.GetChildren()), color.End, color.Bold, color.Cyan, node.GetNameS(), color.End, color.Yellow, node.GetContentS(), color.End)
+		logger.Debug(me.Prefix, " > %s(%s)%s <%s%d%s> name=[%s%s%s%s] value=[%s%s%s]", color.Grey, newpath, color.End, color.Red, len(node.GetChildren()), color.End, color.Bold, color.Cyan, node.GetNameS(), color.End, color.Yellow, node.GetContentS(), color.End)
 
 		if value := node.GetContentS(); value != "" {
 			key := strings.Join(newpath, ".")
@@ -285,7 +286,7 @@ func (me *Zapi) PollData() (*matrix.Matrix, error) {
 				//logger.Trace(me.Prefix, "%slabel (%s) [%s] set value (%s)%s", color.Yellow, key, label, value, color.End)
 				count += 1
 			} else {
-				me.Logger.Debug().Msgf("%sskipped (%s) with value (%s): not in metric or label cache%s", color.Blue, key, value, color.End)
+				logger.Debug(me.Prefix, "%sskipped (%s) with value (%s): not in metric or label cache%s", color.Blue, key, value, color.End)
 				skipped += 1
 			}
 		} else {
@@ -298,7 +299,7 @@ func (me *Zapi) PollData() (*matrix.Matrix, error) {
 		}
 	}
 
-	me.Logger.Debug().Msg("starting data poll")
+	logger.Debug(me.Prefix, "starting data poll")
 
 	me.Matrix.Reset()
 
@@ -335,13 +336,13 @@ func (me *Zapi) PollData() (*matrix.Matrix, error) {
 			return nil, errors.New(errors.ERR_NO_INSTANCE, "")
 		}
 
-		me.Logger.Debug().Msgf("fetched %d instance elements", len(instances))
+		logger.Debug(me.Prefix, "fetched %d instance elements", len(instances))
 
 		if me.Params.GetChildContentS("only_cluster_instance") == "true" {
 			if instance := me.Matrix.GetInstance("cluster"); instance != nil {
 				fetch(instance, instances[0], make([]string, 0))
 			} else {
-				me.Logger.Error().Stack().Err(nil).Msg("cluster instance not found in cache")
+				logger.Error(me.Prefix, "cluster instance not found in cache")
 			}
 			break
 		}
@@ -359,7 +360,7 @@ func (me *Zapi) PollData() (*matrix.Matrix, error) {
 			instance := me.Matrix.GetInstance(strings.Join(keys, "."))
 
 			if instance == nil {
-				me.Logger.Error().Stack().Err(nil).Msgf("skipped instance [%s]: not found in cache", strings.Join(keys, "."))
+				logger.Error(me.Prefix, "skipped instance [%s]: not found in cache", strings.Join(keys, "."))
 				continue
 			}
 			fetch(instance, instanceElem, make([]string, 0))
diff --git a/cmd/collectors/zapi/plugins/shelf/shelf.go b/cmd/collectors/zapi/plugins/shelf/shelf.go
index 32688a2f..8f5131fd 100644
--- a/cmd/collectors/zapi/plugins/shelf/shelf.go
+++ b/cmd/collectors/zapi/plugins/shelf/shelf.go
@@ -9,6 +9,7 @@ import (
 	"goharvest2/pkg/api/ontapi/zapi"
 	"goharvest2/pkg/dict"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"strings"
@@ -36,7 +37,7 @@ func (my *Shelf) Init() error {
 	}
 
 	if my.client, err = zapi.New(my.ParentParams); err != nil {
-		my.Logger.Error().Stack().Err(err).Msg("connecting")
+		logger.Error(my.Prefix, "connecting: %v", err)
 		return err
 	}
 
@@ -50,7 +51,7 @@ func (my *Shelf) Init() error {
 		my.query = "storage-shelf-environment-list-info"
 	}
 
-	my.Logger.Debug().Msg("plugin connected!")
+	logger.Debug(my.Prefix, "plugin connected!")
 
 	my.data = make(map[string]*matrix.Matrix)
 	my.instanceKeys = make(map[string]string)
@@ -93,29 +94,29 @@ func (my *Shelf) Init() error {
 						my.instanceKeys[attribute] = metricName
 						my.instanceLabels[attribute].Set(metricName, display)
 						instanceKeys.NewChildS("", display)
-						my.Logger.Debug().Msgf("added instance key: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
+						logger.Debug(my.Prefix, "added instance key: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
 					} else {
 						my.instanceLabels[attribute].Set(metricName, display)
 						instanceLabels.NewChildS("", display)
-						my.Logger.Debug().Msgf("added instance label: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
+						logger.Debug(my.Prefix, "added instance label: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
 					}
 				} else {
 					metric, err := my.data[attribute].NewMetricFloat64(metricName)
 					if err != nil {
-						my.Logger.Error().Stack().Err(err).Msg("add metric")
+						logger.Error(my.Prefix, "add metric: %v", err)
 						return err
 					}
 					metric.SetName(display)
-					my.Logger.Debug().Msgf("added metric: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
+					logger.Debug(my.Prefix, "added metric: (%s) (%s) [%s]", attribute, x.GetNameS(), display)
 				}
 			}
 		}
-		my.Logger.Debug().Msgf("added data for [%s] with %d metrics", attribute, len(my.data[attribute].GetMetrics()))
+		logger.Debug(my.Prefix, "added data for [%s] with %d metrics", attribute, len(my.data[attribute].GetMetrics()))
 
 		my.data[attribute].SetExportOptions(exportOptions)
 	}
 
-	my.Logger.Debug().Msgf("initialized with data [%d] objects", len(my.data))
+	logger.Debug(my.Prefix, "initialized with data [%d] objects", len(my.data))
 	return nil
 }
 
@@ -148,7 +149,7 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		return nil, errors.New(errors.ERR_NO_INSTANCE, "no shelf instances found")
 	}
 
-	my.Logger.Debug().Msgf("fetching %d shelf counters", len(shelves))
+	logger.Debug(my.Prefix, "fetching %d shelf counters", len(shelves))
 
 	output := make([]*matrix.Matrix, 0)
 
@@ -168,17 +169,17 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 			data.PurgeInstances()
 
 			if my.instanceKeys[attribute] == "" {
-				my.Logger.Warn().Msgf("no instance keys defined for object [%s], skipping", attribute)
+				logger.Warn(my.Prefix, "no instance keys defined for object [%s], skipping", attribute)
 				continue
 			}
 
 			objectElem := shelf.GetChildS(attribute)
 			if objectElem == nil {
-				my.Logger.Warn().Msgf("no [%s] instances on this system", attribute)
+				logger.Warn(my.Prefix, "no [%s] instances on this system", attribute)
 				continue
 			}
 
-			my.Logger.Debug().Msgf("fetching %d [%s] instances", len(objectElem.GetChildren()), attribute)
+			logger.Debug(my.Prefix, "fetching %d [%s] instances", len(objectElem.GetChildren()), attribute)
 
 			for _, obj := range objectElem.GetChildren() {
 
@@ -187,11 +188,11 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 					instance, err := data.NewInstance(shelfId + "." + key)
 
 					if err != nil {
-						my.Logger.Debug().Msgf("add (%s) instance: %v", attribute, err)
+						logger.Debug(my.Prefix, "add (%s) instance: %v", attribute, err)
 						return nil, err
 					}
 
-					my.Logger.Debug().Msgf("add (%s) instance: %s.%s", attribute, shelfId, key)
+					logger.Debug(my.Prefix, "add (%s) instance: %s.%s", attribute, shelfId, key)
 
 					for label, labelDisplay := range my.instanceLabels[attribute].Map() {
 						if value := obj.GetChildContentS(label); value != "" {
@@ -203,7 +204,7 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 					instance.SetLabel("shelf_id", shelfId)
 
 				} else {
-					my.Logger.Debug().Msgf("instance without [%s], skipping", my.instanceKeys[attribute])
+					logger.Debug(my.Prefix, "instance without [%s], skipping", my.instanceKeys[attribute])
 				}
 			}
 
@@ -240,7 +241,7 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				instance := data.GetInstance(shelfId + "." + key)
 
 				if instance == nil {
-					my.Logger.Debug().Msgf("(%s) instance [%s.%s] not found in cache skipping", attribute, shelfId, key)
+					logger.Debug(my.Prefix, "(%s) instance [%s.%s] not found in cache skipping", attribute, shelfId, key)
 					continue
 				}
 
@@ -248,9 +249,9 @@ func (my *Shelf) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 
 					if value := strings.Split(obj.GetChildContentS(metricKey), " ")[0]; value != "" {
 						if err := m.SetValueString(instance, value); err != nil {
-							my.Logger.Debug().Msgf("(%s) failed to parse value (%s): %v", metricKey, value, err)
+							logger.Debug(my.Prefix, "(%s) failed to parse value (%s): %v", metricKey, value, err)
 						} else {
-							my.Logger.Debug().Msgf("(%s) added value (%s)", metricKey, value)
+							logger.Debug(my.Prefix, "(%s) added value (%s)", metricKey, value)
 						}
 					}
 				}
diff --git a/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go b/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go
index f10d3179..8516aa95 100644
--- a/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go
+++ b/cmd/collectors/zapi/plugins/snapmirror/snapmirror.go
@@ -7,6 +7,7 @@ import (
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/pkg/api/ontapi/zapi"
 	"goharvest2/pkg/dict"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"strings"
@@ -35,7 +36,7 @@ func (my *SnapMirror) Init() error {
 	}
 
 	if my.client, err = zapi.New(my.ParentParams); err != nil {
-		my.Logger.Error().Stack().Err(err).Msg("connecting")
+		logger.Error(my.Prefix, "connecting: %v", err)
 		return err
 	}
 
@@ -50,7 +51,7 @@ func (my *SnapMirror) Init() error {
 	my.destLimitCache = dict.New()
 	my.srcLimitCache = dict.New()
 
-	my.Logger.Debug().Msg("plugin initialized")
+	logger.Debug(my.Prefix, "plugin initialized")
 	return nil
 }
 
@@ -61,7 +62,7 @@ func (my *SnapMirror) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		if err := my.updateNodeCache(); err != nil {
 			return nil, err
 		}
-		my.Logger.Debug().Msg("updated node cache")
+		logger.Debug(my.Prefix, "updated node cache")
 	} else if my.nodeUpdCounter > 10 {
 		my.nodeUpdCounter = 0
 	} else {
@@ -72,7 +73,7 @@ func (my *SnapMirror) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		if err := my.updateLimitCache(); err != nil {
 			return nil, err
 		}
-		my.Logger.Debug().Msg("updated limit cache")
+		logger.Debug(my.Prefix, "updated limit cache")
 	} else if my.limitUpdCounter > 100 {
 		my.limitUpdCounter = 0
 	} else {
@@ -153,7 +154,7 @@ func (my *SnapMirror) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		}
 	}
 
-	my.Logger.Debug().Msgf("updated %d destination and %d source nodes, %d node limits", destUpdCount, srcUpdCount, limitUpdCount)
+	logger.Debug(my.Prefix, "updated %d destination and %d source nodes, %d node limits", destUpdCount, srcUpdCount, limitUpdCount)
 
 	return nil, nil
 }
@@ -193,7 +194,7 @@ func (my *SnapMirror) updateNodeCache() error {
 		}
 	}
 
-	my.Logger.Debug().Msgf("updated node cache for %d volumes", count)
+	logger.Debug(my.Prefix, "updated node cache for %d volumes", count)
 	return nil
 }
 
@@ -228,7 +229,7 @@ func (my *SnapMirror) updateLimitCache() error {
 			count += 1
 		}
 	}
-	my.Logger.Debug().Msgf("updated limit cache for %d nodes", count)
+	logger.Debug(my.Prefix, "updated limit cache for %d nodes", count)
 	return nil
 
 }
diff --git a/cmd/collectors/zapiperf/plugins/fcp/fcp.go b/cmd/collectors/zapiperf/plugins/fcp/fcp.go
index 4a81e133..fafd557b 100644
--- a/cmd/collectors/zapiperf/plugins/fcp/fcp.go
+++ b/cmd/collectors/zapiperf/plugins/fcp/fcp.go
@@ -6,6 +6,7 @@ package main
 import (
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"math"
 	"strconv"
@@ -66,7 +67,7 @@ func (me *Fcp) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		var err error
 
 		if speed, err = strconv.Atoi(instance.GetLabel("speed")); err != nil {
-			me.Logger.Debug().Msgf("skip, can't convert speed (%s) to numeric", s)
+			logger.Debug(me.Prefix, "skip, can't convert speed (%s) to numeric", s)
 		}
 
 		if speed != 0 {
@@ -78,7 +79,7 @@ func (me *Fcp) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				rx_percent = rx_bytes / float64(speed)
 				err := rx.SetValueFloat64(instance, rx_percent)
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 
@@ -86,14 +87,14 @@ func (me *Fcp) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				tx_percent = tx_bytes / float64(speed)
 				err := tx.SetValueFloat64(instance, tx_percent)
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 
 			if rx_ok || tx_ok {
 				err := util.SetValueFloat64(instance, math.Max(rx_percent, tx_percent))
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 		}
diff --git a/cmd/collectors/zapiperf/plugins/nic/nic.go b/cmd/collectors/zapiperf/plugins/nic/nic.go
index 60abc821..0a1a479d 100644
--- a/cmd/collectors/zapiperf/plugins/nic/nic.go
+++ b/cmd/collectors/zapiperf/plugins/nic/nic.go
@@ -16,6 +16,7 @@ package main
 import (
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"math"
 	"strconv"
@@ -76,14 +77,14 @@ func (me *Nic) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		if s = instance.GetLabel("speed"); strings.HasSuffix(s, "M") {
 			base, err = strconv.Atoi(strings.TrimSuffix(s, "M"))
 			if err != nil {
-				me.Logger.Warn().Msgf("convert speed [%s]", s)
+				logger.Warn(me.Prefix, "convert speed [%s]", s)
 			} else {
 				speed = base * 125000
 				instance.SetLabel("speed", strconv.Itoa(speed))
-				me.Logger.Debug().Msgf("converted speed (%s) to numeric (%d)", s, speed)
+				logger.Debug(me.Prefix, "converted speed (%s) to numeric (%d)", s, speed)
 			}
 		} else if speed, err = strconv.Atoi(s); err != nil {
-			me.Logger.Warn().Msgf("convert speed [%s]", s)
+			logger.Warn(me.Prefix, "convert speed [%s]", s)
 		}
 
 		if speed != 0 {
@@ -95,7 +96,7 @@ func (me *Nic) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				rx_percent = rx_bytes / float64(speed)
 				err := rx.SetValueFloat64(instance, rx_percent)
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 
@@ -103,14 +104,14 @@ func (me *Nic) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				tx_percent = tx_bytes / float64(speed)
 				err := tx.SetValueFloat64(instance, tx_percent)
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 
 			if rx_ok || tx_ok {
 				err := util.SetValueFloat64(instance, math.Max(rx_percent, tx_percent))
 				if err != nil {
-					me.Logger.Error().Stack().Err(err).Msg("error")
+					logger.Error(me.Prefix, "error: %v", err)
 				}
 			}
 		}
diff --git a/cmd/collectors/zapiperf/plugins/volume/volume.go b/cmd/collectors/zapiperf/plugins/volume/volume.go
index 53fc8c2d..8d7c7ece 100644
--- a/cmd/collectors/zapiperf/plugins/volume/volume.go
+++ b/cmd/collectors/zapiperf/plugins/volume/volume.go
@@ -5,6 +5,7 @@ package main
 
 import (
 	"goharvest2/cmd/poller/plugin"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"regexp"
 	"strings"
@@ -46,7 +47,7 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 		}
 	}
 
-	me.Logger.Debug().Msgf("extracted %d flexgroup volumes", len(cache.GetInstances()))
+	logger.Debug(me.Prefix, "extracted %d flexgroup volumes", len(cache.GetInstances()))
 
 	//cache.Reset()
 
@@ -56,7 +57,7 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 			key := i.GetLabel("node") + "." + i.GetLabel("svm") + "." + match[1]
 			fg := cache.GetInstance(key)
 			if fg == nil {
-				me.Logger.Error().Stack().Err(nil).Msgf("instance [%s] not in local cache", key)
+				logger.Error(me.Prefix, "instance [%s] not in local cache", key)
 				continue
 			}
 
@@ -68,11 +69,11 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 
 				fgm := cache.GetMetric(mkey)
 				if fgm == nil {
-					me.Logger.Error().Stack().Err(nil).Msgf("metric [%s] not in local cache", mkey)
+					logger.Error(me.Prefix, "metric [%s] not in local cache", mkey)
 					continue
 				}
 
-				me.Logger.Trace().Msgf("(%s) handling metric (%s)", fg.GetLabel("volume"), mkey)
+				logger.Trace(me.Prefix, "(%s) handling metric (%s)", fg.GetLabel("volume"), mkey)
 
 				if value, ok := m.GetValueFloat64(i); ok {
 
@@ -83,19 +84,19 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 
 						err := fgm.SetValueFloat64(fg, fgv+value)
 						if err != nil {
-							me.Logger.Error().Stack().Err(err).Msg("error")
+							logger.Error(me.Prefix, "error: %v", err)
 						}
 						// just for debugging
 						fgv2, _ := fgm.GetValueFloat64(fg)
 
-						me.Logger.Trace().Msgf("   > simple increment %f + %f = %f", fgv, value, fgv2)
+						logger.Trace(me.Prefix, "   > simple increment %f + %f = %f", fgv, value, fgv2)
 						continue
 					}
 
 					// latency metric: weighted sum
 					// ops_key := strings.Replace(mkey, "avg_latency", "total_ops", 1)
 					ops_key := strings.Replace(mkey, "_latency", "_ops", 1)
-					me.Logger.Trace().Msgf("    > weighted increment <%s * %s>", mkey, ops_key)
+					logger.Trace(me.Prefix, "    > weighted increment <%s * %s>", mkey, ops_key)
 
 					if ops := data.GetMetric(ops_key); ops != nil {
 						if ops_value, ok := ops.GetValueFloat64(i); ok {
@@ -103,15 +104,15 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 							prod := value * ops_value
 							err := fgm.SetValueFloat64(fg, fgv+prod)
 							if err != nil {
-								me.Logger.Error().Stack().Err(err).Msg("error")
+								logger.Error(me.Prefix, "error: %v", err)
 							}
 
 							// debugging
 							fgv2, _ := fgm.GetValueFloat64(fg)
 
-							me.Logger.Trace().Msgf("       %f + (%f * %f) (=%f) = %f", fgv, value, ops_value, prod, fgv2)
+							logger.Trace(me.Prefix, "       %f + (%f * %f) (=%f) = %f", fgv, value, ops_value, prod, fgv2)
 						} else {
-							me.Logger.Trace().Msg("       no ops value SKIP")
+							logger.Trace(me.Prefix, "       no ops value SKIP")
 						}
 					}
 
@@ -136,7 +137,7 @@ func (me *Volume) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 						if ops_value, ok := ops.GetValueFloat64(i); ok && ops_value != 0 {
 							err := m.SetValueFloat64(i, value/ops_value)
 							if err != nil {
-								me.Logger.Error().Stack().Err(err).Msgf("error")
+								logger.Error(me.Prefix, "error: %v", err)
 							}
 						} else {
 							m.SetValueNAN(i)
diff --git a/cmd/collectors/zapiperf/zapiperf.go b/cmd/collectors/zapiperf/zapiperf.go
index d1aeccf5..4f07c211 100644
--- a/cmd/collectors/zapiperf/zapiperf.go
+++ b/cmd/collectors/zapiperf/zapiperf.go
@@ -8,6 +8,7 @@ import (
 	"goharvest2/pkg/color"
 	"goharvest2/pkg/dict"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/set"
 	"goharvest2/pkg/tree/node"
@@ -62,7 +63,7 @@ func (me *ZapiPerf) Init() error {
 		return err
 	}
 
-	me.Logger.Debug().Msg("initialized")
+	logger.Debug(me.Prefix, "initialized")
 	return nil
 }
 
@@ -80,7 +81,7 @@ func (me *ZapiPerf) InitCache() error {
 		me.object = me.Object
 	}
 	me.Matrix.Object = me.object
-	me.Logger.Debug().Msgf("object= %s --> %s", me.Object, me.object)
+	logger.Debug(me.Prefix, "object= %s --> %s", me.Object, me.object)
 	return nil
 }
 
@@ -89,10 +90,10 @@ func (me *ZapiPerf) loadParamStr(name, defaultValue string) string {
 	var x string
 
 	if x = me.Params.GetChildContentS(name); x != "" {
-		me.Logger.Debug().Msgf("using %s = [%s]", name, x)
+		logger.Debug(me.Prefix, "using %s = [%s]", name, x)
 		return x
 	}
-	me.Logger.Debug().Msgf("using %s = [%s] (default)", name, defaultValue)
+	logger.Debug(me.Prefix, "using %s = [%s] (default)", name, defaultValue)
 	return defaultValue
 }
 
@@ -104,13 +105,13 @@ func (me *ZapiPerf) loadParamInt(name string, defaultValue int) int {
 
 	if x = me.Params.GetChildContentS(name); x != "" {
 		if n, e = strconv.Atoi(x); e == nil {
-			me.Logger.Debug().Msgf("using %s = [%d]", name, n)
+			logger.Debug(me.Prefix, "using %s = [%d]", name, n)
 			return n
 		}
-		me.Logger.Warn().Msgf("invalid parameter %s = [%s] (expected integer)", name, x)
+		logger.Warn(me.Prefix, "invalid parameter %s = [%s] (expected integer)", name, x)
 	}
 
-	me.Logger.Debug().Msgf("using %s = [%d] (default)", name, defaultValue)
+	logger.Debug(me.Prefix, "using %s = [%d] (default)", name, defaultValue)
 	return defaultValue
 }
 
@@ -118,7 +119,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 	var err error
 
-	me.Logger.Debug().Msg("updating data cache")
+	logger.Debug(me.Prefix, "updating data cache")
 
 	// clone matrix without numeric data
 	newData := me.Matrix.Clone(false, true, true)
@@ -179,7 +180,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 			endIndex = len(instanceKeys)
 		}
 
-		me.Logger.Debug().Msgf("starting batch poll for instances [%d:%d]", startIndex, endIndex)
+		logger.Debug(me.Prefix, "starting batch poll for instances [%d:%d]", startIndex, endIndex)
 
 		request.PopChildS(keyName + "s")
 		requestInstances := request.NewChildS(keyName+"s", "")
@@ -190,14 +191,14 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 		startIndex = endIndex
 
 		if err = me.Client.BuildRequest(request); err != nil {
-			me.Logger.Error().Stack().Err(err).Msg("build request: ")
+			logger.Error(me.Prefix, "build request: %v", err)
 			//break?
 			return nil, err
 		}
 
 		response, rd, pd, err := me.Client.InvokeWithTimers()
 		if err != nil {
-			//me.Logger.Error(me.Prefix, "data request: %v", err)
+			//logger.Error(me.Prefix, "data request: %v", err)
 			//@TODO handle "resource limit exceeded"
 			//break
 			return nil, err
@@ -214,7 +215,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 			break
 		}
 
-		me.Logger.Debug().Msgf("fetched batch with %d instances", len(instances.GetChildren()))
+		logger.Debug(me.Prefix, "fetched batch with %d instances", len(instances.GetChildren()))
 
 		// timestamp for batch instances
 		// ignore timestamp from ZAPI which is always integer
@@ -225,28 +226,28 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 			key := i.GetChildContentS(me.instanceKey)
 			if key == "" {
-				me.Logger.Debug().Msgf("skip instance, no key [%s] (name=%s, uuid=%s)", me.instanceKey, i.GetChildContentS("name"), i.GetChildContentS("uuid"))
+				logger.Debug(me.Prefix, "skip instance, no key [%s] (name=%s, uuid=%s)", me.instanceKey, i.GetChildContentS("name"), i.GetChildContentS("uuid"))
 				continue
 			}
 
 			instance := newData.GetInstance(key)
 			if instance == nil {
-				me.Logger.Debug().Msgf("skip instance [%s], not found in cache", key)
+				logger.Debug(me.Prefix, "skip instance [%s], not found in cache", key)
 				continue
 			}
 
 			counters := i.GetChildS("counters")
 			if counters == nil {
-				me.Logger.Debug().Msgf("skip instance [%s], no data counters", key)
+				logger.Debug(me.Prefix, "skip instance [%s], no data counters", key)
 				continue
 			}
 
-			me.Logger.Debug().Msgf("fetching data of instance [%s]", key)
+			logger.Debug(me.Prefix, "fetching data of instance [%s]", key)
 
 			// add batch timestamp as custom counter
 			err := timestamp.SetValueFloat64(instance, ts)
 			if err != nil {
-				me.Logger.Error().Stack().Err(err).Msg("")
+				logger.Error(me.Prefix, "error: %v", err)
 			}
 
 			for _, cnt := range counters.GetChildren() {
@@ -257,11 +258,11 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 				// sanity check
 				// @TODO - redundant
 				if name == "" || value == "" {
-					me.Logger.Debug().Msgf("skipping incomplete counter [%s] with value [%s]", name, value)
+					logger.Debug(me.Prefix, "skipping incomplete counter [%s] with value [%s]", name, value)
 					continue
 				}
 
-				me.Logger.Trace().Msgf("(%s%s%s) parsing counter (%s) = %v", color.Grey, key, color.End, name, value)
+				logger.Trace(me.Prefix, "(%s%s%s) parsing counter (%s) = %v", color.Grey, key, color.End, name, value)
 
 				// ZAPI counter for us is either instance label (string)
 				// or numeric metric (scalar or string)
@@ -269,7 +270,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 				// store as instance label
 				if display, has := me.instanceLabels[name]; has {
 					instance.SetLabel(display, value)
-					me.Logger.Trace().Msgf("+ label (%s) = [%s%s%s]", display, color.Yellow, value, color.End)
+					logger.Trace(me.Prefix, "+ label (%s) = [%s%s%s]", display, color.Yellow, value, color.End)
 					continue
 				}
 
@@ -280,20 +281,20 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 					if len(labels) != len(values) {
 						// warn & skip
-						me.Logger.Error().Stack().Err(nil).Msgf("histogram (%s) labels don't match with parsed values [%s]", name, value)
+						logger.Error(me.Prefix, "histogram (%s) labels don't match with parsed values [%s]", name, value)
 						continue
 					}
 
 					for i, label := range labels {
 						if metric := newData.GetMetric(name + "." + label); metric != nil {
 							if err = metric.SetValueString(instance, values[i]); err != nil {
-								me.Logger.Error().Stack().Err(err).Msgf("set histogram (%s.%s) value [%s]: ", name, label, values[i])
+								logger.Error(me.Prefix, "set histogram (%s.%s) value [%s]: %v", name, label, values[i], err)
 							} else {
-								me.Logger.Trace().Msgf("+ histogram (%s.%s) = [%s%s%s]", name, label, color.Pink, values[i], color.End)
+								logger.Trace(me.Prefix, "+ histogram (%s.%s) = [%s%s%s]", name, label, color.Pink, values[i], color.End)
 								count++
 							}
 						} else {
-							me.Logger.Warn().Msgf("histogram (%s.%s) = [%s] not in cache", name, label, value)
+							logger.Warn(me.Prefix, "histogram (%s.%s) = [%s] not in cache", name, label, value)
 						}
 					}
 					continue
@@ -302,15 +303,15 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 				// store as scalar metric
 				if metric := newData.GetMetric(name); metric != nil {
 					if err = metric.SetValueString(instance, value); err != nil {
-						me.Logger.Error().Stack().Err(err).Msgf("set metric (%s) value [%s]", name, value)
+						logger.Error(me.Prefix, "set metric (%s) value [%s]: %v", name, value, err)
 					} else {
-						me.Logger.Trace().Msgf("+ metric (%s) = [%s%s%s]", name, color.Cyan, value, color.End)
+						logger.Trace(me.Prefix, "+ metric (%s) = [%s%s%s]", name, color.Cyan, value, color.End)
 						count++
 					}
 					continue
 				}
 
-				me.Logger.Warn().Msgf("counter (%s) [%s] not found in cache", name, value)
+				logger.Warn(me.Prefix, "counter (%s) [%s] not found in cache", name, value)
 
 			} // end loop over counters
 
@@ -326,11 +327,11 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 	me.Metadata.LazySetValueUint64("count", "data", count)
 	me.AddCollectCount(count)
 
-	me.Logger.Debug().Msgf("collected %d data points in %d batch polls", count, batchCount)
+	logger.Debug(me.Prefix, "collected %d data points in %d batch polls", count, batchCount)
 
 	// skip calculating from delta if no data from previous poll
 	if me.isCacheEmpty {
-		me.Logger.Debug().Msg("skip postprocessing until next poll (previous cache empty)")
+		logger.Debug(me.Prefix, "skip postprocessing until next poll (previous cache empty)")
 		me.Matrix = newData
 		me.isCacheEmpty = false
 		return nil, nil
@@ -338,8 +339,8 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 	calcStart := time.Now()
 
-	me.Logger.Debug().Msg("starting delta calculations from previous cache")
-	//me.Logger.Debug(me.Prefix, "data has dimensions (%d x %d)", len(newData.Data), len(newData.Data[0]))
+	logger.Debug(me.Prefix, "starting delta calculations from previous cache")
+	//logger.Debug(me.Prefix, "data has dimensions (%d x %d)", len(newData.Data), len(newData.Data[0]))
 
 	// cache data, to store after calculations
 	cachedData := newData.Clone(true, true, true) // @TODO implement copy data
@@ -364,11 +365,11 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 	// calculate timestamp delta first since many counters require it for postprocessing
 	// timestamp has "raw" property, so won't be postprocessed automatically
 	// fmt.Printf("\npostprocessing %s%s%s - %s%v%s\n", color.Red, timestamp.Name, color.End, color.Bold, timestamp.Properties, color.End)
-	//me.Logger.Debug(me.Prefix, "cooking [%s] (%s)", timestamp.Name, timestamp.Properties)
+	//logger.Debug(me.Prefix, "cooking [%s] (%s)", timestamp.Name, timestamp.Properties)
 	//print_vector("current", newData.Data[timestamp.Index])
 	//print_vector("previous", me.Data.Data[timestamp.Index])
 	if err = timestamp.Delta(me.Matrix.GetMetric("timestamp")); err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("(timestamp) calculate delta:")
+		logger.Error(me.Prefix, "(timestamp) calculate delta: %v", err)
 		// @TODO terminate since other counters will be incorrect
 	}
 
@@ -389,7 +390,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 		// for all the other properties we start with delta
 		if err = metric.Delta(me.Matrix.GetMetric(key)); err != nil {
-			me.Logger.Error().Stack().Err(err).Msgf("(%s) calculate delta:", key)
+			logger.Error(me.Prefix, "(%s) calculate delta: %v", key, err)
 			continue
 		}
 
@@ -408,7 +409,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 		// We assume that delta of base counters is already calculated
 		// (name of base counter is stored as Comment)
 		if base = newData.GetMetric(metric.GetComment()); base == nil {
-			me.Logger.Warn().Msgf("(%s) <%s> base counter (%s) missing", key, property, metric.GetComment())
+			logger.Warn(me.Prefix, "(%s) <%s> base counter (%s) missing", key, property, metric.GetComment())
 			continue
 		}
 
@@ -423,7 +424,7 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 			}
 
 			if err != nil {
-				me.Logger.Error().Stack().Err(err).Msgf("(%s) division by base: ", key)
+				logger.Error(me.Prefix, "(%s) division by base: %v", key, err)
 			}
 
 			if property == "average" {
@@ -433,19 +434,19 @@ func (me *ZapiPerf) PollData() (*matrix.Matrix, error) {
 
 		if property == "percent" {
 			if err = metric.MultiplyByScalar(100); err != nil {
-				me.Logger.Error().Stack().Err(err).Msgf("(%s) multiply by scalar: ", key)
+				logger.Error(me.Prefix, "(%s) multiply by scalar: %v", key, err)
 			}
 			continue
 		}
 
-		me.Logger.Error().Stack().Err(err).Msgf("(%s) unknown property: %s", key, property)
+		logger.Error(me.Prefix, "(%s) unknown property: %s", key, property)
 	}
 
 	// calculate rates (which we deferred to calculate averages/percents first)
 	for i, metric := range orderedMetrics {
 		if metric.GetProperty() == "rate" {
 			if err = metric.Divide(timestamp); err != nil {
-				me.Logger.Error().Stack().Err(err).Msgf("(%s) calculate rate: ", orderedKeys[i])
+				logger.Error(me.Prefix, "(%s) calculate rate: %v", orderedKeys[i], err)
 			}
 		}
 	}
@@ -506,7 +507,7 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 		return nil, errors.New(errors.MISSING_PARAM, "counters")
 	}
 
-	me.Logger.Debug().Msgf("updating metric cache (old cache has %d metrics and %d labels", oldMetrics.Size(), oldLabels.Size())
+	logger.Debug(me.Prefix, "updating metric cache (old cache has %d metrics and %d labels", oldMetrics.Size(), oldLabels.Size())
 
 	// build request
 	request = node.NewXmlS("perf-object-counter-list-info")
@@ -541,19 +542,19 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 		display, ok := wanted.GetHas(key)
 		// counter not requested
 		if !ok {
-			me.Logger.Trace().Msgf("%sskip [%s], not requested%s", color.Grey, key, color.End)
+			logger.Trace(me.Prefix, "%sskip [%s], not requested%s", color.Grey, key, color.End)
 			continue
 		}
 
 		// deprecated and possibly replaced counter
 		if counter.GetChildContentS("is-deprecated") == "true" {
 			if r := counter.GetChildContentS("replaced-by"); r != "" {
-				me.Logger.Info().Msgf("replaced deprecated counter [%s] with [%s]", key, r)
+				logger.Info(me.Prefix, "replaced deprecated counter [%s] with [%s]", key, r)
 				if !wanted.Has(r) {
 					replaced.Add(r)
 				}
 			} else {
-				me.Logger.Info().Msgf("skip [%s], deprecated", key)
+				logger.Info(me.Prefix, "skip [%s], deprecated", key)
 				continue
 			}
 		}
@@ -562,27 +563,27 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 		if strings.Contains(counter.GetChildContentS("properties"), "string") {
 			oldLabels.Delete(key)
 			me.instanceLabels[key] = display
-			me.Logger.Debug().Msgf("%s+[%s] added as label name (%s)%s", color.Yellow, key, display, color.End)
+			logger.Debug(me.Prefix, "%s+[%s] added as label name (%s)%s", color.Yellow, key, display, color.End)
 		} else {
 			// add counter as numeric metric
 			oldMetrics.Delete(key)
 			if r := me.addCounter(counter, key, display, true, counters); r != "" && !wanted.Has(r) {
 				missing.Add(r) // required base counter, missing in template
-				me.Logger.Debug().Msgf("%smarking [%s] as required base counter for [%s]%s", color.Red, r, key, color.End)
+				logger.Debug(me.Prefix, "%smarking [%s] as required base counter for [%s]%s", color.Red, r, key, color.End)
 			}
 		}
 	}
 
 	// second loop for replaced counters
 	if replaced.Size() > 0 {
-		me.Logger.Debug().Msgf("attempting to retrieve metadata of %d replaced counters", replaced.Size())
+		logger.Debug(me.Prefix, "attempting to retrieve metadata of %d replaced counters", replaced.Size())
 		for name, counter := range counters {
 			if replaced.Has(name) {
 				oldMetrics.Delete(name)
-				me.Logger.Debug().Msgf("adding [%s] (replacment for deprecated counter)", name)
+				logger.Debug(me.Prefix, "adding [%s] (replacment for deprecated counter)", name)
 				if r := me.addCounter(counter, name, name, true, counters); r != "" && !wanted.Has(r) {
 					missing.Add(r) // required base counter, missing in template
-					me.Logger.Debug().Msgf("%smarking [%s] as required base counter for [%s]%s", color.Red, r, name, color.End)
+					logger.Debug(me.Prefix, "%smarking [%s] as required base counter for [%s]%s", color.Red, r, name, color.End)
 				}
 			}
 		}
@@ -590,12 +591,12 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 
 	// third loop for required base counters, not in template
 	if missing.Size() > 0 {
-		me.Logger.Debug().Msgf("attempting to retrieve metadata of %d missing base counters", missing.Size())
+		logger.Debug(me.Prefix, "attempting to retrieve metadata of %d missing base counters", missing.Size())
 		for name, counter := range counters {
-			//me.Logger.Debug(me.Prefix, "%shas??? [%s]%s", color.Grey, name, color.End)
+			//logger.Debug(me.Prefix, "%shas??? [%s]%s", color.Grey, name, color.End)
 			if missing.Has(name) {
 				oldMetrics.Delete(name)
-				me.Logger.Debug().Msgf("adding [%s] (missing base counter)", name)
+				logger.Debug(me.Prefix, "adding [%s] (missing base counter)", name)
 				me.addCounter(counter, name, "", false, counters)
 			}
 		}
@@ -608,7 +609,7 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 	if !oldMetrics.Has("timestamp") {
 		m, err := me.Matrix.NewMetricFloat64("timestamp")
 		if err != nil {
-			me.Logger.Error().Stack().Err(err).Msg("add timestamp metric")
+			logger.Error(me.Prefix, "add timestamp metric: %v", err)
 		}
 		m.SetProperty("raw")
 		m.SetExportable(false)
@@ -619,21 +620,21 @@ func (me *ZapiPerf) PollCounter() (*matrix.Matrix, error) {
 		// @TODO
 		if key != "timestamp" && !strings.Contains(key, ".") {
 			me.Matrix.RemoveMetric(key)
-			me.Logger.Debug().Msgf("removed metric [%s]", key)
+			logger.Debug(me.Prefix, "removed metric [%s]", key)
 		}
 	}
 
 	for key := range oldLabels.Iter() {
 		//me.Data.RemoveLabel(key)
 		delete(me.instanceLabels, key)
-		me.Logger.Debug().Msgf("removed label [%s]", key)
+		logger.Debug(me.Prefix, "removed label [%s]", key)
 	}
 
 	metricsAdded := len(me.Matrix.GetMetrics()) - (oldMetricsSize - oldMetrics.Size())
 	labelsAdded := len(me.instanceLabels) - (oldLabelsSize - oldLabels.Size())
 
-	me.Logger.Debug().Msgf("added %d new, removed %d metrics (total: %d)", metricsAdded, oldMetrics.Size(), len(me.Matrix.GetMetrics()))
-	me.Logger.Debug().Msgf("added %d new, removed %d labels (total: %d)", labelsAdded, oldLabels.Size(), len(me.instanceLabels))
+	logger.Debug(me.Prefix, "added %d new, removed %d metrics (total: %d)", metricsAdded, oldMetrics.Size(), len(me.Matrix.GetMetrics()))
+	logger.Debug(me.Prefix, "added %d new, removed %d labels (total: %d)", labelsAdded, oldLabels.Size(), len(me.instanceLabels))
 
 	if len(me.Matrix.GetMetrics()) == 0 {
 		return nil, errors.New(errors.ERR_NO_METRIC, "")
@@ -661,7 +662,7 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 	} else if strings.Contains(p, "percent") {
 		property = "percent"
 	} else {
-		me.Logger.Warn().Msgf("skip counter [%s] with unknown property [%s]", name, p)
+		logger.Warn(me.Prefix, "skip counter [%s] with unknown property [%s]", name, p)
 		return ""
 	}
 
@@ -672,7 +673,7 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 		display = strings.ReplaceAll(name, "-", "_") // redundant for zapiperf
 	}
 
-	me.Logger.Debug().Msgf("handling counter [%s] with property [%s] and unit [%s]", name, property, unit)
+	logger.Debug(me.Prefix, "handling counter [%s] with property [%s] and unit [%s]", name, property, unit)
 
 	// counter type is array, each element will be converted to a metric instance
 	if counter.GetChildContentS("type") == "array" {
@@ -681,7 +682,7 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 		var e string
 
 		if labels, e = parseHistogramLabels(counter); e != "" {
-			me.Logger.Warn().Msgf("skipping [%s] of type array: %s", name, e)
+			logger.Warn(me.Prefix, "skipping [%s] of type array: %s", name, e)
 			return ""
 		}
 
@@ -690,15 +691,15 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 				if base.GetChildContentS("type") == "array" {
 					baseLabels, e = parseHistogramLabels(base)
 					if e != "" {
-						me.Logger.Warn().Msgf("skipping [%s], base counter [%s] is array, but %s", name, baseCounter, e)
+						logger.Warn(me.Prefix, "skipping [%s], base counter [%s] is array, but %s", name, baseCounter, e)
 						return ""
 					} else if len(baseLabels) != len(labels) {
-						me.Logger.Warn().Msgf("skipping [%s], array labels don't match with base counter labels [%s]", name, baseCounter)
+						logger.Warn(me.Prefix, "skipping [%s], array labels don't match with base counter labels [%s]", name, baseCounter)
 						return ""
 					}
 				}
 			} else {
-				me.Logger.Warn().Msgf("skipping [%s], base counter [%s] not found", name, baseCounter)
+				logger.Warn(me.Prefix, "skipping [%s], base counter [%s] not found", name, baseCounter)
 				return ""
 			}
 		}
@@ -714,11 +715,11 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 			}
 
 			if m = me.Matrix.GetMetric(key); m != nil {
-				me.Logger.Debug().Msgf("updating array metric [%s] attributes", key)
+				logger.Debug(me.Prefix, "updating array metric [%s] attributes", key)
 			} else if m, err = me.Matrix.NewMetricFloat64(key); err == nil {
-				me.Logger.Debug().Msgf("%s+[%s] added array metric (%s), element with label (%s)%s", color.Pink, name, display, label, color.End)
+				logger.Debug(me.Prefix, "%s+[%s] added array metric (%s), element with label (%s)%s", color.Pink, name, display, label, color.End)
 			} else {
-				me.Logger.Error().Stack().Err(err).Msgf("add array metric element [%s]: ", key)
+				logger.Error(me.Prefix, "add array metric element [%s]: %v", key, err)
 				return ""
 			}
 
@@ -741,11 +742,11 @@ func (me *ZapiPerf) addCounter(counter *node.Node, name, display string, enabled
 	} else {
 		var m matrix.Metric
 		if m = me.Matrix.GetMetric(name); m != nil {
-			me.Logger.Debug().Msgf("updating scalar metric [%s] attributes", name)
+			logger.Debug(me.Prefix, "updating scalar metric [%s] attributes", name)
 		} else if m, err = me.Matrix.NewMetricFloat64(name); err == nil {
-			me.Logger.Debug().Msgf("%s+[%s] added scalar metric (%s)%s", color.Cyan, name, display, color.End)
+			logger.Debug(me.Prefix, "%s+[%s] added scalar metric (%s)%s", color.Cyan, name, display, color.End)
 		} else {
-			me.Logger.Error().Stack().Err(err).Msgf("add scalar metric [%s]", name)
+			logger.Error(me.Prefix, "add scalar metric [%s]: %v", name, err)
 			return ""
 		}
 
@@ -806,7 +807,7 @@ func (me *ZapiPerf) PollInstance() (*matrix.Matrix, error) {
 	}
 	oldSize = oldInstances.Size()
 
-	me.Logger.Debug().Msgf("updating instance cache (old cache has: %d)", oldInstances.Size())
+	logger.Debug(me.Prefix, "updating instance cache (old cache has: %d)", oldInstances.Size())
 
 	if me.Client.IsClustered() {
 		request = node.NewXmlS("perf-object-instance-list-info-iter")
@@ -826,7 +827,7 @@ func (me *ZapiPerf) PollInstance() (*matrix.Matrix, error) {
 	for {
 
 		if results, batchTag, err = me.Client.InvokeBatchRequest(request, batchTag); err != nil {
-			me.Logger.Error().Stack().Err(err).Msg("instance request")
+			logger.Error(me.Prefix, "instance request: %v", err)
 			break
 		}
 
@@ -846,28 +847,28 @@ func (me *ZapiPerf) PollInstance() (*matrix.Matrix, error) {
 				// instance key missing
 				n := i.GetChildContentS("name")
 				u := i.GetChildContentS("uuid")
-				me.Logger.Debug().Msgf("skip instance, missing key [%s] (name=%s, uuid=%s)", me.instanceKey, n, u)
+				logger.Debug(me.Prefix, "skip instance, missing key [%s] (name=%s, uuid=%s)", me.instanceKey, n, u)
 			} else if oldInstances.Delete(key) {
 				// instance already in cache
 				continue
 			} else if _, err = me.Matrix.NewInstance(key); err != nil {
-				me.Logger.Warn().Msgf("add instance: %v", err)
+				logger.Warn(me.Prefix, "add instance: %v", err)
 			} else {
-				me.Logger.Debug().Msgf("added new instance [%s]", key)
+				logger.Debug(me.Prefix, "added new instance [%s]", key)
 			}
 		}
 	}
 
 	for key := range oldInstances.Iter() {
 		me.Matrix.RemoveInstance(key)
-		me.Logger.Debug().Msgf("removed instance [%s]", key)
+		logger.Debug(me.Prefix, "removed instance [%s]", key)
 	}
 
 	removed = oldInstances.Size()
 	newSize = len(me.Matrix.GetInstances())
 	added = newSize - (oldSize - removed)
 
-	me.Logger.Debug().Msgf("added %d new, removed %d (total instances %d)", added, removed, newSize)
+	logger.Debug(me.Prefix, "added %d new, removed %d (total instances %d)", added, removed, newSize)
 
 	if newSize == 0 {
 		return nil, errors.New(errors.ERR_NO_INSTANCE, "")
diff --git a/cmd/exporters/influxdb/influxdb.go b/cmd/exporters/influxdb/influxdb.go
index 1db4a288..24e7277d 100644
--- a/cmd/exporters/influxdb/influxdb.go
+++ b/cmd/exporters/influxdb/influxdb.go
@@ -9,6 +9,7 @@ import (
 	"goharvest2/cmd/poller/exporter"
 	"goharvest2/pkg/color"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"io/ioutil"
 	"net/http"
@@ -59,7 +60,7 @@ func (e *InfluxDB) Init() error {
 	}
 
 	if port = e.Params.GetChildContentS("port"); port == "" {
-		e.Logger.Debug().Msgf("using default port [%s]", defaultPort)
+		logger.Debug(e.Prefix, "using default port [%s]", defaultPort)
 		port = defaultPort
 	} else if _, err = strconv.Atoi(port); err != nil {
 		return errors.New(errors.INVALID_PARAM, "port")
@@ -68,28 +69,28 @@ func (e *InfluxDB) Init() error {
 	if bucket = e.Params.GetChildContentS("bucket"); bucket == "" {
 		return errors.New(errors.MISSING_PARAM, "bucket")
 	}
-	e.Logger.Debug().Msgf("using bucket [%s]", bucket)
+	logger.Debug(e.Prefix, "using bucket [%s]", bucket)
 
 	if org = e.Params.GetChildContentS("org"); org == "" {
 		return errors.New(errors.MISSING_PARAM, "org")
 	}
-	e.Logger.Debug().Msgf("using organization [%s]", org)
+	logger.Debug(e.Prefix, "using organization [%s]", org)
 
 	if e.token = e.Params.GetChildContentS("token"); e.token == "" {
 		return errors.New(errors.MISSING_PARAM, "token")
 	} else {
-		e.Logger.Debug().Msg("will use authorization with api token")
+		logger.Debug(e.Prefix, "will use authorization with api token")
 	}
 
 	if v = e.Params.GetChildContentS("version"); v == "" {
 		v = defaultApiVersion
 	}
-	e.Logger.Debug().Msgf("using api version [%s]", v)
+	logger.Debug(e.Prefix, "using api version [%s]", v)
 
 	if p = e.Params.GetChildContentS("precision"); p == "" {
 		p = defaultApiPrecision
 	}
-	e.Logger.Debug().Msgf("using api precision [%s]", p)
+	logger.Debug(e.Prefix, "using api precision [%s]", p)
 
 	// timeout parameter
 	timeout := time.Duration(detaultTimeout) * time.Second
@@ -97,20 +98,20 @@ func (e *InfluxDB) Init() error {
 		if t, err := strconv.Atoi(ct); err == nil {
 			timeout = time.Duration(t) * time.Second
 		} else {
-			e.Logger.Warn().Msgf("invalid client_timeout [%s], using default: %d s", ct, detaultTimeout)
+			logger.Warn(e.Prefix, "invalid client_timeout [%s], using default: %d s", ct, detaultTimeout)
 		}
 	} else {
-		e.Logger.Debug().Msgf("using default client_timeout: %d s", detaultTimeout)
+		logger.Debug(e.Prefix, "using default client_timeout: %d s", detaultTimeout)
 	}
 
 	// construct client URL
 	e.url = fmt.Sprintf("http://%s:%s/api/v%s/write?org=%s&bucket=%s&precision=%s", addr, port, v, org, bucket, p)
-	e.Logger.Debug().Msgf("url= [%s]", e.url)
+	logger.Debug(e.Prefix, "url= [%s]", e.url)
 
 	// construct HTTP client
 	e.client = &http.Client{Timeout: timeout}
 
-	e.Logger.Debug().Msgf("initialized exporter, ready to emit to [%s:%s]", addr, port)
+	logger.Debug(e.Prefix, "initialized exporter, ready to emit to [%s:%s]", addr, port)
 	return nil
 }
 
@@ -131,27 +132,27 @@ func (e *InfluxDB) Export(data *matrix.Matrix) error {
 	if metrics, err = e.Render(data); err == nil && len(metrics) != 0 {
 		// fix render time
 		if err = e.Metadata.LazyAddValueInt64("time", "render", time.Since(s).Microseconds()); err != nil {
-			e.Logger.Error().Stack().Err(err).Msg("metadata render time")
+			logger.Error(e.Prefix, "metadata render time: %v", err)
 		}
 		// in debug mode, don't actually export but write to log
 		if e.Options.Debug {
-			e.Logger.Debug().Msg("simulating export since in debug mode")
+			logger.Debug(e.Prefix, "simulating export since in debug mode")
 			for _, m := range metrics {
-				e.Logger.Debug().Msgf("M= [%s%s%s]", color.Blue, m, color.End)
+				logger.Debug(e.Prefix, "M= [%s%s%s]", color.Blue, m, color.End)
 			}
 			return nil
 			// otherwise to the actual export: send to the DB
 		} else if err = e.Emit(metrics); err != nil {
-			e.Logger.Error().Stack().Err(err).Msgf("(%s.%s) --> %s", data.Object, data.UUID)
+			logger.Error(e.Prefix, "(%s.%s) --> %s", data.Object, data.UUID, err.Error())
 			return err
 		}
 	}
 
-	e.Logger.Debug().Msgf("(%s.%s) --> exported %d data points", data.Object, data.UUID, len(metrics))
+	logger.Debug(e.Prefix, "(%s.%s) --> exported %d data points", data.Object, data.UUID, len(metrics))
 
 	// update metadata
 	if err = e.Metadata.LazyAddValueInt64("time", "export", time.Since(s).Microseconds()); err != nil {
-		e.Logger.Error().Stack().Err(err).Msg("metadata export time")
+		logger.Error(e.Prefix, "metadata export time: %v", err)
 	}
 
 	/* skipped for now, since InfluxDB complains about "time" field name
@@ -256,7 +257,7 @@ func (e *InfluxDB) Render(data *matrix.Matrix) ([][]byte, error) {
 
 		// skip instance without key tags
 		if len(m.tag_set) == 0 {
-			e.Logger.Debug().Msgf("skip instance (%s), no tag set parsed from labels (%v)", key, instance.GetLabels().Map())
+			logger.Debug(e.Prefix, "skip instance (%s), no tag set parsed from labels (%v)", key, instance.GetLabels().Map())
 		}
 
 		// field set
@@ -299,26 +300,26 @@ func (e *InfluxDB) Render(data *matrix.Matrix) ([][]byte, error) {
 			countTmp++
 		}
 
-		e.Logger.Trace().Msgf("rendering from: %s", m.String())
+		logger.Trace(e.Prefix, "rendering from: %s", m.String())
 
 		// skip instance with no tag set (no metrics)
 		if len(m.field_set) == 0 {
-			e.Logger.Debug().Msgf("skip instance (%s), no field set parsed", key)
+			logger.Debug(e.Prefix, "skip instance (%s), no field set parsed", key)
 		} else if r, err := m.Render(); err == nil {
 			rendered = append(rendered, []byte(r))
 			//logger.Debug(e.Prefix, "M= [%s%s%s]", color.Blue, r, color.End)
 			count += countTmp
 		} else {
-			e.Logger.Debug().Msg(err.Error())
+			logger.Debug(e.Prefix, err.Error())
 		}
 	}
 
-	e.Logger.Debug().Msgf("rendered %d measurements with %d data points for (%s)", len(rendered), count, object)
+	logger.Debug(e.Prefix, "rendered %d measurements with %d data points for (%s)", len(rendered), count, object)
 
 	// update metadata
 	e.AddExportCount(count)
 	if err := e.Metadata.LazySetValueUint64("count", "export", count); err != nil {
-		e.Logger.Error().Stack().Err(err).Msg("metadata export count")
+		logger.Error(e.Prefix, "metadata export count: %v", err)
 	}
 	return rendered, nil
 }
diff --git a/cmd/exporters/prometheus/httpd.go b/cmd/exporters/prometheus/httpd.go
index b1949081..8c9690c1 100644
--- a/cmd/exporters/prometheus/httpd.go
+++ b/cmd/exporters/prometheus/httpd.go
@@ -10,6 +10,7 @@ package main
 import (
 	"bytes"
 	"fmt"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/set"
 	"net/http"
 	"strings"
@@ -22,13 +23,13 @@ func (me *Prometheus) startHttpD(addr, port string) {
 	mux.HandleFunc("/", me.ServeInfo)
 	mux.HandleFunc("/metrics", me.ServeMetrics)
 
-	me.Logger.Debug().Msgf(" (httpd)", "starting server at [%s:%s]", addr, port)
+	logger.Debug(me.Prefix+" (httpd)", "starting server at [%s:%s]", addr, port)
 	server := &http.Server{Addr: addr + ":" + port, Handler: mux}
 
 	if err := server.ListenAndServe(); err != nil {
-		me.Logger.Fatal().Msgf(" (httpd) %v", err.Error())
+		logger.Fatal(me.Prefix+" (httpd)", err.Error())
 	} else {
-		me.Logger.Info().Msgf(" (httpd)", "listening at [http://%s:%s]", addr, port)
+		logger.Info(me.Prefix+" (httpd)", "listening at [http://%s:%s]", addr, port)
 	}
 }
 
@@ -71,12 +72,12 @@ func (me *Prometheus) checkAddr(addr string) bool {
 // send a deny request response
 func (me *Prometheus) denyAccess(w http.ResponseWriter, r *http.Request) {
 
-	me.Logger.Debug().Msgf(" (httpd) ", "denied request [%s] (%s)", r.RequestURI, r.RemoteAddr)
+	logger.Debug(me.Prefix+" (httpd) ", "denied request [%s] (%s)", r.RequestURI, r.RemoteAddr)
 	w.WriteHeader(403)
 	w.Header().Set("content-type", "text/plain")
 	_, err := w.Write([]byte("403 Forbidden"))
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 }
 
@@ -94,7 +95,7 @@ func (me *Prometheus) ServeMetrics(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	me.Logger.Debug().Msgf(" (httpd) ", "serving request [%s] (%s)", r.RequestURI, r.RemoteAddr)
+	logger.Debug(me.Prefix+" (httpd) ", "serving request [%s] (%s)", r.RequestURI, r.RemoteAddr)
 
 	me.cache.Lock()
 	for _, metrics := range me.cache.Get() {
@@ -109,7 +110,7 @@ func (me *Prometheus) ServeMetrics(w http.ResponseWriter, r *http.Request) {
 		data = append(data, md...)
 		count += len(md)
 	} else {
-		me.Logger.Error().Stack().Err(err).Msg("(httpd) render metadata")
+		logger.Error(me.Prefix+" (httpd) ", "render metadata: %v", err)
 	}
 	/*
 
@@ -123,17 +124,17 @@ func (me *Prometheus) ServeMetrics(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("content-type", "text/plain")
 	_, err := w.Write(bytes.Join(data, []byte("\n")))
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 
 	me.Metadata.Reset()
 	err = me.Metadata.LazySetValueInt64("time", "http", time.Since(start).Microseconds())
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 	err = me.Metadata.LazySetValueInt("count", "http", count)
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 }
 
@@ -151,7 +152,7 @@ func (me *Prometheus) ServeInfo(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	me.Logger.Debug().Msgf(" (httpd)", "serving info request [%s] (%s)", r.RequestURI, r.RemoteAddr)
+	logger.Debug(me.Prefix+" (httpd)", "serving info request [%s] (%s)", r.RequestURI, r.RemoteAddr)
 
 	body := make([]string, 0)
 
@@ -170,16 +171,16 @@ func (me *Prometheus) ServeInfo(w http.ResponseWriter, r *http.Request) {
 	}
 	me.cache.Unlock()
 
-	me.Logger.Debug().Msgf(" (httpd)", "fetching %d cached elements", len(cache))
+	logger.Debug(me.Prefix+" (httpd)", "fetching %d cached elements", len(cache))
 
 	for key, data := range cache {
-		me.Logger.Debug().Msgf(" (httpd)", "key => [%s] (%d)", key, len(data))
+		logger.Debug(me.Prefix+" (httpd)", "key => [%s] (%d)", key, len(data))
 		var collector, object string
 
 		if keys := strings.Split(key, "."); len(keys) == 2 {
 			collector = keys[0]
 			object = keys[1]
-			me.Logger.Debug().Msgf(" (httpd)", "collector [%s] - object [%s]", collector, object)
+			logger.Debug(me.Prefix+" (httpd)", "collector [%s] - object [%s]", collector, object)
 		} else {
 			continue
 		}
@@ -228,11 +229,11 @@ func (me *Prometheus) ServeInfo(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("content-type", "text/html")
 	_, err := w.Write([]byte(body_flat))
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 
 	err = me.Metadata.LazyAddValueInt64("time", "info", time.Since(start).Microseconds())
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 }
diff --git a/cmd/exporters/prometheus/prometheus.go b/cmd/exporters/prometheus/prometheus.go
index 20e2eb88..f332ce0f 100644
--- a/cmd/exporters/prometheus/prometheus.go
+++ b/cmd/exporters/prometheus/prometheus.go
@@ -26,6 +26,7 @@ import (
 	"goharvest2/cmd/poller/exporter"
 	"goharvest2/pkg/color"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/set"
 	"regexp"
@@ -82,7 +83,7 @@ func (me *Prometheus) Init() error {
 	}
 
 	if x := me.Params.GetChildContentS("global_prefix"); x != "" {
-		me.Logger.Debug().Msgf("will use global prefix [%s]", x)
+		logger.Debug(me.Prefix, "will use global prefix [%s]", x)
 		me.globalPrefix = x
 		if !strings.HasSuffix(x, "_") {
 			me.globalPrefix += "_"
@@ -92,7 +93,7 @@ func (me *Prometheus) Init() error {
 	}
 
 	if me.Options.Debug {
-		me.Logger.Debug().Msg("initialized without HTTP server since in debug mode")
+		logger.Debug(me.Prefix, "initialized without HTTP server since in debug mode")
 		return nil
 	}
 
@@ -104,15 +105,15 @@ func (me *Prometheus) Init() error {
 	// all other parameters are only relevant to the HTTP daemon
 	if x := me.Params.GetChildContentS("cache_max_keep"); x != "" {
 		if d, err := time.ParseDuration(x); err == nil {
-			me.Logger.Debug().Msgf("using cache_max_keep [%s]", x)
+			logger.Debug(me.Prefix, "using cache_max_keep [%s]", x)
 			me.cache = newCache(d)
 		} else {
-			me.Logger.Error().Stack().Err(err).Msgf("cache_max_keep [%s]", x)
+			logger.Error(me.Prefix, " cache_max_keep [%s] %v", x, err)
 		}
 	}
 
 	if me.cache == nil {
-		me.Logger.Debug().Msgf("using default cache_max_keep [%s]", cacheMaxKeep)
+		logger.Debug(me.Prefix, "using default cache_max_keep [%s]", cacheMaxKeep)
 		if d, err := time.ParseDuration(cacheMaxKeep); err == nil {
 			me.cache = newCache(d)
 		} else {
@@ -124,11 +125,11 @@ func (me *Prometheus) Init() error {
 	if x := me.Params.GetChildS("allow_addrs"); x != nil {
 		me.allowAddrs = x.GetAllChildContentS()
 		if len(me.allowAddrs) == 0 {
-			me.Logger.Error().Stack().Err(nil).Msg("allow_addrs without any")
+			logger.Error(me.Prefix, "allow_addrs without any")
 			return errors.New(errors.INVALID_PARAM, "allow_addrs")
 		}
 		me.checkAddrs = true
-		me.Logger.Debug().Msgf("added %d plain allow rules", len(me.allowAddrs))
+		logger.Debug(me.Prefix, "added %d plain allow rules", len(me.allowAddrs))
 	}
 
 	// allow access only from addresses matching one of defined regular expressions
@@ -139,16 +140,16 @@ func (me *Prometheus) Init() error {
 			if reg, err := regexp.Compile(r); err == nil {
 				me.allowAddrsRegex = append(me.allowAddrsRegex, reg)
 			} else {
-				me.Logger.Error().Stack().Err(err).Msg("parse regex")
+				logger.Error(me.Prefix, "parse regex: %v", err)
 				return errors.New(errors.INVALID_PARAM, "allow_addrs_regex")
 			}
 		}
 		if len(me.allowAddrsRegex) == 0 {
-			me.Logger.Error().Stack().Err(nil).Msg("allow_addrs_regex without any")
+			logger.Error(me.Prefix, "allow_addrs_regex without any")
 			return errors.New(errors.INVALID_PARAM, "allow_addrs")
 		}
 		me.checkAddrs = true
-		me.Logger.Debug().Msgf("added %d regex allow rules", len(me.allowAddrsRegex))
+		logger.Debug(me.Prefix, "added %d regex allow rules", len(me.allowAddrsRegex))
 	}
 
 	// cache addresses that have been allowed or denied already
@@ -173,7 +174,7 @@ func (me *Prometheus) Init() error {
 	addr := localHttpAddr
 	if x := me.Params.GetChildContentS("local_http_addr"); x != "" {
 		addr = x
-		me.Logger.Debug().Msgf("using custom local addr [%s]", x)
+		logger.Debug(me.Prefix, "using custom local addr [%s]", x)
 	}
 
 	go me.startHttpD(addr, port)
@@ -181,7 +182,7 @@ func (me *Prometheus) Init() error {
 	// @TODO: implement error checking to enter failed state if HTTPd failed
 	// (like we did in Alpha)
 
-	me.Logger.Debug().Msgf("initialized, HTTP daemon started at [http://%s:%s]", addr, port)
+	logger.Debug(me.Prefix, "initialized, HTTP daemon started at [http://%s:%s]", addr, port)
 
 	return nil
 }
@@ -205,7 +206,7 @@ func (me *Prometheus) Export(data *matrix.Matrix) error {
 	me.Lock()
 	defer me.Unlock()
 
-	me.Logger.Trace().Msgf("incoming %s%s(%s) (%s)%s", color.Bold, color.Cyan, data.UUID, data.Object, color.End)
+	logger.Trace(me.Prefix, "incoming %s%s(%s) (%s)%s", color.Bold, color.Cyan, data.UUID, data.Object, color.End)
 
 	// render metrics into Prometheus format
 	start := time.Now()
@@ -217,9 +218,9 @@ func (me *Prometheus) Export(data *matrix.Matrix) error {
 
 	// simulate export in debug mode
 	if me.Options.Debug {
-		me.Logger.Debug().Msg("no export since in debug mode")
+		logger.Debug(me.Prefix, "no export since in debug mode")
 		for _, m := range metrics {
-			me.Logger.Debug().Msgf("M= %s", string(m))
+			logger.Debug(me.Prefix, "M= %s", string(m))
 		}
 		return nil
 	}
@@ -231,17 +232,17 @@ func (me *Prometheus) Export(data *matrix.Matrix) error {
 	me.cache.Lock()
 	me.cache.Put(key, metrics)
 	me.cache.Unlock()
-	me.Logger.Debug().Msgf("added to cache with key [%s%s%s%s]", color.Bold, color.Red, key, color.End)
+	logger.Debug(me.Prefix, "added to cache with key [%s%s%s%s]", color.Bold, color.Red, key, color.End)
 
 	// update metadata
 	me.AddExportCount(uint64(len(metrics)))
 	err = me.Metadata.LazyAddValueInt64("time", "render", d.Microseconds())
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 	err = me.Metadata.LazyAddValueInt64("time", "export", time.Since(start).Microseconds())
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("error")
+		logger.Error(me.Prefix, "error: %v", err)
 	}
 
 	return nil
@@ -283,12 +284,12 @@ func (me *Prometheus) render(data *matrix.Matrix) ([][]byte, error) {
 
 	if x := options.GetChildS("instance_labels"); x != nil {
 		labels_to_include = x.GetAllChildContentS()
-		me.Logger.Debug().Msgf("requested instance_labels : %v", labels_to_include)
+		logger.Debug(me.Prefix, "requested instance_labels : %v", labels_to_include)
 	}
 
 	if x := options.GetChildS("instance_keys"); x != nil {
 		keys_to_include = x.GetAllChildContentS()
-		me.Logger.Debug().Msgf("requested keys_labels : %v", keys_to_include)
+		logger.Debug(me.Prefix, "requested keys_labels : %v", keys_to_include)
 	}
 
 	if options.GetChildContentS("include_all_labels") == "true" {
@@ -306,11 +307,11 @@ func (me *Prometheus) render(data *matrix.Matrix) ([][]byte, error) {
 	for key, instance := range data.GetInstances() {
 
 		if !instance.IsExportable() {
-			me.Logger.Trace().Msgf("skip instance [%s]: disabled for export", key)
+			logger.Trace(me.Prefix, "skip instance [%s]: disabled for export", key)
 			continue
 		}
 
-		me.Logger.Trace().Msgf("rendering instance [%s] (%v)", key, instance.GetLabels())
+		logger.Trace(me.Prefix, "rendering instance [%s] (%v)", key, instance.GetLabels())
 
 		instance_keys := make([]string, len(global_labels))
 		instance_labels := make([]string, 0)
@@ -326,18 +327,18 @@ func (me *Prometheus) render(data *matrix.Matrix) ([][]byte, error) {
 				if value != "" {
 					instance_keys = append(instance_keys, fmt.Sprintf("%s=\"%s\"", key, value))
 				}
-				me.Logger.Trace().Msgf("++ key [%s] (%s) found=%v", key, value, value != "")
+				logger.Trace(me.Prefix, "++ key [%s] (%s) found=%v", key, value, value != "")
 			}
 
 			for _, label := range labels_to_include {
 				value := instance.GetLabel(label)
 				instance_labels = append(instance_labels, fmt.Sprintf("%s=\"%s\"", label, value))
-				me.Logger.Trace().Msgf("++ label [%s] (%s) %t", label, value, value != "")
+				logger.Trace(me.Prefix, "++ label [%s] (%s) %t", label, value, value != "")
 			}
 
 			// @TODO, probably be strict, and require all keys to be present
 			if len(instance_keys) == 0 && options.GetChildContentS("require_instance_keys") != "False" {
-				me.Logger.Trace().Msgf("skip instance, no keys parsed (%v) (%v)", instance_keys, instance_labels)
+				logger.Trace(me.Prefix, "skip instance, no keys parsed (%v) (%v)", instance_keys, instance_labels)
 				continue
 			}
 
@@ -346,18 +347,18 @@ func (me *Prometheus) render(data *matrix.Matrix) ([][]byte, error) {
 				label_data := fmt.Sprintf("%s_labels{%s,%s} 1.0", prefix, strings.Join(instance_keys, ","), strings.Join(instance_labels, ","))
 				rendered = append(rendered, []byte(label_data))
 			} else {
-				me.Logger.Trace().Msgf("skip instance labels, no labels parsed (%v) (%v)", instance_keys, instance_labels)
+				logger.Trace(me.Prefix, "skip instance labels, no labels parsed (%v) (%v)", instance_keys, instance_labels)
 			}
 		}
 
 		for mkey, metric := range data.GetMetrics() {
 
 			if !metric.IsExportable() {
-				me.Logger.Debug().Msgf("skip metric [%s]: disabled for export", mkey)
+				logger.Debug(me.Prefix, "skip metric [%s]: disabled for export", mkey)
 				continue
 			}
 
-			me.Logger.Trace().Msgf("rendering metric [%s]", mkey)
+			logger.Trace(me.Prefix, "rendering metric [%s]", mkey)
 
 			if value, ok := metric.GetValueString(instance); ok {
 
@@ -389,11 +390,11 @@ func (me *Prometheus) render(data *matrix.Matrix) ([][]byte, error) {
 					rendered = append(rendered, []byte(x))
 				}
 			} else {
-				me.Logger.Trace().Msg("skipped: no data value")
+				logger.Trace(me.Prefix, "skipped: no data value")
 			}
 		}
 	}
-	me.Logger.Debug().Msgf("rendered %d data points from %d (%s) instances", len(rendered), len(data.GetInstances()), data.Object)
+	logger.Debug(me.Prefix, "rendered %d data points from %d (%s) instances", len(rendered), len(data.GetInstances()), data.Object)
 	return rendered, nil
 }
 
diff --git a/cmd/harvest/harvest.go b/cmd/harvest/harvest.go
index 9d0c6924..30813f66 100644
--- a/cmd/harvest/harvest.go
+++ b/cmd/harvest/harvest.go
@@ -94,7 +94,9 @@ func doManageCmd(cmd *cobra.Command, args []string) {
 		os.Exit(1)
 	}
 
-	HarvestPidPath = conf.GetHarvestPidPath()
+	if HarvestPidPath = os.Getenv("HARVEST_PIDS"); HarvestPidPath == "" {
+		HarvestPidPath = "/var/run/harvest/"
+	}
 
 	if opts.verbose {
 		_ = cmd.Flags().Set("loglevel", "1")
diff --git a/cmd/poller/collector/collector.go b/cmd/poller/collector/collector.go
index 273e4150..d7acb9ca 100644
--- a/cmd/poller/collector/collector.go
+++ b/cmd/poller/collector/collector.go
@@ -17,7 +17,6 @@ package collector
 
 import (
 	"goharvest2/pkg/conf"
-	"goharvest2/pkg/logging"
 	"path"
 	"reflect"
 	"strconv"
@@ -27,6 +26,7 @@ import (
 
 	"goharvest2/pkg/dload"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 
@@ -78,7 +78,7 @@ var CollectorStatus = [3]string{
 type AbstractCollector struct {
 	Name    string           // name of the collector, CamelCased
 	Object  string           // object of the collector, describes what thet collector is collecting
-	Logger  *logging.Logger  // logger used for logging
+	Prefix  string           // prefix used for logging
 	Status  uint8            // current state of th
 	Message string           // reason if collector is in failed state
 	Options *options.Options // poller options
@@ -105,10 +105,11 @@ func New(name, object string, options *options.Options, params *node.Node) *Abst
 		Name:     name,
 		Object:   object,
 		Options:  options,
-		Logger:   logging.SubLogger("collector", name+":"+object),
 		Params:   params,
 		countMux: &sync.Mutex{},
 	}
+	c.Prefix = "(collector) (" + name + ":" + object + ")"
+
 	return &c
 }
 
@@ -269,7 +270,7 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 			if err != nil {
 
 				if !me.Schedule.IsStandBy() {
-					me.Logger.Debug().Msgf("handling error during [%s] poll...", task.Name)
+					logger.Debug(me.Prefix, "handling error during [%s] poll...", task.Name)
 				}
 				switch {
 				// target system is unreachable
@@ -280,7 +281,7 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 					}
 					if !me.Schedule.IsStandBy() {
 						//logger.Error(me.Prefix, err.Error())
-						me.Logger.Warn().Msgf("target unreachable, entering standby mode (retry to connect in %d s)", retryDelay)
+						logger.Warn(me.Prefix, "target unreachable, entering standby mode (retry to connect in %d s)", retryDelay)
 					}
 					me.Schedule.SetStandByMode(task, time.Duration(retryDelay)*time.Second)
 					me.SetStatus(1, errors.ERR_CONNECTION)
@@ -288,15 +289,15 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 				case errors.IsErr(err, errors.ERR_NO_INSTANCE):
 					me.Schedule.SetStandByMode(task, 5*time.Minute)
 					me.SetStatus(1, errors.ERR_NO_INSTANCE)
-					me.Logger.Info().Msgf("no [%s] instances on system, entering standby mode", me.Object)
+					logger.Info(me.Prefix, "no [%s] instances on system, entering standby mode", me.Object)
 				// no metrics available
 				case errors.IsErr(err, errors.ERR_NO_METRIC):
 					me.SetStatus(1, errors.ERR_NO_METRIC)
 					me.Schedule.SetStandByMode(task, 1*time.Hour)
-					me.Logger.Warn().Msgf("no [%s] metrics on system, entering standby mode", me.Object)
+					logger.Warn(me.Prefix, "no [%s] metrics on system, entering standby mode", me.Object)
 				// not an error we are expecting, so enter failed state and terminate
 				default:
-					me.Logger.Error().Stack().Err(err).Msg("")
+					logger.Error(me.Prefix, err.Error())
 					if errmsg := errors.GetClass(err); errmsg != "" {
 						me.SetStatus(2, errmsg)
 					} else {
@@ -310,7 +311,7 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 				// recover from standby mode
 				me.Schedule.Recover()
 				me.SetStatus(0, "running")
-				me.Logger.Info().Msg("recovered from standby mode, back to normal schedule")
+				logger.Info(me.Prefix, "recovered from standby mode, back to normal schedule")
 			}
 
 			if data != nil {
@@ -323,12 +324,12 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 
 					for _, plg := range me.Plugins {
 						if pluginData, err := plg.Run(data); err != nil {
-							me.Logger.Error().Stack().Err(err).Msgf("plugin [%s]: ", plg.GetName())
+							logger.Error(me.Prefix, "plugin [%s]: %s", plg.GetName(), err.Error())
 						} else if pluginData != nil {
 							results = append(results, pluginData...)
-							me.Logger.Debug().Msgf("plugin [%s] added (%d) data", plg.GetName(), len(pluginData))
+							logger.Debug(me.Prefix, "plugin [%s] added (%d) data", plg.GetName(), len(pluginData))
 						} else {
-							me.Logger.Debug().Msgf("plugin [%s]: completed", plg.GetName())
+							logger.Debug(me.Prefix, "plugin [%s]: completed", plg.GetName())
 						}
 					}
 
@@ -349,37 +350,37 @@ func (me *AbstractCollector) Start(wg *sync.WaitGroup) {
 
 		// pass results to exporters
 
-		me.Logger.Debug().Msgf("exporting collected (%d) data", len(results))
+		logger.Debug(me.Prefix, "exporting collected (%d) data", len(results))
 
 		// @TODO better handling when exporter is standby/failed state
 		for _, e := range me.Exporters {
 			if code, status, reason := e.GetStatus(); code != 0 {
-				me.Logger.Warn().Msgf("exporter [%s] down (%d - %s) (%s), skip export", e.GetName(), code, status, reason)
+				logger.Warn(me.Prefix, "exporter [%s] down (%d - %s) (%s), skip export", e.GetName(), code, status, reason)
 				continue
 			}
 
 			if err := e.Export(me.Metadata); err != nil {
-				me.Logger.Warn().Msgf("export metadata to [%s]: %s", e.GetName(), err.Error())
+				logger.Warn(me.Prefix, "export metadata to [%s]: %s", e.GetName(), err.Error())
 			}
 
 			// continue if metadata failed, since it might be specific to metadata
 			for _, data := range results {
 				if data.IsExportable() {
 					if err := e.Export(data); err != nil {
-						me.Logger.Error().Stack().Err(err).Msgf("export data to [%s]:", e.GetName())
+						logger.Error(me.Prefix, "export data to [%s]: %s", e.GetName(), err.Error())
 						break
 					}
 				} else {
-					me.Logger.Debug().Msgf("skipped data (%s) (%s) - set non-exportable", data.UUID, data.Object)
+					logger.Debug(me.Prefix, "skipped data (%s) (%s) - set non-exportable", data.UUID, data.Object)
 				}
 			}
 		}
 
 		if nd := me.Schedule.NextDue(); nd > 0 {
-			me.Logger.Debug().Msgf("sleeping %s until next poll", nd.String()) //DEBUG
+			logger.Debug(me.Prefix, "sleeping %s until next poll", nd.String()) //DEBUG
 			me.Schedule.Sleep()
 		} else if nd.Milliseconds() > -50 { // avoid warning for small delays
-			me.Logger.Warn().Msgf("lagging behind schedule %s", (-nd).String())
+			logger.Warn(me.Prefix, "lagging behind schedule %s", (-nd).String())
 		}
 	}
 }
@@ -457,7 +458,7 @@ func (me *AbstractCollector) SetMetadata(m *matrix.Matrix) {
 func (me *AbstractCollector) WantedExporters(configFp string) []string {
 	names, err := conf.GetUniqueExporters(me.Params, configFp)
 	if err != nil {
-		me.Logger.Error().Stack().Err(err).Msg("Error while fetching exporters")
+		logger.Error(me.Prefix, "Error while fetching exporters %v", err)
 	}
 	return names
 }
@@ -487,7 +488,7 @@ func (me *AbstractCollector) LoadPlugins(params *node.Node) error {
 
 		// case 1: available as built-in plugin
 		if p = getBuiltinPlugin(name, abc); p != nil {
-			me.Logger.Debug().Msgf("loaded built-in plugin [%s]", name)
+			logger.Debug(me.Prefix, "loaded built-in plugin [%s]", name)
 			// case 2: available as dynamic plugin
 		} else {
 			binpath := path.Join(me.Options.HomePath, "bin", "plugins", strings.ToLower(me.Name))
@@ -503,15 +504,15 @@ func (me *AbstractCollector) LoadPlugins(params *node.Node) error {
 				return errors.New(errors.ERR_DLOAD, name+": New()")
 			}
 			p = NewFunc(abc)
-			me.Logger.Debug().Msgf("loaded dynamic plugin [%s]", name)
+			logger.Debug(me.Prefix, "loaded dynamic plugin [%s]", name)
 		}
 
 		if err := p.Init(); err != nil {
-			me.Logger.Error().Stack().Err(err).Msgf("init plugin [%s]:", name)
+			logger.Error(me.Prefix, "init plugin [%s]: %v", name, err)
 			return err
 		}
 		me.Plugins = append(me.Plugins, p)
 	}
-	me.Logger.Debug().Msgf("initialized %d plugins", len(me.Plugins))
+	logger.Debug(me.Prefix, "initialized %d plugins", len(me.Plugins))
 	return nil
 }
diff --git a/cmd/poller/collector/helpers.go b/cmd/poller/collector/helpers.go
index 7f1ac929..3d0fe07f 100644
--- a/cmd/poller/collector/helpers.go
+++ b/cmd/poller/collector/helpers.go
@@ -22,6 +22,7 @@ import (
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/cmd/poller/plugin/aggregator"
 	"goharvest2/cmd/poller/plugin/label_agent"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/tree"
 	"goharvest2/pkg/tree/node"
 )
@@ -58,7 +59,7 @@ func (c *AbstractCollector) ImportSubTemplate(model, filename string, version [3
 	)
 
 	pathPrefix = path.Join(c.Options.HomePath, "conf/", strings.ToLower(c.Name), model)
-	c.Logger.Debug().Msgf("Looking for best-fitting template in [%s]", pathPrefix)
+	logger.Debug(c.Prefix, "Looking for best-fitting template in [%s]", pathPrefix)
 
 	// check for available versons, those are the subdirectories that include filename
 	availableVersions = make(map[string]bool)
@@ -68,7 +69,7 @@ func (c *AbstractCollector) ImportSubTemplate(model, filename string, version [3
 				if templates, err := ioutil.ReadDir(path.Join(pathPrefix, file.Name())); err == nil {
 					for _, t := range templates {
 						if t.Name() == filename {
-							c.Logger.Trace().Msgf("available version dir: [%s]", file.Name())
+							logger.Trace(c.Prefix, "available version dir: [%s]", file.Name())
 							availableVersions[file.Name()] = true
 							break
 						}
@@ -79,7 +80,7 @@ func (c *AbstractCollector) ImportSubTemplate(model, filename string, version [3
 	} else {
 		return nil, err
 	}
-	c.Logger.Trace().Msgf("checking for %d available versions: %v", len(availableVersions), availableVersions)
+	logger.Trace(c.Prefix, "checking for %d available versions: %v", len(availableVersions), availableVersions)
 
 	versionDecimal = version[0]*100 + version[1]*10 + version[2]
 
@@ -103,7 +104,7 @@ func (c *AbstractCollector) ImportSubTemplate(model, filename string, version [3
 	}
 
 	subTemplateFp = path.Join(pathPrefix, selectedVersion, filename)
-	c.Logger.Debug().Msgf("selected best-fitting subtemplate [%s]", subTemplateFp)
+	logger.Debug(c.Prefix, "selected best-fitting subtemplate [%s]", subTemplateFp)
 	return tree.Import("yaml", subTemplateFp)
 }
 
diff --git a/cmd/poller/exporter/exporter.go b/cmd/poller/exporter/exporter.go
index 84e344b5..af98e300 100644
--- a/cmd/poller/exporter/exporter.go
+++ b/cmd/poller/exporter/exporter.go
@@ -9,7 +9,6 @@ package exporter
 
 import (
 	"goharvest2/cmd/poller/options"
-	"goharvest2/pkg/logging"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"strconv"
@@ -42,7 +41,7 @@ var ExporterStatus = [3]string{
 type AbstractExporter struct {
 	Class       string
 	Name        string
-	Logger      *logging.Logger // logger used for logging
+	Prefix      string
 	Status      uint8
 	Message     string
 	Options     *options.Options
@@ -64,7 +63,7 @@ func New(c, n string, o *options.Options, p *node.Node) *AbstractExporter {
 		Name:     n,
 		Options:  o,
 		Params:   p,
-		Logger:   logging.SubLogger("exporter", n),
+		Prefix:   "(exporter) (" + n + ")",
 		Mutex:    &sync.Mutex{},
 		countMux: &sync.Mutex{},
 	}
diff --git a/cmd/poller/options/options.go b/cmd/poller/options/options.go
index 366d65fb..b332f1e9 100644
--- a/cmd/poller/options/options.go
+++ b/cmd/poller/options/options.go
@@ -68,7 +68,10 @@ func SetPathsAndHostname(args *Options) {
 
 	args.HomePath = conf.GetHarvestHomePath()
 
-	args.LogPath = conf.GetHarvestLogPath()
-
-	args.PidPath = conf.GetHarvestPidPath()
+	if args.LogPath = os.Getenv("HARVEST_LOGS"); args.LogPath == "" {
+		args.LogPath = "/var/log/harvest/"
+	}
+	if args.PidPath = os.Getenv("HARVEST_PIDS"); args.PidPath == "" {
+		args.PidPath = "/var/run/harvest/"
+	}
 }
diff --git a/cmd/poller/plugin/aggregator/aggregator.go b/cmd/poller/plugin/aggregator/aggregator.go
index 5ccdf084..fd2d4122 100644
--- a/cmd/poller/plugin/aggregator/aggregator.go
+++ b/cmd/poller/plugin/aggregator/aggregator.go
@@ -7,6 +7,7 @@ package aggregator
 import (
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"regexp"
 	"strings"
@@ -44,9 +45,9 @@ func (me *Aggregator) Init() error {
 	}
 
 	if len(me.rules) == 1 {
-		me.Logger.Debug().Msg("parsed 1 aggregation rule")
+		logger.Debug(me.Prefix, "parsed 1 aggregation rule")
 	} else {
-		me.Logger.Debug().Msgf("parsed %d aggregation rules", len(me.rules))
+		logger.Debug(me.Prefix, "parsed %d aggregation rules", len(me.rules))
 	}
 	return nil
 }
@@ -57,14 +58,14 @@ func (me *Aggregator) parseRules() error {
 
 	for _, line := range me.Params.GetAllChildContentS() {
 
-		me.Logger.Trace().Msgf("parsing raw rule: [%s]", line)
+		logger.Trace(me.Prefix, "parsing raw rule: [%s]", line)
 
 		r := rule{}
 
 		fields := strings.Fields(line)
 		if len(fields) == 2 || len(fields) == 1 {
 			// parse label, possibly followed by value and object
-			me.Logger.Trace().Msgf("handling first field: [%s]", fields[0])
+			logger.Trace(me.Prefix, "handling first field: [%s]", fields[0])
 			prefix := strings.SplitN(fields[0], "<", 2)
 			r.label = strings.TrimSpace(prefix[0])
 			if len(prefix) == 2 {
@@ -82,10 +83,10 @@ func (me *Aggregator) parseRules() error {
 				if strings.HasPrefix(value, "`") {
 					value = strings.TrimPrefix(strings.TrimSuffix(value, "`"), "`")
 					if r.checkRegex, err = regexp.Compile(value); err != nil {
-						me.Logger.Error().Stack().Err(err).Msgf("rule [%s]: compile regex:", line)
+						logger.Error(me.Prefix, "rule [%s]: compile regex: %v", line, err)
 						return err
 					} else {
-						me.Logger.Trace().Msgf("parsed regex: [%s]", r.checkRegex.String())
+						logger.Trace(me.Prefix, "parsed regex: [%s]", r.checkRegex.String())
 					}
 				} else if value != "" {
 					r.checkValue = value
@@ -96,7 +97,7 @@ func (me *Aggregator) parseRules() error {
 				}
 			}
 			if len(fields) == 2 {
-				me.Logger.Trace().Msgf("handling second field: [%s]", fields[1])
+				logger.Trace(me.Prefix, "handling second field: [%s]", fields[1])
 				if strings.TrimSpace(fields[1]) == "..." {
 					r.allLabels = true
 				} else {
@@ -104,9 +105,9 @@ func (me *Aggregator) parseRules() error {
 				}
 			}
 			me.rules = append(me.rules, &r)
-			me.Logger.Debug().Msgf("parsed rule [%v]", r)
+			logger.Debug(me.Prefix, "parsed rule [%v]", r)
 		} else {
-			me.Logger.Warn().Msgf("invalid rule syntax [%s]", line)
+			logger.Warn(me.Prefix, "invalid rule syntax [%s]", line)
 			return errors.New(errors.INVALID_PARAM, "invalid rule")
 		}
 	}
@@ -145,18 +146,18 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 
 	for _, instance := range data.GetInstances() {
 
-		me.Logger.Trace().Msgf("handling instance with labels [%s]", instance.GetLabels().String())
+		logger.Trace(me.Prefix, "handling instance with labels [%s]", instance.GetLabels().String())
 
 		for i, rule := range me.rules {
 
-			me.Logger.Trace().Msgf("handling rule [%v]", rule)
+			logger.Trace(me.Prefix, "handling rule [%v]", rule)
 			if obj_name = instance.GetLabel(rule.label); obj_name == "" {
-				me.Logger.Warn().Msgf("label name for [%s] missing, skipped", rule.label)
+				logger.Warn(me.Prefix, "label name for [%s] missing, skipped", rule.label)
 				continue
 			}
 
 			if rule.checkLabel != "" {
-				me.Logger.Trace().Msgf("checking label (%s => %s)....", rule.checkLabel, rule.checkValue)
+				logger.Trace(me.Prefix, "checking label (%s => %s)....", rule.checkLabel, rule.checkValue)
 				if rule.checkRegex != nil {
 					if !rule.checkRegex.MatchString(instance.GetLabel(rule.checkLabel)) {
 						continue
@@ -176,7 +177,7 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 			} else {
 				obj_key = obj_name
 			}
-			me.Logger.Trace().Msgf("instance (%s= %s): formatted key [%s]", rule.label, obj_name, obj_key)
+			logger.Trace(me.Prefix, "instance (%s= %s): formatted key [%s]", rule.label, obj_name, obj_key)
 
 			if obj_instance = matrices[i].GetInstance(obj_key); obj_instance == nil {
 				rule.counts[obj_key] = make(map[string]int)
@@ -202,7 +203,7 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				}
 
 				if obj_metric = matrices[i].GetMetric(key); obj_metric == nil {
-					me.Logger.Warn().Msgf("metric [%s] not found in [%s] cache", key, rule.label)
+					logger.Warn(me.Prefix, "metric [%s] not found in [%s] cache", key, rule.label)
 					continue
 				}
 
@@ -210,7 +211,7 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				//obj_metric.Print()
 
 				if err = obj_metric.AddValueFloat64(obj_instance, value); err != nil {
-					me.Logger.Error().Stack().Err(err).Msgf("add value [%s] [%s]:", key, obj_name)
+					logger.Error(me.Prefix, "add value [%s] [%s]: %v", key, obj_name, err)
 				}
 
 				rule.counts[obj_key][key]++
@@ -243,7 +244,7 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				continue
 			}
 
-			me.Logger.Debug().Msgf("[%s] (%s) normalizing values as average", mk, mn)
+			logger.Debug(me.Prefix, "[%s] (%s) normalizing values as average", mk, mn)
 
 			for key, instance := range m.GetInstances() {
 
@@ -256,7 +257,7 @@ func (me *Aggregator) Run(data *matrix.Matrix) ([]*matrix.Matrix, error) {
 				}
 
 				if err = metric.SetValueFloat64(instance, value/float64(count)); err != nil {
-					me.Logger.Error().Stack().Err(err).Msgf("set value [%s] [%s]:", mn, key)
+					logger.Error(me.Prefix, "set value [%s] [%s]: %v", mn, key, err)
 				}
 			}
 		}
diff --git a/cmd/poller/plugin/label_agent/label_agent.go b/cmd/poller/plugin/label_agent/label_agent.go
index c90786ef..bb18574a 100644
--- a/cmd/poller/plugin/label_agent/label_agent.go
+++ b/cmd/poller/plugin/label_agent/label_agent.go
@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"goharvest2/cmd/poller/plugin"
 	"goharvest2/pkg/errors"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"strings"
 )
@@ -45,7 +46,7 @@ func (me *LabelAgent) Init() error {
 	if count = me.parseRules(); count == 0 {
 		err = errors.New(errors.MISSING_PARAM, "valid rules")
 	} else {
-		me.Logger.Debug().Msgf("parsed %d rules for %d actions", count, len(me.actions))
+		logger.Debug(me.Prefix, "parsed %d rules for %d actions", count, len(me.actions))
 	}
 
 	return err
@@ -75,7 +76,7 @@ func (me *LabelAgent) splitSimple(instance *matrix.Instance) {
 			for i := range r.targets {
 				if r.targets[i] != "" && values[i] != "" {
 					instance.SetLabel(r.targets[i], values[i])
-					me.Logger.Trace().Msgf("splitSimple: (%s) [%s] => (%s) [%s]", r.source, instance.GetLabel(r.source), r.targets[i], values[i])
+					logger.Trace(me.Prefix, "splitSimple: (%s) [%s] => (%s) [%s]", r.source, instance.GetLabel(r.source), r.targets[i], values[i])
 				}
 			}
 		}
@@ -89,7 +90,7 @@ func (me *LabelAgent) splitRegex(instance *matrix.Instance) {
 			for i := range r.targets {
 				if r.targets[i] != "" && m[i+1] != "" {
 					instance.SetLabel(r.targets[i], m[i+1])
-					me.Logger.Trace().Msgf("splitRegex: (%s) [%s] => (%s) [%s]", r.source, instance.GetLabel(r.source), r.targets[i], m[i+1])
+					logger.Trace(me.Prefix, "splitRegex: (%s) [%s] => (%s) [%s]", r.source, instance.GetLabel(r.source), r.targets[i], m[i+1])
 				}
 			}
 		}
@@ -121,7 +122,7 @@ func (me *LabelAgent) joinSimple(instance *matrix.Instance) {
 		}
 		if len(values) != 0 {
 			instance.SetLabel(r.target, strings.Join(values, r.sep))
-			me.Logger.Trace().Msgf("joinSimple: (%v) => (%s) [%s]", r.sources, r.target, instance.GetLabel(r.target))
+			logger.Trace(me.Prefix, "joinSimple: (%v) => (%s) [%s]", r.sources, r.target, instance.GetLabel(r.target))
 		}
 	}
 }
@@ -132,7 +133,7 @@ func (me *LabelAgent) replaceSimple(instance *matrix.Instance) {
 		if old := instance.GetLabel(r.source); old != "" {
 			if value := strings.ReplaceAll(old, r.old, r.new); value != old {
 				instance.SetLabel(r.target, value)
-				me.Logger.Trace().Msgf("replaceSimple: (%s) [%s] => (%s) [%s]", r.source, old, r.target, value)
+				logger.Trace(me.Prefix, "replaceSimple: (%s) [%s] => (%s) [%s]", r.source, old, r.target, value)
 			}
 		}
 	}
@@ -143,22 +144,22 @@ func (me *LabelAgent) replaceRegex(instance *matrix.Instance) {
 	for _, r := range me.replaceRegexRules {
 		old := instance.GetLabel(r.source)
 		if m := r.reg.FindStringSubmatch(old); m != nil {
-			me.Logger.Trace().Msgf("replaceRegex: (%d) matches= %v", len(m)-1, m[1:])
+			logger.Trace(me.Prefix, "replaceRegex: (%d) matches= %v", len(m)-1, m[1:])
 			s := make([]interface{}, 0)
 			for _, i := range r.indices {
 				if i < len(m)-1 {
 					s = append(s, m[i+1])
-					me.Logger.Trace().Msgf("substring [%d] = (%s)", i, m[i+1])
+					logger.Trace(me.Prefix, "substring [%d] = (%s)", i, m[i+1])
 				} else {
 					// probably we need to throw warning
 					s = append(s, "")
-					me.Logger.Trace().Msgf("substring [%d] = no match!", i)
+					logger.Trace(me.Prefix, "substring [%d] = no match!", i)
 				}
 			}
-			me.Logger.Trace().Msgf("replaceRegex: (%d) substitution strings= %v", len(s), s)
+			logger.Trace(me.Prefix, "replaceRegex: (%d) substitution strings= %v", len(s), s)
 			if value := fmt.Sprintf(r.format, s...); value != "" && value != old {
 				instance.SetLabel(r.target, value)
-				me.Logger.Trace().Msgf("replaceRegex: (%s) [%s] => (%s) [%s]", r.source, old, r.target, value)
+				logger.Trace(me.Prefix, "replaceRegex: (%s) [%s] => (%s) [%s]", r.source, old, r.target, value)
 			}
 		}
 	}
@@ -169,7 +170,7 @@ func (me *LabelAgent) excludeEquals(instance *matrix.Instance) {
 	for _, r := range me.excludeEqualsRules {
 		if instance.GetLabel(r.label) == r.value {
 			instance.SetExportable(false)
-			me.Logger.Trace().Msgf("excludeEquals: (%s) [%s] instance with labels [%s] => excluded", r.label, r.value, instance.GetLabels().String())
+			logger.Trace(me.Prefix, "excludeEquals: (%s) [%s] instance with labels [%s] => excluded", r.label, r.value, instance.GetLabels().String())
 			break
 		}
 	}
@@ -180,7 +181,7 @@ func (me *LabelAgent) excludeContains(instance *matrix.Instance) {
 	for _, r := range me.excludeContainsRules {
 		if strings.Contains(instance.GetLabel(r.label), r.value) {
 			instance.SetExportable(false)
-			me.Logger.Trace().Msgf("excludeContains: (%s) [%s] instance with labels [%s] => excluded", r.label, r.value, instance.GetLabels().String())
+			logger.Trace(me.Prefix, "excludeContains: (%s) [%s] instance with labels [%s] => excluded", r.label, r.value, instance.GetLabels().String())
 			break
 		}
 	}
@@ -191,7 +192,7 @@ func (me *LabelAgent) excludeRegex(instance *matrix.Instance) {
 	for _, r := range me.excludeRegexRules {
 		if r.reg.MatchString(instance.GetLabel(r.label)) {
 			instance.SetExportable(false)
-			me.Logger.Trace().Msgf("excludeEquals: (%s) [%s] instance with labels [%s] => excluded", r.label, r.reg.String(), instance.GetLabels().String())
+			logger.Trace(me.Prefix, "excludeEquals: (%s) [%s] instance with labels [%s] => excluded", r.label, r.reg.String(), instance.GetLabels().String())
 			break
 		}
 	}
@@ -208,7 +209,7 @@ func (me *LabelAgent) mapValues(m *matrix.Matrix) error {
 
 		if metric = m.GetMetric(r.metric); metric == nil {
 			if metric, err = m.NewMetricUint8(r.metric); err != nil {
-				me.Logger.Error().Stack().Err(err).Msgf("valueMapping: new metric [%s]:", r.metric)
+				logger.Error(me.Prefix, "valueMapping: new metric [%s]: %v", r.metric, err)
 				return err
 			} else {
 				metric.SetProperty("mapping")
@@ -218,10 +219,10 @@ func (me *LabelAgent) mapValues(m *matrix.Matrix) error {
 		for key, instance := range m.GetInstances() {
 			if v, ok := r.mapping[instance.GetLabel(r.label)]; ok {
 				metric.SetValueUint8(instance, v)
-				me.Logger.Trace().Msgf("valueMapping: [%s] [%s] mapped (%s) value to %d", r.metric, key, instance.GetLabel(r.label), v)
+				logger.Trace(me.Prefix, "valueMapping: [%s] [%s] mapped (%s) value to %d", r.metric, key, instance.GetLabel(r.label), v)
 			} else if r.hasDefault {
 				metric.SetValueUint8(instance, r.defaultValue)
-				me.Logger.Trace().Msgf("valueMapping: [%s] [%s] mapped (%s) value to default %d", r.metric, key, instance.GetLabel(r.label), r.defaultValue)
+				logger.Trace(me.Prefix, "valueMapping: [%s] [%s] mapped (%s) value to default %d", r.metric, key, instance.GetLabel(r.label), r.defaultValue)
 			}
 		}
 	}
diff --git a/cmd/poller/plugin/label_agent/parse_rules.go b/cmd/poller/plugin/label_agent/parse_rules.go
index 1cd5fd69..0e21d403 100644
--- a/cmd/poller/plugin/label_agent/parse_rules.go
+++ b/cmd/poller/plugin/label_agent/parse_rules.go
@@ -5,6 +5,7 @@
 package label_agent
 
 import (
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"regexp"
 	"strconv"
@@ -51,7 +52,7 @@ func (me *LabelAgent) parseRules() int {
 		case "value_mapping":
 			me.parseValueMappingRule(rule)
 		default:
-			me.Logger.Warn().Msgf("unknown rule (%s)", name)
+			logger.Warn(me.Prefix, "unknown rule (%s)", name)
 		}
 	}
 
@@ -122,18 +123,18 @@ type splitSimpleRule struct {
 func (me *LabelAgent) parseSplitSimpleRule(rule string) {
 	if fields := strings.SplitN(rule, " `", 2); len(fields) == 2 {
 		r := splitSimpleRule{source: strings.TrimSpace(fields[0])}
-		me.Logger.Debug().Msgf("fields := %v", fields)
+		logger.Debug(me.Prefix, "fields := %v", fields)
 		if fields = strings.SplitN(fields[1], "` ", 2); len(fields) == 2 {
-			me.Logger.Debug().Msgf("fields = %v", fields)
+			logger.Debug(me.Prefix, "fields = %v", fields)
 			r.sep = fields[0]
 			if r.targets = strings.Split(fields[1], ","); len(r.targets) != 0 {
 				me.splitSimpleRules = append(me.splitSimpleRules, r)
-				me.Logger.Debug().Msgf("(split) parsed rule [%v]", r)
+				logger.Debug(me.Prefix, "(split) parsed rule [%v]", r)
 				return
 			}
 		}
 	}
-	me.Logger.Warn().Msgf("(split) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(split) rule has invalid format [%s]", rule)
 }
 
 type splitPairsRule struct {
@@ -151,11 +152,11 @@ func (me *LabelAgent) parseSplitPairsRule(rule string) {
 		r := splitPairsRule{source: strings.TrimSpace(fields[0])}
 		r.sep1 = fields[1]
 		r.sep2 = fields[3]
-		me.Logger.Debug().Msgf("(split_pairs) parsed rule [%v]", r)
+		logger.Debug(me.Prefix, "(split_pairs) parsed rule [%v]", r)
 		me.splitPairsRules = append(me.splitPairsRules, r)
 		return
 	}
-	me.Logger.Warn().Msgf("(split_pairs) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(split_pairs) rule has invalid format [%s]", rule)
 }
 
 type splitRegexRule struct {
@@ -175,18 +176,18 @@ func (me *LabelAgent) parseSplitRegexRule(rule string) {
 		if fields = strings.SplitN(fields[1], "` ", 2); len(fields) == 2 {
 			var err error
 			if r.reg, err = regexp.Compile(fields[0]); err != nil {
-				me.Logger.Error().Stack().Err(err).Msg("(split_regex) invalid regex")
+				logger.Error(me.Prefix, "(split_regex) invalid regex: %v", err)
 				return
 			}
-			me.Logger.Trace().Msgf("(split_regex) compule regex [%s]", r.reg.String())
+			logger.Trace(me.Prefix, "(split_regex) compule regex [%s]", r.reg.String())
 			if r.targets = strings.Split(fields[1], ","); len(r.targets) != 0 {
 				me.splitRegexRules = append(me.splitRegexRules, r)
-				me.Logger.Debug().Msgf("(split_regex) parsed rule [%v]", r)
+				logger.Debug(me.Prefix, "(split_regex) parsed rule [%v]", r)
 				return
 			}
 		}
 	}
-	me.Logger.Warn().Msgf("(split_regex) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(split_regex) rule has invalid format [%s]", rule)
 }
 
 type joinSimpleRule struct {
@@ -207,12 +208,12 @@ func (me *LabelAgent) parseJoinSimpleRule(rule string) {
 			r.sep = fields[0]
 			if r.sources = strings.Split(fields[1], ","); len(r.sources) != 0 {
 				me.joinSimpleRules = append(me.joinSimpleRules, r)
-				me.Logger.Debug().Msgf("(join) parsed rule [%v]", r)
+				logger.Debug(me.Prefix, "(join) parsed rule [%v]", r)
 				return
 			}
 		}
 	}
-	me.Logger.Warn().Msgf("(join) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(join) rule has invalid format [%s]", rule)
 }
 
 type replaceSimpleRule struct {
@@ -234,11 +235,11 @@ func (me *LabelAgent) parseReplaceSimpleRule(rule string) {
 			r.old = strings.TrimSuffix(fields[1], "`")
 			r.new = strings.TrimSuffix(fields[2], "`")
 			me.replaceSimpleRules = append(me.replaceSimpleRules, r)
-			me.Logger.Debug().Msgf("(replace) parsed rule [%v]", r)
+			logger.Debug(me.Prefix, "(replace) parsed rule [%v]", r)
 			return
 		}
 	}
-	me.Logger.Warn().Msgf("(replace) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(replace) rule has invalid format [%s]", rule)
 }
 
 type replaceRegexRule struct {
@@ -260,16 +261,16 @@ func (me *LabelAgent) parseReplaceRegexRule(rule string) {
 			r := replaceRegexRule{source: labels[0], target: labels[1]}
 			var err error
 			if r.reg, err = regexp.Compile(strings.TrimSuffix(fields[1], "`")); err != nil {
-				me.Logger.Error().Stack().Err(err).Msg("(replace_regex) invalid regex")
+				logger.Error(me.Prefix, "(replace_regex) invalid regex: %v", err)
 				return
 			}
-			me.Logger.Trace().Msgf("(replace_regex) compiled regular expression [%s]", r.reg.String())
+			logger.Trace(me.Prefix, "(replace_regex) compiled regular expression [%s]", r.reg.String())
 
 			r.indices = make([]int, 0)
 			err_pos := -1
 
 			if fields[2] = strings.TrimSuffix(fields[2], "`"); len(fields[2]) != 0 {
-				me.Logger.Trace().Msgf("(replace_regex) parsing substitution string [%s] (%d)", fields[2], len(fields[2]))
+				logger.Trace(me.Prefix, "(replace_regex) parsing substitution string [%s] (%d)", fields[2], len(fields[2]))
 				inside_num := false
 				num := ""
 				for i, b := range fields[2] {
@@ -300,15 +301,15 @@ func (me *LabelAgent) parseReplaceRegexRule(rule string) {
 				}
 			}
 			if err_pos != -1 {
-				me.Logger.Error().Stack().Err(nil).Msgf("(replace_regex) invalid char in substitution string at pos %d (%s)", err_pos, string(fields[2][err_pos]))
+				logger.Error(me.Prefix, "(replace_regex) invalid char in substitution string at pos %d (%s)", err_pos, string(fields[2][err_pos]))
 				return
 			}
 			me.replaceRegexRules = append(me.replaceRegexRules, r)
-			me.Logger.Debug().Msgf("(replace_regex) parsed rule [%v]", r)
+			logger.Debug(me.Prefix, "(replace_regex) parsed rule [%v]", r)
 			return
 		}
 	}
-	me.Logger.Warn().Msgf("(replace_regex) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(replace_regex) rule has invalid format [%s]", rule)
 }
 
 type excludeEqualsRule struct {
@@ -325,9 +326,9 @@ func (me *LabelAgent) parseExcludeEqualsRule(rule string) {
 		r := excludeEqualsRule{label: fields[0]}
 		r.value = strings.TrimSuffix(fields[1], "`")
 		me.excludeEqualsRules = append(me.excludeEqualsRules, r)
-		me.Logger.Debug().Msgf("(exclude_equals) parsed rule [%v]", r)
+		logger.Debug(me.Prefix, "(exclude_equals) parsed rule [%v]", r)
 	} else {
-		me.Logger.Warn().Msgf("(exclude_equals) rule definition [%s] should have two fields", rule)
+		logger.Warn(me.Prefix, "(exclude_equals) rule definition [%s] should have two fields", rule)
 	}
 }
 
@@ -341,9 +342,9 @@ func (me *LabelAgent) parseExcludeContainsRule(rule string) {
 		r := excludeContainsRule{label: fields[0]}
 		r.value = strings.TrimSuffix(fields[1], "`")
 		me.excludeContainsRules = append(me.excludeContainsRules, r)
-		me.Logger.Debug().Msgf("(exclude_contains) parsed rule [%v]", r)
+		logger.Debug(me.Prefix, "(exclude_contains) parsed rule [%v]", r)
 	} else {
-		me.Logger.Error().Stack().Err(nil).Msgf("(exclude_contains) rule definition [%s] should have two fields", rule)
+		logger.Error(me.Prefix, "(exclude_contains) rule definition [%s] should have two fields", rule)
 	}
 }
 
@@ -358,13 +359,13 @@ func (me *LabelAgent) parseExcludeRegexRule(rule string) {
 		var err error
 		if r.reg, err = regexp.Compile(strings.TrimSuffix(fields[1], "`")); err == nil {
 			me.excludeRegexRules = append(me.excludeRegexRules, r)
-			me.Logger.Debug().Msgf("(exclude_regex) compiled regex: [%s]", r.reg.String())
-			me.Logger.Debug().Msgf("(exclude_regex) parsed rule [%v]", r)
+			logger.Trace(me.Prefix, "(exclude_regex) compiled regex: [%s]", r.reg.String())
+			logger.Debug(me.Prefix, "(exclude_regex) parsed rule [%v]", r)
 		} else {
-			me.Logger.Error().Stack().Err(err).Msgf("(exclude_regex) compile regex:")
+			logger.Error(me.Prefix, "(exclude_regex) compile regex: %v", err)
 		}
 	} else {
-		me.Logger.Error().Stack().Err(nil).Msgf("(exclude_regex) rule definition [%s] should have two fields", rule)
+		logger.Error(me.Prefix, "(exclude_regex) rule definition [%s] should have two fields", rule)
 	}
 }
 
@@ -395,7 +396,7 @@ func (me *LabelAgent) parseValueMappingRule(rule string) {
 			fields[3] = strings.TrimPrefix(strings.TrimSuffix(fields[3], "`"), "`")
 
 			if v, err := strconv.ParseUint(fields[3], 10, 8); err != nil {
-				me.Logger.Error().Stack().Err(err).Msgf("(value_mapping) parse default value (%s): ", fields[3])
+				logger.Error(me.Prefix, "(value_mapping) parse default value (%s): %v", fields[3], err)
 				return
 			} else {
 				r.hasDefault = true
@@ -404,8 +405,8 @@ func (me *LabelAgent) parseValueMappingRule(rule string) {
 		}
 
 		me.valueMappingRules = append(me.valueMappingRules, r)
-		me.Logger.Debug().Msgf("(value_mapping) parsed rule [%v]", r)
+		logger.Debug(me.Prefix, "(value_mapping) parsed rule [%v]", r)
 		return
 	}
-	me.Logger.Warn().Msgf("(value_mapping) rule has invalid format [%s]", rule)
+	logger.Warn(me.Prefix, "(value_mapping) rule has invalid format [%s]", rule)
 }
diff --git a/cmd/poller/plugin/plugin.go b/cmd/poller/plugin/plugin.go
index 634df1de..b8ed494b 100644
--- a/cmd/poller/plugin/plugin.go
+++ b/cmd/poller/plugin/plugin.go
@@ -30,7 +30,7 @@ package plugin
 import (
 	"goharvest2/cmd/poller/options"
 	"goharvest2/pkg/errors"
-	"goharvest2/pkg/logging"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 )
@@ -46,7 +46,7 @@ type Plugin interface {
 type AbstractPlugin struct {
 	Parent       string
 	Name         string
-	Logger       *logging.Logger // logger used for logging
+	Prefix       string
 	Options      *options.Options
 	Params       *node.Node
 	ParentParams *node.Node
@@ -78,7 +78,10 @@ func (me *AbstractPlugin) InitAbc() error {
 	if me.Name = me.Params.GetNameS(); me.Name == "" {
 		return errors.New(errors.MISSING_PARAM, "plugin name")
 	}
-	me.Logger = logging.SubLogger("plugin", me.Parent+":"+me.Name)
+
+	me.Prefix = "(plugin) (" + me.Parent + ":" + me.Name + ")"
+
+	logger.Trace(me.Prefix, "initialized")
 
 	return nil
 }
diff --git a/cmd/poller/poller.go b/cmd/poller/poller.go
index 91e5abb7..6b388823 100644
--- a/cmd/poller/poller.go
+++ b/cmd/poller/poller.go
@@ -34,9 +34,10 @@ import (
 	"goharvest2/pkg/conf"
 	"goharvest2/pkg/dload"
 	"goharvest2/pkg/errors"
-	"goharvest2/pkg/logging"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
+	"log/syslog"
 	"net/http"
 	_ "net/http/pprof" // #nosec since pprof is off by default
 	"os"
@@ -53,16 +54,12 @@ import (
 
 // default params
 var (
-	pollerSchedule  string = "60s"
-	logFileName     string = ""
-	logMaxMegaBytes int    = logging.DefaultLogMaxMegaBytes // 10MB
-	logMaxBackups   int    = logging.DefaultLogMaxBackups
-	logMaxAge       int    = logging.DefaultLogMaxAge
+	_POLLER_SCHEDULE string = "60s"
+	_LOG_FILE_NAME   string = ""
+	_LOG_MAX_BYTES   int64  = 10000000 // 10MB
+	_LOG_MAX_FILES   int    = 10
 )
 
-// init with default configuration by default it gets logged both to console and  harvest.log
-var logger *logging.Logger = logging.Get()
-
 // signals to catch
 var SIGNALS = []os.Signal{
 	syscall.SIGHUP,
@@ -72,7 +69,7 @@ var SIGNALS = []os.Signal{
 }
 
 // deprecated collectors to throw warning
-var deprecatedCollectors = map[string]string{
+var _DEPRECATED_COLLECTORS = map[string]string{
 	"psutil": "Unix",
 }
 
@@ -80,6 +77,7 @@ var deprecatedCollectors = map[string]string{
 // group of collectors and exporters as a single UNIX process
 type Poller struct {
 	name            string
+	prefix          string
 	target          string
 	options         *options.Options
 	pid             int
@@ -98,7 +96,7 @@ func New() *Poller {
 	return &Poller{}
 }
 
-// Init starts Poller, reads parameters, opens zeroLog handler, initializes metadata,
+// Init starts Poller, reads parameters, opens log handler, initializes metadata,
 // starts collectors and exporters
 func (me *Poller) Init() error {
 
@@ -108,89 +106,77 @@ func (me *Poller) Init() error {
 	options.SetPathsAndHostname(&args)
 	me.options = &args
 	me.name = args.Poller
+	logger.Info(me.prefix, "options config: %s", me.options.Config)
+
+	// use prefix for logging
+	me.prefix = "(poller) (" + me.name + ")"
 
-	fileLoggingEnabled := false
-	consoleLoggingEnabled := false
-	zeroLogLevel := logging.GetZerologLevel(me.options.LogLevel)
 	// if we are daemon, use file logging
 	if me.options.Daemon {
-		logFileName = "poller_" + me.name + ".log"
-		fileLoggingEnabled = true
-	} else {
-		consoleLoggingEnabled = true
-	}
-
-	if me.params, err = conf.GetPoller(me.options.Config, me.name); err != nil {
-		// seperate logger is not yet configured as it depends on setting logMaxMegaBytes, logMaxFiles later
-		// Using default insance of logger which logs below error to harvest.log
-		logging.SubLogger("Poller", me.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 i, err := strconv.ParseInt(s, 10, 64); err == nil {
-			logMaxMegaBytes = int(i / (1024 * 1024))
+		_LOG_FILE_NAME = "poller_" + me.name + ".log"
+		if err = logger.OpenFileOutput(me.options.LogPath, _LOG_FILE_NAME); err != nil {
+			return err
 		}
 	}
 
-	// maximum number of rotated files to keep
-	if s := me.params.GetChildContentS("log_max_files"); s != "" {
-		if i, err := strconv.Atoi(s); err == nil {
-			logMaxBackups = i
-		}
+	// set logging level
+	if err = logger.SetLevel(me.options.LogLevel); err != nil {
+		logger.Warn(me.prefix, "using default loglevel=2 (info): %s", err.Error())
 	}
 
-	logConfig := logging.LogConfig{ConsoleLoggingEnabled: consoleLoggingEnabled,
-		PrefixKey:          "Poller",
-		PrefixValue:        me.name,
-		LogLevel:           zeroLogLevel,
-		FileLoggingEnabled: fileLoggingEnabled,
-		Directory:          me.options.LogPath,
-		Filename:           logFileName,
-		MaxSize:            logMaxMegaBytes,
-		MaxBackups:         logMaxBackups,
-		MaxAge:             logMaxAge}
-
-	logger = logging.Configure(logConfig)
-	logger.Info().Msgf("log level used: %s", zeroLogLevel.String())
-	logger.Info().Msgf("options config: %s", me.options.Config)
-
 	// if profiling port > 0 start profiling service
 	if me.options.Profiling > 0 {
 		addr := fmt.Sprintf("localhost:%d", me.options.Profiling)
-		logger.Info().Msgf("profiling enabled on [%s]", addr)
+		logger.Info(me.prefix, "profiling enabled on [%s]", addr)
 		go func() {
 			fmt.Println(http.ListenAndServe(addr, nil))
 		}()
 	}
 
 	// useful info for debugging
-	logger.Debug().Msgf("* %s *s", version.String())
-	logger.Debug().Msgf("options= %s", me.options.String())
+	logger.Debug(me.prefix, "* %s *s", version.String())
+	logger.Debug(me.prefix, "options= %s", me.options.String())
 
 	// set signal handler for graceful termination
 	signalChannel := make(chan os.Signal, 1)
 	signal.Notify(signalChannel, SIGNALS...)
 	go me.handleSignals(signalChannel)
-	logger.Debug().Msgf("set signal handler for %v", SIGNALS)
+	logger.Debug(me.prefix, "set signal handler for %v", SIGNALS)
 
 	// write PID to file
 	if err = me.registerPid(); err != nil {
-		logger.Warn().Msgf("failed to write PID file: %v", err)
+		logger.Warn(me.prefix, "failed to write PID file: %v", err)
 		return err
 	}
 
 	// announce startup
 	if me.options.Daemon {
-		logger.Info().Msgf("started as daemon [pid=%d] [pid file=%s]", me.pid, me.pidf)
+		logger.Info(me.prefix, "started as daemon [pid=%d] [pid file=%s]", me.pid, me.pidf)
 	} else {
-		logger.Info().Msgf("started in foreground [pid=%d]", me.pid)
+		logger.Info(me.prefix, "started in foreground [pid=%d]", me.pid)
 	}
 
 	// load parameters from config (harvest.yml)
-	logger.Debug().Msgf("importing config [%s]", me.options.Config)
+	logger.Debug(me.prefix, "importing config [%s]", me.options.Config)
+	if me.params, err = conf.GetPoller(me.options.Config, me.name); err != nil {
+		logger.Error(me.prefix, "read config: %v", err)
+		return err
+	}
+
+	// log handling parameters
+	// size of file before rotating
+	if s := me.params.GetChildContentS("log_max_bytes"); s != "" {
+		if i, err := strconv.ParseInt(s, 10, 64); err == nil {
+			_LOG_MAX_BYTES = i
+		}
+	}
+
+	// maximum number of rotated files to keep
+	if s := me.params.GetChildContentS("log_max_files"); s != "" {
+		if i, err := strconv.Atoi(s); err == nil {
+			_LOG_MAX_FILES = i
+		}
+	}
 
 	// each poller is associated with a remote host
 	// if no address is specified, assume that is local host
@@ -198,6 +184,7 @@ func (me *Poller) Init() error {
 	if me.target = me.params.GetChildContentS("addr"); me.target == "" {
 		me.target = "localhost"
 	}
+
 	// check optional parameter auth_style
 	// if certificates are missing use default paths
 	if me.params.GetChildContentS("auth_style") == "certificate_auth" {
@@ -210,10 +197,10 @@ func (me *Poller) Init() error {
 				// 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)
-				logger.Debug().Msgf("using default [%s] path: [%s]", filenames[i], fp)
+				logger.Debug(me.prefix, "using default [%s] path: [%s]", filenames[i], fp)
 			}
 			if _, err = os.Stat(fp); err != nil {
-				logger.Error().Stack().Err(err).Msgf("%s", filenames[i])
+				logger.Error(me.prefix, "%s %v", filenames[i], err)
 				return errors.New(errors.MISSING_PARAM, filenames[i]+": "+err.Error())
 			}
 		}
@@ -224,7 +211,7 @@ func (me *Poller) Init() error {
 	me.load_metadata()
 
 	if me.exporter_params, err = conf.GetExporters(me.options.Config); err != nil {
-		logger.Warn().Msgf("read exporter params: %v", err)
+		logger.Warn(me.prefix, "read exporter params: %v", err)
 		// @TODO just warn or abort?
 	}
 
@@ -232,7 +219,7 @@ func (me *Poller) Init() error {
 	// exporters are initialized on the fly, if at least one collector
 	// has requested them
 	if collectors := me.params.GetChildS("collectors"); collectors == nil {
-		logger.Warn().Msg("no collectors defined for this poller in config")
+		logger.Warn(me.prefix, "no collectors defined for this poller in config")
 		return errors.New(errors.ERR_NO_COLLECTOR, "no collectors")
 	} else {
 		for _, c := range collectors.GetAllChildContentS() {
@@ -248,47 +235,47 @@ func (me *Poller) Init() error {
 				}
 			}
 			if !ok {
-				logger.Debug().Msgf("skipping collector [%s]", c)
+				logger.Debug(me.prefix, "skipping collector [%s]", c)
 				continue
 			}
 
 			if err = me.load_collector(c, ""); err != nil {
-				logger.Error().Stack().Err(err).Msgf("load collector (%s):", c)
+				logger.Error(me.prefix, "load collector (%s): %v", c, err)
 			}
 		}
 	}
 
 	// at least one collector should successfully initialize
 	if len(me.collectors) == 0 {
-		logger.Warn().Msg("no collectors initialized, stopping")
+		logger.Warn(me.prefix, "no collectors initialized, stopping")
 		return errors.New(errors.ERR_NO_COLLECTOR, "no collectors")
 	}
 
-	logger.Debug().Msgf("initialized %d collectors", len(me.collectors))
+	logger.Debug(me.prefix, "initialized %d collectors", len(me.collectors))
 
 	// we are more tolerable against exporters, since we might only
 	// want to debug collectors without actually exporting
 	if len(me.exporters) == 0 {
-		logger.Warn().Msg("no exporters initialized, continuing without exporters")
+		logger.Warn(me.prefix, "no exporters initialized, continuing without exporters")
 	} else {
-		logger.Debug().Msgf("initialized %d exporters", len(me.exporters))
+		logger.Debug(me.prefix, "initialized %d exporters", len(me.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 != "" {
-		pollerSchedule = s
+		_POLLER_SCHEDULE = s
 	}
 	me.schedule = schedule.New()
-	if err = me.schedule.NewTaskString("poller", pollerSchedule, nil); err != nil {
-		logger.Error().Stack().Err(err).Msg("set schedule:")
+	if err = me.schedule.NewTaskString("poller", _POLLER_SCHEDULE, nil); err != nil {
+		logger.Error(me.prefix, "set schedule: %v", err)
 		return err
 	}
-	logger.Debug().Msgf("set poller schedule with %s frequency", pollerSchedule)
+	logger.Debug(me.prefix, "set poller schedule with %s frequency", _POLLER_SCHEDULE)
 
 	// famous last words
-	logger.Info().Msg("poller start-up complete")
+	logger.Info(me.prefix, "poller start-up complete")
 
 	return nil
 
@@ -306,7 +293,7 @@ func (me *Poller) Start() {
 
 	// start collectors
 	for _, col = range me.collectors {
-		logger.Debug().Msgf("launching collector (%s:%s)", col.GetName(), col.GetObject())
+		logger.Debug(me.prefix, "launching collector (%s:%s)", col.GetName(), col.GetObject())
 		wg.Add(1)
 		go col.Start(&wg)
 	}
@@ -317,7 +304,7 @@ func (me *Poller) Start() {
 	wg.Wait()
 
 	// ...until there are no collectors running anymore
-	logger.Info().Msg("no active collectors -- terminating")
+	logger.Info(me.prefix, "no active collectors -- terminating")
 
 	me.Stop()
 }
@@ -361,7 +348,7 @@ func (me *Poller) Run() {
 			// update status of collectors
 			for _, c := range me.collectors {
 				code, status, msg := c.GetStatus()
-				logger.Debug().Msgf("collector (%s:%s) status: (%d - %s) %s", c.GetName(), c.GetObject(), code, status, msg)
+				logger.Debug(me.prefix, "collector (%s:%s) status: (%d - %s) %s", c.GetName(), c.GetObject(), code, status, msg)
 
 				if code == 0 {
 					upc++
@@ -382,7 +369,7 @@ func (me *Poller) Run() {
 			// update status of exporters
 			for _, e := range me.exporters {
 				code, status, msg := e.GetStatus()
-				logger.Debug().Msgf("exporter (%s) status: (%d - %s) %s", e.GetName(), code, status, msg)
+				logger.Debug(me.prefix, "exporter (%s) status: (%d - %s) %s", e.GetName(), code, status, msg)
 
 				if code == 0 {
 					upe++
@@ -403,34 +390,54 @@ func (me *Poller) Run() {
 			// @TODO if there are no "master" exporters, don't collect metadata
 			for _, e := range me.exporters {
 				if err := e.Export(me.metadata); err != nil {
-					logger.Error().Stack().Err(err).Msg("export component metadata:")
+					logger.Error(me.prefix, "export component metadata: %v", err)
 				}
 				if err := e.Export(me.status); err != nil {
-					logger.Error().Stack().Err(err).Msg("export target metadata:")
+					logger.Error(me.prefix, "export target metadata: %v", err)
 				}
 			}
 
-			// only zeroLog when numbers have changes, since hopefully that happens rarely
+			// only log 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(me.prefix, "updated status, up collectors: %d (of %d), up exporters: %d (of %d)", upc, len(me.collectors), upe, len(me.exporters))
 			}
 			upCollectors = upc
 			upExporters = upe
+
+			// some housekeeping jobs if we are daemon
+			// @TODO: syslog or panic on log file related errors (might mean fs is corrupt or unavailable)
+			// @TODO: probably delegate to log handler (both rotating and panicing)
+			if me.options.Daemon {
+				// check size of log file
+				if stat, err := os.Stat(path.Join(me.options.LogPath, _LOG_FILE_NAME)); err != nil {
+					logger.Error(me.prefix, "stat: %v", err)
+					// rotate if exceeds threshold
+				} else if stat.Size() >= _LOG_MAX_BYTES {
+					logger.Debug(me.prefix, "rotating log (size= %d bytes)", stat.Size())
+					if err = logger.Rotate(me.options.LogPath, _LOG_FILE_NAME, _LOG_MAX_FILES); err != nil {
+						logger.Error(me.prefix, "rotating log: %v", err)
+					}
+				}
+			}
 		}
 
 		me.schedule.Sleep()
 	}
 }
 
-// Stop gracefully exits the program by closing zeroLog file and removing pid file
+// Stop gracefully exits the program by closing log file and removing pid file
 func (me *Poller) Stop() {
-	logger.Info().Msgf("cleaning up and stopping [pid=%d]", me.pid)
+	logger.Info(me.prefix, "cleaning up and stopping [pid=%d]", me.pid)
 
 	if me.options.Daemon {
 		if err := os.Remove(me.pidf); err != nil {
-			logger.Error().Stack().Err(err).Msg("clean pid file")
+			logger.Error(me.prefix, "clean pid file: %v", err)
 		} else {
-			logger.Debug().Msgf("cleaned pid file [%s]", me.pidf)
+			logger.Debug(me.prefix, "cleaned pid file [%s]", me.pidf)
+		}
+
+		if err := logger.CloseFileOutput(); err != nil {
+			logger.Error(me.prefix, "close log file: %v", err)
 		}
 	}
 }
@@ -460,7 +467,7 @@ func (me *Poller) registerPid() error {
 func (me *Poller) handleSignals(signal_channel chan os.Signal) {
 	for {
 		sig := <-signal_channel
-		logger.Info().Msgf("caught signal [%s]", sig)
+		logger.Info(me.prefix, "caught signal [%s]", sig)
 		me.Stop()
 		os.Exit(0)
 	}
@@ -502,11 +509,11 @@ func (me *Poller) load_collector(class, object string) error {
 	binpath = path.Join(me.options.HomePath, "bin", "collectors")
 
 	// throw warning for deprecated collectors
-	if r, d := deprecatedCollectors[strings.ToLower(class)]; d {
+	if r, d := _DEPRECATED_COLLECTORS[strings.ToLower(class)]; d {
 		if r != "" {
-			logger.Warn().Msgf("collector (%s) is deprecated, please use (%s) instead", class, r)
+			logger.Warn(me.prefix, "collector (%s) is deprecated, please use (%s) instead", class, r)
 		} else {
-			logger.Warn().Msgf("collector (%s) is deprecated, see documentation for help", class)
+			logger.Warn(me.prefix, "collector (%s) is deprecated, see documentation for help", class)
 		}
 	}
 
@@ -529,8 +536,9 @@ func (me *Poller) load_collector(class, object string) error {
 
 	if custom, err = collector.ImportTemplate(me.options.HomePath, "custom.yaml", class); err == nil && custom != nil {
 		template.Merge(custom)
-		logger.Debug().Msg("merged custom and default templates")
+		logger.Debug(me.prefix, "merged custom and default templates")
 	}
+
 	// add Poller's parameters to the collector parameters
 	template.Union(me.params)
 
@@ -543,10 +551,10 @@ func (me *Poller) load_collector(class, object string) error {
 	if object != "" {
 		col = NewFunc(collector.New(class, object, me.options, template.Copy()))
 		if err = col.Init(); err != nil {
-			logger.Error().Msgf("init collector (%s:%s): %v", class, object, err)
+			logger.Error(me.prefix, "init collector (%s:%s): %v", class, object, err)
 		} else {
 			collectors = append(collectors, col)
-			logger.Debug().Msgf("initialized collector (%s:%s)", class, object)
+			logger.Debug(me.prefix, "initialized collector (%s:%s)", class, object)
 		}
 		// if template has list of objects, initialize 1 subcollector for each
 	} else if objects := template.GetChildS("objects"); objects != nil {
@@ -566,20 +574,20 @@ func (me *Poller) load_collector(class, object string) error {
 			}
 
 			if !ok {
-				logger.Debug().Msgf("skipping object [%s]", object.GetNameS())
+				logger.Debug(me.prefix, "skipping object [%s]", object.GetNameS())
 				continue
 			}
 
 			col = NewFunc(collector.New(class, object.GetNameS(), me.options, template.Copy()))
 			if err = col.Init(); err != nil {
-				logger.Warn().Msgf("init collector-object (%s:%s): %v", class, object.GetNameS(), err)
+				logger.Warn(me.prefix, "init collector-object (%s:%s): %v", class, object.GetNameS(), err)
 				if errors.IsErr(err, errors.ERR_CONNECTION) {
-					logger.Warn().Msgf("aborting collector (%s)", class)
+					logger.Warn(me.prefix, "aborting collector (%s)", class)
 					break
 				}
 			} else {
 				collectors = append(collectors, col)
-				logger.Debug().Msgf("initialized collector-object (%s:%s)", class, object.GetNameS())
+				logger.Debug(me.prefix, "initialized collector-object (%s:%s)", class, object.GetNameS())
 			}
 		}
 	} else {
@@ -587,7 +595,8 @@ func (me *Poller) load_collector(class, object string) error {
 	}
 
 	me.collectors = append(me.collectors, collectors...)
-	logger.Debug().Msgf("initialized (%s) with %d objects", class, len(collectors))
+	logger.Debug(me.prefix, "initialized (%s) with %d objects", class, len(collectors))
+
 	// link each collector with requested exporter & update metadata
 	for _, col = range collectors {
 
@@ -595,12 +604,12 @@ func (me *Poller) load_collector(class, object string) error {
 		obj := col.GetObject()
 
 		for _, exp_name := range col.WantedExporters(me.options.Config) {
-			logger.Trace().Msgf("exp_name %s", exp_name)
+			logger.Trace(me.prefix, "exp_name %s", exp_name)
 			if exp := me.load_exporter(exp_name); exp != nil {
 				col.LinkExporter(exp)
-				logger.Debug().Msgf("linked (%s:%s) to exporter (%s)", name, obj, exp_name)
+				logger.Debug(me.prefix, "linked (%s:%s) to exporter (%s)", name, obj, exp_name)
 			} else {
-				logger.Warn().Msgf("exporter (%s) requested by (%s:%s) not available", exp_name, name, obj)
+				logger.Warn(me.prefix, "exporter (%s) requested by (%s:%s) not available", exp_name, name, obj)
 			}
 		}
 
@@ -636,40 +645,40 @@ func (me *Poller) load_exporter(name string) exporter.Exporter {
 	}
 
 	if params = me.exporter_params.GetChildS(name); params == nil {
-		logger.Warn().Msgf("exporter (%s) not defined in config", name)
+		logger.Warn(me.prefix, "exporter (%s) not defined in config", name)
 		return nil
 	}
 
 	if class = params.GetChildContentS("exporter"); class == "" {
-		logger.Warn().Msgf("exporter (%s) has no exporter class defined", name)
+		logger.Warn(me.prefix, "exporter (%s) has no exporter class defined", name)
 		return nil
 	}
 
 	binpath = path.Join(me.options.HomePath, "bin", "exporters")
 
 	if sym, err = dload.LoadFuncFromModule(binpath, strings.ToLower(class), "New"); err != nil {
-		logger.Error().Msgf("dload: %v", err.Error())
+		logger.Error(me.prefix, "dload: %v", err.Error())
 		return nil
 	}
 
 	NewFunc, ok := sym.(func(*exporter.AbstractExporter) exporter.Exporter)
 	if !ok {
-		logger.Error().Msg("New() has not expected signature")
+		logger.Error(me.prefix, "New() has not expected signature")
 		return nil
 	}
 
 	exp = NewFunc(exporter.New(class, name, me.options, params))
 	if err = exp.Init(); err != nil {
-		logger.Error().Msgf("init exporter (%s): %v", name, err)
+		logger.Error(me.prefix, "init exporter (%s): %v", name, err)
 		return nil
 	}
 
 	me.exporters = append(me.exporters, exp)
-	logger.Debug().Msgf("initialized exporter (%s)", name)
+	logger.Debug(me.prefix, "initialized exporter (%s)", name)
 
 	// update metadata
 	if instance, err := me.metadata.NewInstance(exp.GetClass() + "." + exp.GetName()); err != nil {
-		logger.Error().Msgf("add metadata instance: %v", err)
+		logger.Error(me.prefix, "add metadata instance: %v", err)
 	} else {
 		instance.SetLabel("type", "exporter")
 		instance.SetLabel("name", exp.GetClass())
@@ -758,16 +767,19 @@ func init() {
 func main() {
 
 	// don't recover if a goroutine has panicked, instead
-	// try to zeroLog as much as possible, since normally it's
+	// try to log as much as possible, since normally it's
 	// not properly logged
 	defer func() {
 		//logger.Warn("(main) ", "defer func here")
 		if r := recover(); r != nil {
-			logger.Info().Msgf("harvest poller panicked: %v", r)
+			syslogger, err := syslog.NewLogger(syslog.LOG_ERR|syslog.LOG_DAEMON, logger.LOG_FLAGS)
+			if err == nil {
+				syslogger.Printf("harvest poller paniced: %v", r)
+			}
 			// if logger still available try to write there as well
 			// do this last, since might make us panic as again
-			logger.Fatal().Msgf("(main) %v", r)
-			logger.Fatal().Msg(`(main) terminating abnormally, tip: run in foreground mode (with "--loglevel 0") to debug`)
+			logger.Fatal("(main) ", "%v", r)
+			logger.Fatal("(main) ", "terminating abnormally, tip: run in foreground mode (with \"--loglevel 0\") to debug")
 
 			os.Exit(1)
 		}
diff --git a/deb/build-deb.sh b/deb/build-deb.sh
index 2ea6a5ff..fa3df4c4 100755
--- a/deb/build-deb.sh
+++ b/deb/build-deb.sh
@@ -21,7 +21,7 @@ rm -rf "$BUILD"
 mkdir -p "$BUILD"
 mkdir -p "$BUILD/opt/harvest/bin/"
 cp -r "$SRC/grafana" "$SRC/conf" "$BUILD/opt/harvest/"
-cp "$SRC/harvest.yml" "$BUILD/opt/harvest/"
+cp "$SRC/harvest.example.yml" "$BUILD/opt/harvest/"
 cp -r "$SRC/pkg/" "$SRC/cmd/" "$SRC/docs/" "$BUILD/opt/harvest/"
 cp -r "$SRC/rpm/" "$SRC/deb/" "$SRC/service/" "$SRC/.git" "$BUILD/opt/harvest/"
 cp "$SRC/Makefile" "$SRC/README.md" "$SRC/LICENSE" "$SRC/go.mod" "$SRC/go.sum" "$BUILD/opt/harvest/"
diff --git a/deb/postinst b/deb/postinst
index 98aa0a67..669598f5 100755
--- a/deb/postinst
+++ b/deb/postinst
@@ -4,20 +4,6 @@
 # Copyright NetApp Inc, 2021 All rights reserved
 #
 
-# After installation/upgrade, copy latest harvest.yml as harvest.example.yml
-if [ -e /opt/harvest/harvest.yml ]; then
-    rm -rf /opt/harvest/harvest.example.yml
-    cp /opt/harvest/harvest.yml /opt/harvest/harvest.example.yml
-    echo "  --> copy new harvest.yml as [/opt/harvest/harvest.example.yml]"
-fi
-# restore old harvest.yml in case of upgrade
-if [ -e /opt/harvest/backup_harvest.yml ]; then
-    rm -rf /opt/harvest/harvest.yml
-    cp /opt/harvest/backup_harvest.yml /opt/harvest/harvest.yml
-    rm -rf /opt/harvest/backup_harvest.yml
-    echo "  --> restoring backup_harvest.yml as [/opt/harvest/harvest.yml]"
-fi
-
 echo " --> set directory ownership"
 chown -R harvest:harvest /opt/harvest
 mkdir -p /var/log/harvest
@@ -28,9 +14,11 @@ chown -R harvest:harvest /var/run/harvest
 echo "  --> copying service"
 cp /opt/harvest/service/harvest.service /etc/systemd/system/
 chmod 664 /etc/systemd/system/harvest.service
-
+if [ ! -e /opt/harvest/harvest.yml ]; then
+    cp /opt/harvest/harvest.example.yml /opt/harvest/harvest.yml
+    echo "  --> use default config file [/opt/harvest/harvest.yml]"
+fi
 systemctl daemon-reload
 systemctl start harvest.service
 systemctl enable harvest.service
-echo "  --> harvest service started through systemctl"
-echo "  --> harvest installed location /opt/harvest"
\ No newline at end of file
+echo "  --> harvest service started through systemctl"
\ No newline at end of file
diff --git a/deb/postrm b/deb/postrm
index b6b4c716..76a0c9f2 100755
--- a/deb/postrm
+++ b/deb/postrm
@@ -6,8 +6,7 @@
 
 if [[ "$1" == "purge" || "$1" == "remove" ]]; then
     if [ "$1" == "purge" ]; then
-      rm -rf /opt/harvest   && echo "  --> removing /opt/harvest folder"
-      rm -r /var/log/harvest && echo " --> delete log directory"
+        rm -r /var/log/harvest && echo " --> delete log directory"
     fi
     if systemctl list-units --full -all | grep -Fq 'harvest.service'; then
       echo " --> removing systemctl harvest files "
@@ -26,11 +25,5 @@ if [[ "$1" == "purge" || "$1" == "remove" ]]; then
     if [ $(getent group harvest) ]; then
       groupdel harvest && echo " --> delete harvest group"
     fi
-    if [ -e /opt/harvest/backup_harvest.yml ]; then
-      rm -rf /opt/harvest/harvest.yml
-      cp /opt/harvest/backup_harvest.yml /opt/harvest/harvest.yml
-      rm -rf /opt/harvest/backup_harvest.yml
-      echo " --> restoring backup_harvest.yml as [/opt/harvest/harvest.yml]"
-    fi
     echo " --> cleanup & uninstall complete"
 fi
diff --git a/deb/preinst b/deb/preinst
index a65eb557..0c9d7077 100755
--- a/deb/preinst
+++ b/deb/preinst
@@ -11,11 +11,6 @@ if [[ "$1" == "install" || "$1" == "upgrade" ]]; then
     if [  -e /usr/bin/harvest ]; then
       unlink /usr/bin/harvest && echo " --> delete link"
     fi
-    # Backup existing harvest.yml to avoid overwrite from new binary
-    if [ -e /opt/harvest/harvest.yml ]; then
-      cp /opt/harvest/harvest.yml /opt/harvest/backup_harvest.yml
-      echo " --> Backing up existing config file as [/opt/harvest/backup_harvest.yml]"
-    fi
     getent group harvest > /dev/null 2>&1 || addgroup -q --system harvest && echo " --> group created"
     getent passwd harvest > /dev/null 2>&1 || adduser -q --system --no-create-home \
         --ingroup harvest --disabled-password --shell /bin/false harvest && echo " --> user created"
diff --git a/deb/prerm b/deb/prerm
index 9c386ff9..b0d3a675 100755
--- a/deb/prerm
+++ b/deb/prerm
@@ -6,8 +6,3 @@
 
 echo " --> stop harvest for $1"
 systemctl stop harvest
-if [ -e /opt/harvest/harvest.yml ]; then
-  echo " --> backing up harvest conf"
-  cp /opt/harvest/harvest.yml /opt/harvest/backup_harvest.yml
-  echo "  --> Backing up existing config file as [/opt/harvest/backup_harvest.yml]"
-fi
\ No newline at end of file
diff --git a/docker/allPollerInOneContainer/Dockerfile b/docker/allPollerInOneContainer/Dockerfile
index bed85fbe..34d3a0b3 100644
--- a/docker/allPollerInOneContainer/Dockerfile
+++ b/docker/allPollerInOneContainer/Dockerfile
@@ -19,7 +19,7 @@ COPY . .
 
 RUN make build VERSION=$VERSION RELEASE=$RELEASE
 
-RUN cp -a $BUILD_DIR/harvest.yml $INSTALL_DIR/harvest.yml
+RUN cp -a $BUILD_DIR/harvest.example.yml $INSTALL_DIR/harvest.yml
 
 RUN cp -aR bin $BUILD_DIR/conf $BUILD_DIR/docs $BUILD_DIR/grafana $BUILD_DIR/docker/allPollerInOneContainer/docker-entrypoint.sh $INSTALL_DIR && \
    chmod +x $INSTALL_DIR/docker-entrypoint.sh
diff --git a/go.mod b/go.mod
index ca58b4d8..01ab86cb 100644
--- a/go.mod
+++ b/go.mod
@@ -4,8 +4,6 @@ go 1.15
 
 require (
 	github.com/hashicorp/go-version v1.3.0
-	github.com/rs/zerolog v1.22.0
 	github.com/spf13/cobra v1.1.3
-	gopkg.in/natefinch/lumberjack.v2 v2.0.0
 	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
 )
diff --git a/go.sum b/go.sum
index fed2110f..7c41e10b 100644
--- a/go.sum
+++ b/go.sum
@@ -11,7 +11,6 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
@@ -128,8 +127,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -143,9 +140,6 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
-github.com/rs/zerolog v1.22.0 h1:XrVUjV4K+izZpKXZHlPrYQiDtmdGiCylnT4i43AAWxg=
-github.com/rs/zerolog v1.22.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@@ -171,7 +165,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -183,8 +176,6 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -203,7 +194,6 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
 golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
 golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -218,7 +208,6 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -227,7 +216,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -241,12 +229,9 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -266,11 +251,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
 google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -298,13 +279,10 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/harvest.yml b/harvest.example.yml
similarity index 100%
rename from harvest.yml
rename to harvest.example.yml
diff --git a/pkg/api/ontapi/zapi/client.go b/pkg/api/ontapi/zapi/client.go
index 11b97916..74d235a7 100644
--- a/pkg/api/ontapi/zapi/client.go
+++ b/pkg/api/ontapi/zapi/client.go
@@ -11,7 +11,7 @@ import (
 	"crypto/tls"
 	"fmt"
 	"goharvest2/pkg/errors"
-	"goharvest2/pkg/logging"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/tree"
 	"goharvest2/pkg/tree/node"
 	"io"
@@ -33,7 +33,6 @@ type Client struct {
 	system     *system
 	apiVersion string
 	vfiler     string
-	Logger     *logging.Logger // logger used for logging
 }
 
 func New(config *node.Node) (*Client, error) {
@@ -50,16 +49,15 @@ func New(config *node.Node) (*Client, error) {
 	)
 
 	client = Client{}
-	client.Logger = logging.SubLogger("Zapi", "Client")
 
 	// check required & optional parameters
 	if client.apiVersion = config.GetChildContentS("api_version"); client.apiVersion == "" {
 		client.apiVersion = DefaultApiVersion
-		client.Logger.Debug().Msgf("using default API version [%s]", DefaultApiVersion)
+		logger.Debug("(Zapi:Client)", "using default API version [%s]", DefaultApiVersion)
 	}
 
 	if client.vfiler = config.GetChildContentS("api_vfiler"); client.vfiler != "" {
-		client.Logger.Debug().Msgf("using vfiler tunneling [%s]", client.vfiler)
+		logger.Debug("(Zapi:Client)", "using vfiler tunneling [%s]", client.vfiler)
 	}
 
 	if addr = config.GetChildContentS("addr"); addr == "" {
@@ -79,7 +77,7 @@ func New(config *node.Node) (*Client, error) {
 	// by default, encorce secure TLS, if not requested otherwise by user
 	if x := config.GetChildContentS("use_insecure_tls"); x != "" {
 		if useInsecureTLS, err = strconv.ParseBool(x); err != nil {
-			client.Logger.Error().Stack().Err(err).Msg("use_insecure_tls")
+			logger.Error("(Zapi:Client)", "use_insecure_tls: %v ", err)
 		}
 	} else {
 		useInsecureTLS = false
@@ -127,11 +125,11 @@ func New(config *node.Node) (*Client, error) {
 	// initialize http client
 	if t, err := strconv.Atoi(config.GetChildContentS("client_timeout")); err == nil {
 		timeout = time.Duration(t) * time.Second
-		client.Logger.Debug().Msgf("using timeout [%d] s", t)
+		logger.Debug("(Zapi:Client)", "using timeout [%d] s", t)
 	} else {
 		// default timeout
 		timeout = time.Duration(DefaultTimeout) * time.Second
-		client.Logger.Debug().Msgf("using default timeout [%d] s", DefaultTimeout)
+		logger.Debug("(Zapi:Client)", "using default timeout [%d] s", DefaultTimeout)
 	}
 
 	httpclient = &http.Client{Transport: transport, Timeout: timeout}
diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go
index 08560e27..faf49ab4 100644
--- a/pkg/conf/conf.go
+++ b/pkg/conf/conf.go
@@ -9,12 +9,58 @@ import (
 	"goharvest2/pkg/errors"
 	"goharvest2/pkg/tree"
 	"goharvest2/pkg/tree/node"
+	"gopkg.in/yaml.v3"
+	"io/ioutil"
 	"os"
 	"path"
 )
 
-func LoadConfig(config_fp string) (*node.Node, error) {
-	return tree.Import("yaml", config_fp)
+// LoadConfig loads the config info from harvest.yml
+func LoadConfig(configPath string) (*node.Node, error) {
+	configNode, err := tree.Import("yaml", configPath)
+	if configNode != nil {
+		// Load HarvestConfig to rewrite passwords - eventually all the code will be refactored to use HarvestConfig.
+		// This is needed because the current yaml parser does not handle password with special characters.
+		// E.g abc#123, that's because the # is interpreted as the beginning of a comment. The code below overwrites
+		// the incorrect password with the correct one by using a better yaml parser for each Poller and Default section
+		_ = LoadHarvestConfig(configPath)
+		pollers := configNode.GetChildS("Pollers")
+		if pollers != nil {
+			for _, poller := range pollers.GetChildren() {
+				password := poller.GetChildContentS("password")
+				pollerStruct := (*Config.Pollers)[poller.GetNameS()]
+				if pollerStruct.Password != "" && pollerStruct.Password != password {
+					poller.SetChildContentS("password", pollerStruct.Password)
+				}
+			}
+		}
+		// Check Defaults also
+		defaultNode := configNode.GetChildS("Defaults")
+		if defaultNode != nil {
+			password := defaultNode.GetChildContentS("password")
+			defaultStruct := *Config.Defaults
+			if defaultStruct.Password != "" && defaultStruct.Password != password {
+				defaultNode.SetChildContentS("password", defaultStruct.Password)
+			}
+		}
+	}
+	return configNode, err
+}
+
+var Config = HarvestConfig{}
+
+func LoadHarvestConfig(configPath string) error {
+	contents, err := ioutil.ReadFile(configPath)
+	if err != nil {
+		fmt.Printf("error reading config file=[%s] %+v\n", configPath, err)
+		return err
+	}
+	err = yaml.Unmarshal(contents, &Config)
+	if err != nil {
+		fmt.Printf("error unmarshalling config file=[%s] %+v\n", configPath, err)
+		return err
+	}
+	return nil
 }
 
 func SafeConfig(n *node.Node, fp string) error {
@@ -116,22 +162,6 @@ func GetHarvestHomePath() string {
 	return "./"
 }
 
-func GetHarvestLogPath() string {
-	var logPath string
-	if logPath = os.Getenv("HARVEST_LOGS"); logPath == "" {
-		logPath = "/var/log/harvest/"
-	}
-	return logPath
-}
-
-func GetHarvestPidPath() string {
-	var pidPath string
-	if pidPath = os.Getenv("HARVEST_PIDS"); pidPath == "" {
-		pidPath = "/var/run/harvest/"
-	}
-	return pidPath
-}
-
 /*
 This method returns port configured in prometheus exporter for given poller
 If there are more than 1 exporter configured for a poller then return string will have ports as comma seperated
@@ -206,7 +236,7 @@ type Poller struct {
 	Addr           *string   `yaml:"addr,omitempty"`
 	AuthStyle      *string   `yaml:"auth_style,omitempty"`
 	Username       *string   `yaml:"username,omitempty"`
-	Password       *string   `yaml:"password,omitempty"`
+	Password       string    `yaml:"password,omitempty"`
 	UseInsecureTls *bool     `yaml:"use_insecure_tls,omitempty"`
 	SslCert        *string   `yaml:"ssl_cert,omitempty"`
 	SslKey         *string   `yaml:"ssl_key,omitempty"`
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
new file mode 100644
index 00000000..2047537d
--- /dev/null
+++ b/pkg/logger/logger.go
@@ -0,0 +1,164 @@
+/*
+ * Copyright NetApp Inc, 2021 All rights reserved
+ */
+package logger
+
+import (
+	"fmt"
+	"goharvest2/pkg/errors"
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"strconv"
+	"strings"
+)
+
+const (
+	LOG_FLAGS  int         = log.Ldate | log.Ltime | log.Lmsgprefix
+	FILE_FLAGS int         = os.O_APPEND | os.O_CREATE | os.O_WRONLY
+	FILE_PERM  os.FileMode = 0644
+	DIR_PERM   os.FileMode = 0755
+)
+
+var file *os.File
+
+var levels = [6]string{
+	" (trace  ) %-12s: ",
+	" (debug  ) %-12s: ",
+	" (info   ) %-12s: ",
+	" (warning) %-12s: ",
+	" (error  ) %-12s: ",
+	" (fatal  ) %-12s: ",
+}
+
+var level = 2
+
+func OpenFileOutput(dirpath, filename string) error {
+	var info os.FileInfo
+	var err error
+
+	info, err = os.Stat(dirpath)
+	if err != nil || !info.IsDir() {
+		err = os.Mkdir(dirpath, DIR_PERM)
+	}
+	if err == nil || os.IsExist(err) {
+
+		file, err = os.OpenFile(path.Join(dirpath, filename), FILE_FLAGS, FILE_PERM)
+		if err == nil {
+			log.SetOutput(file)
+		}
+	}
+	return err
+}
+
+func CloseFileOutput() error {
+	return file.Close()
+}
+
+func Rotate(dirpath, filename string, maxfiles int) error {
+	var (
+		files                       []os.FileInfo
+		rotated                     []string
+		err                         error
+		curr_filepath, new_filepath string
+	)
+
+	curr_filepath = path.Join(dirpath, filename)
+	new_filepath = path.Join(dirpath, filename+"."+"1")
+
+	// list files in log folder, to rename older files
+	if files, err = ioutil.ReadDir(dirpath); err != nil {
+		return err
+	}
+
+	// rotate already existing backups
+	rotated = make([]string, maxfiles) // not really necessary, only max index should be enough...
+
+	for _, f := range files {
+		if !f.IsDir() && strings.HasPrefix(f.Name(), filename+".") {
+			if i, err := strconv.Atoi(strings.TrimPrefix(f.Name(), filename+".")); err == nil {
+				// keep
+				if i < maxfiles {
+					rotated[i] = path.Join(dirpath, f.Name())
+					// delete if index is higher than maxfiles
+				} else {
+					os.Remove(path.Join(dirpath, f.Name())) // ignore errs, not critical
+				}
+			}
+		}
+	}
+
+	// rotate older files, starting from highest index
+	for i := maxfiles - 1; i > 0; i -= 1 {
+		if fp := rotated[i]; fp != "" {
+			os.Rename(fp, path.Join(dirpath, filename+"."+strconv.Itoa(i+1)))
+		}
+	}
+
+	// close current file
+	if err = CloseFileOutput(); err != nil {
+		return err
+	}
+
+	// send messages to void until we reopen file
+	if err = OpenFileOutput("dev", "null"); err != nil {
+		return err
+	}
+
+	os.Rename(curr_filepath, new_filepath) // catch and return err does not makes sense, probably we should panic
+
+	CloseFileOutput()
+
+	return OpenFileOutput(dirpath, filename)
+}
+
+func SetLevel(l int) error {
+	var err error
+	if l >= 0 && l < len(levels) {
+		level = l
+	} else {
+		err = errors.New(errors.INVALID_PARAM, "level "+strconv.Itoa(l))
+	}
+	return err
+}
+
+func Log(lvl int, prefix, format string, vars ...interface{}) {
+	log.Printf(fmt.Sprintf(levels[lvl], prefix) + fmt.Sprintf(format, vars...))
+}
+
+func Trace(prefix, format string, vars ...interface{}) {
+	if level == 0 {
+		Log(0, prefix, format, vars...)
+	}
+}
+
+func Debug(prefix, format string, vars ...interface{}) {
+	if level <= 1 {
+		Log(1, prefix, format, vars...)
+	}
+}
+
+func Info(prefix, format string, vars ...interface{}) {
+	if level <= 2 {
+		Log(2, prefix, format, vars...)
+	}
+}
+
+func Warn(prefix, format string, vars ...interface{}) {
+	if level <= 3 {
+		Log(3, prefix, format, vars...)
+	}
+}
+
+func Error(prefix, format string, vars ...interface{}) {
+	if level <= 4 {
+		Log(4, prefix, format, vars...)
+	}
+}
+
+func Fatal(prefix, format string, vars ...interface{}) {
+	if level <= 5 {
+		Log(5, prefix, format, vars...)
+	}
+}
diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go
deleted file mode 100644
index 36a30663..00000000
--- a/pkg/logging/logger.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package logging
-
-import (
-	"github.com/rs/zerolog/pkgerrors"
-	"goharvest2/pkg/conf"
-	"io"
-	"os"
-	"path"
-	"sync"
-
-	"github.com/rs/zerolog"
-	"gopkg.in/natefinch/lumberjack.v2"
-)
-
-const (
-	defaultLogFileName           string = "harvest.log"
-	defaultLogLevel                     = zerolog.InfoLevel
-	defaultConsoleLoggingEnabled bool   = true
-	// DefaultFileLoggingEnabled is intentionally false to avoid opening many file descriptors for same log file
-	defaultFileLoggingEnabled bool = false
-	DefaultLogMaxMegaBytes    int  = 10 // 10MB
-	DefaultLogMaxBackups      int  = 10
-	DefaultLogMaxAge          int  = 30
-)
-
-// Configuration for logging
-type LogConfig struct {
-	// Enable console logging
-	ConsoleLoggingEnabled bool
-	// Log Level
-	LogLevel zerolog.Level
-	// Prefix
-	PrefixKey   string
-	PrefixValue string
-	// FileLoggingEnabled makes the framework log to a file
-	FileLoggingEnabled bool
-	// Directory to log to to when filelogging is enabled
-	Directory string
-	// Filename is the name of the logfile which will be placed inside the directory
-	Filename string
-	// MaxSize the max size in MB of the logfile before it's rolled
-	MaxSize int
-	// MaxBackups the max number of rolled files to keep
-	MaxBackups int
-	// MaxAge the max age in days to keep a logfile
-	MaxAge int
-}
-
-var logger *Logger
-
-var once sync.Once
-
-type Logger struct {
-	*zerolog.Logger
-}
-
-// Get Returns a logger with default configuration if logger is not initialized yet
-// default configuration only writes to console and not to file see param defaultFileLoggingEnabled
-func Get() *Logger {
-	once.Do(func() {
-		if logger == nil {
-			defaultPrefixKey := "harvest"
-			defaultPrefixValue := "harvest"
-			logConfig := LogConfig{ConsoleLoggingEnabled: defaultConsoleLoggingEnabled,
-				PrefixKey:          defaultPrefixKey,
-				PrefixValue:        defaultPrefixValue,
-				LogLevel:           defaultLogLevel,
-				FileLoggingEnabled: defaultFileLoggingEnabled,
-				Directory:          conf.GetHarvestLogPath(),
-				Filename:           defaultLogFileName,
-				MaxSize:            DefaultLogMaxMegaBytes,
-				MaxBackups:         DefaultLogMaxBackups,
-				MaxAge:             DefaultLogMaxAge}
-			logger = Configure(logConfig)
-		}
-	})
-	return logger
-}
-
-// SubLogger adds the field key with val as a string to the logger context and returns sublogger
-func SubLogger(key string, value string) *Logger {
-	logger := Get().With().Str(key, value).Logger()
-	subLogger := &Logger{
-		Logger: &logger,
-	}
-	return subLogger
-}
-
-// Configure sets up the logging framework
-func Configure(config LogConfig) *Logger {
-	var writers []io.Writer
-
-	if config.ConsoleLoggingEnabled {
-		// writers = append(writers, zerolog.ConsoleWriter{Out: os.Stderr})
-		writers = append(writers, os.Stdout)
-	}
-	if config.FileLoggingEnabled {
-		writers = append(writers, newRollingFile(config))
-	}
-	multiWriters := zerolog.MultiLevelWriter(writers...)
-
-	zerolog.SetGlobalLevel(config.LogLevel)
-	zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
-	zeroLogger := zerolog.New(multiWriters).With().Str(config.PrefixKey, config.PrefixValue).Timestamp().Logger()
-
-	zeroLogger.Debug().
-		Bool("consoleLoggingEnabled", config.ConsoleLoggingEnabled).
-		Bool("fileLogging", config.FileLoggingEnabled).
-		Str("loglevel", config.LogLevel.String()).
-		Str("prefixKey", config.PrefixKey).
-		Str("prefixValue", config.PrefixValue).
-		Str("logDirectory", config.Directory).
-		Str("fileName", config.Filename).
-		Int("maxSizeMB", config.MaxSize).
-		Int("maxBackups", config.MaxBackups).
-		Int("maxAgeInDays", config.MaxAge).
-		Msg("logging configured")
-
-	logger = &Logger{
-		Logger: &zeroLogger,
-	}
-	return logger
-}
-
-//returns lumberjack writer
-func newRollingFile(config LogConfig) io.Writer {
-	return &lumberjack.Logger{
-		Filename:   path.Join(config.Directory, config.Filename),
-		MaxBackups: config.MaxBackups, // files
-		MaxSize:    config.MaxSize,    // megabytes
-		MaxAge:     config.MaxAge,     // days
-		Compress:   true,
-	}
-}
-
-// GetZerologLevel returns log level mapping
-func GetZerologLevel(logLevel int) zerolog.Level {
-	switch logLevel {
-	case 0:
-		return zerolog.TraceLevel
-	case 1:
-		return zerolog.DebugLevel
-	case 2:
-		return zerolog.InfoLevel
-	case 3:
-		return zerolog.WarnLevel
-	case 4:
-		return zerolog.ErrorLevel
-	case 5:
-		return zerolog.FatalLevel
-	default:
-		return zerolog.InfoLevel
-	}
-}
diff --git a/rpm/build-rpm.sh b/rpm/build-rpm.sh
index 7a32d3a2..495513e1 100755
--- a/rpm/build-rpm.sh
+++ b/rpm/build-rpm.sh
@@ -24,7 +24,7 @@ cp -r "$SRC/docs/" "$BUILD/harvest/"
 cp -r "$SRC/conf/" "$BUILD/harvest/"
 cp -r "$SRC/rpm/" "$BUILD/harvest/"
 cp -r "$SRC/service/" "$BUILD/harvest/"
-cp "$SRC/harvest.yml" "$BUILD/harvest/"
+cp "$SRC/harvest.example.yml" "$BUILD/harvest/"
 cp "$SRC/go.mod" "$BUILD/harvest/"
 cp "$SRC/go.sum" "$BUILD/harvest/"
 if [ -d "$SRC/vendor" ]; then
diff --git a/rpm/spec b/rpm/spec
index 39261f86..b4be45df 100644
--- a/rpm/spec
+++ b/rpm/spec
@@ -30,11 +30,6 @@ if [ $1 = 2 ]; then
       systemctl stop harvest && echo "stopped harvest ..."
     fi
 fi
-# Backup existing harvest.yml to avoid overwrite from new binary
-if [ -e $RPM_BUILD_ROOT/opt/harvest/harvest.yml ]; then
-    cp $RPM_BUILD_ROOT/opt/harvest/harvest.yml $RPM_BUILD_ROOT/opt/harvest/backup_harvest.yml
-    echo "  --> Backing up existing config file as [/opt/harvest/backup_harvest.yml]"
-fi
 
 %install
 echo " --> unpacking package..."
@@ -47,18 +42,9 @@ mkdir -p $RPM_BUILD_ROOT/var/log/harvest
 mkdir -p $RPM_BUILD_ROOT/var/run/harvest
 
 %post
-# After installation/upgrade, copy latest harvest.yml as harvest.example.yml
-if [ -e $RPM_BUILD_ROOT/opt/harvest/harvest.yml ]; then
-    rm -rf $RPM_BUILD_ROOT/opt/harvest/harvest.example.yml
-    cp $RPM_BUILD_ROOT/opt/harvest/harvest.yml $RPM_BUILD_ROOT/opt/harvest/harvest.example.yml
-    echo " --> copy new harvest.yml as [/opt/harvest/harvest.example.yml]"
-fi
-# restore old harvest.yml in case of upgrade
-if [ -e $RPM_BUILD_ROOT/opt/harvest/backup_harvest.yml ]; then
-    rm -rf $RPM_BUILD_ROOT/opt/harvest/harvest.yml
-    cp $RPM_BUILD_ROOT/opt/harvest/backup_harvest.yml $RPM_BUILD_ROOT/opt/harvest/harvest.yml
-    rm -rf $RPM_BUILD_ROOT/opt/harvest/backup_harvest.yml
-    echo " --> restoring existing harvest.yml as [/opt/harvest/harvest.yml]"
+if [ ! -e $RPM_BUILD_ROOT/opt/harvest/harvest.yml ]; then
+    cp $RPM_BUILD_ROOT/opt/harvest/harvest.example.yml $RPM_BUILD_ROOT/opt/harvest/harvest.yml
+    echo "  --> use default config file [/opt/harvest/harvest.yml]"
 fi
 getent group harvest > /dev/null 2>&1 || groupadd -r harvest && echo "  --> create harvest group"
 getent passwd harvest > /dev/null 2>&1 || useradd -r -M -g harvest --shell=/sbin/nologin harvest && echo "  --> create harvest user"
@@ -79,9 +65,9 @@ systemctl start harvest.service
 systemctl enable harvest.service
 echo "  --> harvest service started through systemctl"
 if [ $1 = 1 ]; then
-    echo " --> install complete! harvest directory /opt/harvest"
+    echo " --> install complete!"
 else
-    echo " --> updated harvest! harvest directory /opt/harvest"
+    echo " --> updated harvest!"
 fi
 
 %preun
@@ -89,10 +75,6 @@ fi
 if [ $1 = 0 ]; then
     echo "stopping harvest ..."
     systemctl stop harvest
-    if [ -e /opt/harvest/harvest.yml ]; then
-      cp /opt/harvest/harvest.yml /opt/harvest/backup_harvest.yml
-      echo " --> Backing up existing config file as [/opt/harvest/backup_harvest.yml]"
-    fi
 fi
 
 %postun
@@ -108,13 +90,6 @@ if [ $1 = 0 ]; then
     rm /etc/systemd/system/harvest.service
     systemctl daemon-reload
     systemctl reset-failed
-    # restore harvest.yml
-    if [ -e /opt/harvest/backup_harvest.yml ]; then
-      rm -rf /opt/harvest/harvest.yml
-      cp /opt/harvest/backup_harvest.yml /opt/harvest/harvest.yml
-      rm -rf /opt/harvest/backup_harvest.yml
-      echo " --> restoring backup_harvest.yml as [/opt/harvest/harvest.yml]"
-    fi
     # keep logs at var/log/harvest
     echo " --> kept log folder"
     echo " --> uninstall complete"


Test patch:
diff --git a/cmd/exporters/influxdb/influxdb_test.go b/cmd/exporters/influxdb/influxdb_test.go
index f3360be7..d9cb9f7e 100644
--- a/cmd/exporters/influxdb/influxdb_test.go
+++ b/cmd/exporters/influxdb/influxdb_test.go
@@ -6,6 +6,7 @@ package main
 import (
 	"goharvest2/cmd/poller/exporter"
 	"goharvest2/cmd/poller/options"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"testing"
@@ -16,6 +17,8 @@ import (
 // rendered data
 func TestExportDebug(t *testing.T) {
 
+	logger.SetLevel(0)
+
 	opts := &options.Options{}
 	opts.Debug = true
 
diff --git a/cmd/poller/plugin/aggregator/aggregator_test.go b/cmd/poller/plugin/aggregator/aggregator_test.go
index 0363ecfc..ef3aea59 100644
--- a/cmd/poller/plugin/aggregator/aggregator_test.go
+++ b/cmd/poller/plugin/aggregator/aggregator_test.go
@@ -6,6 +6,7 @@ package aggregator
 
 import (
 	"goharvest2/cmd/poller/plugin"
+	"goharvest2/pkg/logger"
 	"goharvest2/pkg/matrix"
 	"goharvest2/pkg/tree/node"
 	"testing"
@@ -16,6 +17,8 @@ var m *matrix.Matrix
 
 func TestInitPlugin(t *testing.T) {
 
+	logger.SetLevel(0)
+
 	params := node.NewS("Aggregator")
 	params.NewChildS("", "node")
 
diff --git a/cmd/tools/doctor/doctor_test.go b/cmd/tools/doctor/doctor_test.go
index c03b7cea..755a9056 100644
--- a/cmd/tools/doctor/doctor_test.go
+++ b/cmd/tools/doctor/doctor_test.go
@@ -1,10 +1,7 @@
 package doctor
 
 import (
-	"fmt"
 	"goharvest2/pkg/conf"
-	"gopkg.in/yaml.v3"
-	"io/ioutil"
 	"strings"
 	"testing"
 )
@@ -31,34 +28,31 @@ func assertRedacted(t *testing.T, input, redacted string) {
 
 func TestConfigToStruct(t *testing.T) {
 	path := "testConfig.yml"
-	contents, err := ioutil.ReadFile(path)
+	err := conf.LoadHarvestConfig(path)
 	if err != nil {
-		fmt.Printf("error reading config file %+v\n", err)
 		return
 	}
-	harvestConfig := &conf.HarvestConfig{}
-	err = yaml.Unmarshal(contents, harvestConfig)
-	if err != nil {
-		fmt.Printf("error reading config file=[%s] %+v\n", path, err)
-		return
+	if conf.Config.Defaults.Password != "123#abc" {
+		t.Fatalf(`expected harvestConfig.Defaults.Password to be 123#abc, actual=[%+v]`,
+			conf.Config.Defaults.Addr)
 	}
 
-	if harvestConfig.Defaults.Addr != nil {
+	if conf.Config.Defaults.Addr != nil {
 		t.Fatalf(`expected harvestConfig.Defaults.Addr to be nil, actual=[%+v]`,
-			harvestConfig.Defaults.Addr)
+			conf.Config.Defaults.Addr)
 	}
-	if len(*harvestConfig.Defaults.Collectors) != 2 {
+	if len(*conf.Config.Defaults.Collectors) != 2 {
 		t.Fatalf(`expected two default collectors, actual=%+v`,
-			*harvestConfig.Defaults.Collectors)
+			*conf.Config.Defaults.Collectors)
 	}
 
-	allowedRegexes := (*harvestConfig.Exporters)["influxy"].AllowedAddrsRegex
+	allowedRegexes := (*conf.Config.Exporters)["influxy"].AllowedAddrsRegex
 	if (*allowedRegexes)[0] != "^192.168.0.\\d+$" {
 		t.Fatalf(`expected allow_addrs_regex to be ^192.168.0.\d+$ actual=%+v`,
 			(*allowedRegexes)[0])
 	}
 
-	collectors := (*harvestConfig.Pollers)["infinity2"].Collectors
+	collectors := (*conf.Config.Pollers)["infinity2"].Collectors
 	if (*collectors)[0] != "Zapi" {
 		t.Fatalf(`expected infinity2 collectors to contain Zapi actual=%+v`,
 			(*collectors)[0])
diff --git a/cmd/tools/doctor/testConfig.yml b/cmd/tools/doctor/testConfig.yml
index 9e548c60..8776b637 100644
--- a/cmd/tools/doctor/testConfig.yml
+++ b/cmd/tools/doctor/testConfig.yml
@@ -31,7 +31,7 @@ Defaults:
     - ZapiPerf
   use_insecure_tls: false
   username: myuser
-  password: mypasw
+  password: 123#abc
 
 Pollers:
   # this is a special/optional poller that provides stats about harvest itself