<commit_before>package proberlist

import (
	"fmt"
	"github.com/Symantec/health-agent/lib/prober"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
	"io"
	"log"
	"time"
)

var (
	latencyBucketer = tricorder.NewGeometricBucketer(0.1, 100e3)
)

func newProberList(proberPath string) *ProberList {
	pl := &ProberList{
		proberPath:            proberPath,
		probeTimeDistribution: latencyBucketer.NewCumulativeDistribution(),
	}
	if err := tricorder.RegisterMetric(proberPath+"/probe-duration",
		pl.probeTimeDistribution, units.Millisecond,
		"duration of last probe"); err != nil {
		panic(err)
	}
	if err := tricorder.RegisterMetric(proberPath+"/probe-start-time",
		&pl.probeStartTime, units.None,
		"start time of last probe"); err != nil {
		panic(err)
	}
	return pl
}

func (pl *ProberList) add(registerProber RegisterProber, path string,
	probeInterval uint8) {
	if err := registerProber.Register(mkdir(path)); err != nil {
		panic(err)
	}
	pl.addProber(registerProber, path, probeInterval)
}

func (pl *ProberList) addProber(genericProber prober.Prober, path string,
	probeInterval uint8) {
	newProber := proberType{
		prober:                genericProber,
		probeInterval:         time.Duration(probeInterval) * time.Second,
		probeTimeDistribution: latencyBucketer.NewCumulativeDistribution(),
	}
	if err := tricorder.RegisterMetric(pl.proberPath+path+"/probe-duration",
		newProber.probeTimeDistribution, units.Millisecond,
		"duration of last probe"); err != nil {
		panic(err)
	}
	pl.probers = append(pl.probers, newProber)
}

func (pl *ProberList) startProbing(defaultProbeInterval uint,
	logger *log.Logger) {
	for _, p := range pl.probers {
		if p.probeInterval > 0 {
			go p.proberLoop(logger)
		}
	}
	go pl.proberLoop(defaultProbeInterval, logger)
}

func (pl *ProberList) proberLoop(probeInterval uint, logger *log.Logger) {
	for {
		probeStartTime := time.Now()
		pl.probe(logger)
		probeDuration := time.Since(probeStartTime)
		time.Sleep(time.Second*time.Duration(probeInterval) - probeDuration)
	}
}

func (pl *ProberList) probe(logger *log.Logger) {
	pl.probeStartTime = time.Now()
	for _, p := range pl.probers {
		if p.probeInterval > 0 { // Handled by a dedicated goroutine.
			continue
		}
		startTime := time.Now()
		if err := p.prober.Probe(); err != nil {
			logger.Println(err)
		}
		p.probeTimeDistribution.Add(time.Since(startTime))
	}
	pl.probeTimeDistribution.Add(time.Since(pl.probeStartTime))
}

func (pl *ProberList) writeHtml(writer io.Writer) {
	for _, p := range pl.probers {
		if htmler, ok := p.prober.(HtmlWriter); ok {
			htmler.WriteHtml(writer)
			fmt.Fprintln(writer, "<br>")
		}
	}
}

func (p proberType) proberLoop(logger *log.Logger) {
	for {
		probeStartTime := time.Now()
		if err := p.prober.Probe(); err != nil {
			logger.Println(err)
		}
		probeDuration := time.Since(probeStartTime)
		p.probeTimeDistribution.Add(probeDuration)
		time.Sleep(p.probeInterval - probeDuration)
	}
}

func mkdir(name string) *tricorder.DirectorySpec {
	dir, err := tricorder.RegisterDirectory(name)
	if err != nil {
		panic(err)
	}
	return dir<commit_msg>Start probers with custom intervals fast and gradually slow down.<commit_after>package proberlist

import (
	"fmt"
	"github.com/Symantec/health-agent/lib/prober"
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
	"io"
	"log"
	"time"
)

var (
	latencyBucketer = tricorder.NewGeometricBucketer(0.1, 100e3)
)

func newProberList(proberPath string) *ProberList {
	pl := &ProberList{
		proberPath:            proberPath,
		probeTimeDistribution: latencyBucketer.NewCumulativeDistribution(),
	}
	if err := tricorder.RegisterMetric(proberPath+"/probe-duration",
		pl.probeTimeDistribution, units.Millisecond,
		"duration of last probe"); err != nil {
		panic(err)
	}
	if err := tricorder.RegisterMetric(proberPath+"/probe-start-time",
		&pl.probeStartTime, units.None,
		"start time of last probe"); err != nil {
		panic(err)
	}
	return pl
}

func (pl *ProberList) add(registerProber RegisterProber, path string,
	probeInterval uint8) {
	if err := registerProber.Register(mkdir(path)); err != nil {
		panic(err)
	}
	pl.addProber(registerProber, path, probeInterval)
}

func (pl *ProberList) addProber(genericProber prober.Prober, path string,
	probeInterval uint8) {
	newProber := proberType{
		prober:                genericProber,
		probeInterval:         time.Duration(probeInterval) * time.Second,
		probeTimeDistribution: latencyBucketer.NewCumulativeDistribution(),
	}
	if err := tricorder.RegisterMetric(pl.proberPath+path+"/probe-duration",
		newProber.probeTimeDistribution, units.Millisecond,
		"duration of last probe"); err != nil {
		panic(err)
	}
	pl.probers = append(pl.probers, newProber)
}

func (pl *ProberList) startProbing(defaultProbeInterval uint,
	logger *log.Logger) {
	for _, p := range pl.probers {
		if p.probeInterval > 0 {
			go p.proberLoop(defaultProbeInterval, logger)
		}
	}
	go pl.proberLoop(defaultProbeInterval, logger)
}

func (pl *ProberList) proberLoop(probeInterval uint, logger *log.Logger) {
	for {
		probeStartTime := time.Now()
		pl.probe(logger)
		probeDuration := time.Since(probeStartTime)
		time.Sleep(time.Second*time.Duration(probeInterval) - probeDuration)
	}
}

func (pl *ProberList) probe(logger *log.Logger) {
	pl.probeStartTime = time.Now()
	for _, p := range pl.probers {
		if p.probeInterval > 0 { // Handled by a dedicated goroutine.
			continue
		}
		startTime := time.Now()
		if err := p.prober.Probe(); err != nil {
			logger.Println(err)
		}
		p.probeTimeDistribution.Add(time.Since(startTime))
	}
	pl.probeTimeDistribution.Add(time.Since(pl.probeStartTime))
}

func (pl *ProberList) writeHtml(writer io.Writer) {
	for _, p := range pl.probers {
		if htmler, ok := p.prober.(HtmlWriter); ok {
			htmler.WriteHtml(writer)
			fmt.Fprintln(writer, "<br>")
		}
	}
}

func (p proberType) proberLoop(defaultProbeInterval uint, logger *log.Logger) {
	// Set the initial probe interval to the global default, if less than the
	// interval for this prober. The probe interval will be gradually increased
	// until the target probe interval is reached. This gives faster probing at
	// startup when higher resolution may be helpful, and then backs off.
	probeInterval := time.Duration(defaultProbeInterval) * time.Second
	if probeInterval > p.probeInterval {
		probeInterval = p.probeInterval
	}
	for {
		probeStartTime := time.Now()
		if err := p.prober.Probe(); err != nil {
			logger.Println(err)
		}
		probeDuration := time.Since(probeStartTime)
		p.probeTimeDistribution.Add(probeDuration)
		time.Sleep(probeInterval - probeDuration)
		// Increase the probe interval until the interval for this prober is
		// reached.
		if probeInterval < p.probeInterval {
			probeInterval += time.Duration(defaultProbeInterval) * time.Second
		}
		if probeInterval > p.probeInterval {
			probeInterval = p.probeInterval
		}
	}
}

func mkdir(name string) *tricorder.DirectorySpec {
	dir, err := tricorder.RegisterDirectory(name)
	if err != nil {
		panic(err)
	}
	return dir
{'fraction_non_alphanumeric': 0.12433042007330138, 'fraction_numerical': 0.00281928390188892, 'mean_word_length': 4.375, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4516333', 'n_tokens_mistral': 2598, 'n_tokens_neox': 2438, 'n_words': 519}