Make metrics better follow guidelines (#787)

* Improve stat linux metric names.

cpu is no longer used.

* node_cpu -> node_cpu_seconds_total for Linux

* Improve filesystem metric names with units

* Improve units and names of linux disk stats

Remove sector metrics, the bytes metrics cover those already.

* Infiniband counters should end in _total

* Improve timex metric names, convert to more normal units.

See
3c073991eb/kernel/time/ntp.c (L909)
for what stabil means, looks like a moving average of some form.

* Update test fixture

* For meminfo metrics that had "kB" units, add _bytes

* Interrupts counter should have _total
pull/796/head
Brian Brazil 2018-01-17 16:55:55 +00:00 committed by Ben Kochie
parent b4d7ba119a
commit a98067a294
12 changed files with 547 additions and 604 deletions

View File

@ -54,7 +54,7 @@ func init() {
func NewCPUCollector() (Collector, error) { func NewCPUCollector() (Collector, error) {
return &cpuCollector{ return &cpuCollector{
cpu: prometheus.NewDesc( cpu: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", cpuCollectorSubsystem), prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "seconds_total"),
"Seconds the cpus spent in each mode.", "Seconds the cpus spent in each mode.",
[]string{"cpu", "mode"}, nil, []string{"cpu", "mode"}, nil,
), ),

View File

@ -31,16 +31,29 @@ import (
const ( const (
diskSubsystem = "disk" diskSubsystem = "disk"
diskSectorSize uint64 = 512 diskSectorSize = 512
) )
var ( var (
ignoredDevices = kingpin.Flag("collector.diskstats.ignored-devices", "Regexp of devices to ignore for diskstats.").Default("^(ram|loop|fd|(h|s|v|xv)d[a-z]|nvme\\d+n\\d+p)\\d+$").String() ignoredDevices = kingpin.Flag("collector.diskstats.ignored-devices", "Regexp of devices to ignore for diskstats.").Default("^(ram|loop|fd|(h|s|v|xv)d[a-z]|nvme\\d+n\\d+p)\\d+$").String()
) )
type typedFactorDesc struct {
desc *prometheus.Desc
valueType prometheus.ValueType
factor float64
}
func (d *typedFactorDesc) mustNewConstMetric(value float64, labels ...string) prometheus.Metric {
if d.factor != 0 {
value *= d.factor
}
return prometheus.MustNewConstMetric(d.desc, d.valueType, value, labels...)
}
type diskstatsCollector struct { type diskstatsCollector struct {
ignoredDevicesPattern *regexp.Regexp ignoredDevicesPattern *regexp.Regexp
descs []typedDesc descs []typedFactorDesc
} }
func init() { func init() {
@ -54,10 +67,10 @@ func NewDiskstatsCollector() (Collector, error) {
return &diskstatsCollector{ return &diskstatsCollector{
ignoredDevicesPattern: regexp.MustCompile(*ignoredDevices), ignoredDevicesPattern: regexp.MustCompile(*ignoredDevices),
// Docs from https://www.kernel.org/doc/Documentation/iostats.txt // Docs from https://www.kernel.org/doc/Documentation/iostats.txt
descs: []typedDesc{ descs: []typedFactorDesc{
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "reads_completed"), prometheus.BuildFQName(namespace, diskSubsystem, "reads_completed_total"),
"The total number of reads completed successfully.", "The total number of reads completed successfully.",
diskLabelNames, diskLabelNames,
nil, nil,
@ -65,7 +78,7 @@ func NewDiskstatsCollector() (Collector, error) {
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "reads_merged"), prometheus.BuildFQName(namespace, diskSubsystem, "reads_merged_total"),
"The total number of reads merged. See https://www.kernel.org/doc/Documentation/iostats.txt.", "The total number of reads merged. See https://www.kernel.org/doc/Documentation/iostats.txt.",
diskLabelNames, diskLabelNames,
nil, nil,
@ -73,23 +86,25 @@ func NewDiskstatsCollector() (Collector, error) {
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "sectors_read"), prometheus.BuildFQName(namespace, diskSubsystem, "read_bytes_total"),
"The total number of sectors read successfully.", "The total number of bytes read successfully.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: diskSectorSize,
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "read_time_ms"), prometheus.BuildFQName(namespace, diskSubsystem, "read_time_seconds_total"),
"The total number of milliseconds spent by all reads.", "The total number of milliseconds spent by all reads.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: .001,
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "writes_completed"), prometheus.BuildFQName(namespace, diskSubsystem, "writes_completed_total"),
"The total number of writes completed successfully.", "The total number of writes completed successfully.",
diskLabelNames, diskLabelNames,
nil, nil,
@ -97,7 +112,7 @@ func NewDiskstatsCollector() (Collector, error) {
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "writes_merged"), prometheus.BuildFQName(namespace, diskSubsystem, "writes_merged_total"),
"The number of writes merged. See https://www.kernel.org/doc/Documentation/iostats.txt.", "The number of writes merged. See https://www.kernel.org/doc/Documentation/iostats.txt.",
diskLabelNames, diskLabelNames,
nil, nil,
@ -105,19 +120,21 @@ func NewDiskstatsCollector() (Collector, error) {
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "sectors_written"), prometheus.BuildFQName(namespace, diskSubsystem, "written_bytes_total"),
"The total number of sectors written successfully.", "The total number of bytes written successfully.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: diskSectorSize,
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "write_time_ms"), prometheus.BuildFQName(namespace, diskSubsystem, "write_time_seconds_total"),
"This is the total number of milliseconds spent by all writes.", "This is the total number of seconds spent by all writes.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: .001,
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
@ -129,35 +146,21 @@ func NewDiskstatsCollector() (Collector, error) {
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "io_time_ms"), prometheus.BuildFQName(namespace, diskSubsystem, "io_time_seconds_total"),
"Total Milliseconds spent doing I/Os.", "Total seconds spent doing I/Os.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: .001,
}, },
{ {
desc: prometheus.NewDesc( desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "io_time_weighted"), prometheus.BuildFQName(namespace, diskSubsystem, "io_time_weighted_seconds_total"),
"The weighted # of milliseconds spent doing I/Os. See https://www.kernel.org/doc/Documentation/iostats.txt.", "The weighted # of seconds spent doing I/Os. See https://www.kernel.org/doc/Documentation/iostats.txt.",
diskLabelNames,
nil,
), valueType: prometheus.CounterValue,
},
{
desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "bytes_read"),
"The total number of bytes read successfully.",
diskLabelNames,
nil,
), valueType: prometheus.CounterValue,
},
{
desc: prometheus.NewDesc(
prometheus.BuildFQName(namespace, diskSubsystem, "bytes_written"),
"The total number of bytes written successfully.",
diskLabelNames, diskLabelNames,
nil, nil,
), valueType: prometheus.CounterValue, ), valueType: prometheus.CounterValue,
factor: .001,
}, },
}, },
}, nil }, nil
@ -201,15 +204,6 @@ func getDiskStats() (map[string]map[int]string, error) {
return parseDiskStats(file) return parseDiskStats(file)
} }
func convertDiskSectorsToBytes(sectorCount string) (string, error) {
sectors, err := strconv.ParseUint(sectorCount, 10, 64)
if err != nil {
return "", err
}
return strconv.FormatUint(sectors*diskSectorSize, 10), nil
}
func parseDiskStats(r io.Reader) (map[string]map[int]string, error) { func parseDiskStats(r io.Reader) (map[string]map[int]string, error) {
var ( var (
diskStats = map[string]map[int]string{} diskStats = map[string]map[int]string{}
@ -226,17 +220,6 @@ func parseDiskStats(r io.Reader) (map[string]map[int]string, error) {
for i, v := range parts[3:] { for i, v := range parts[3:] {
diskStats[dev][i] = v diskStats[dev][i] = v
} }
bytesRead, err := convertDiskSectorsToBytes(diskStats[dev][2])
if err != nil {
return nil, fmt.Errorf("invalid value for sectors read in %s: %s", procFilePath("diskstats"), scanner.Text())
}
diskStats[dev][11] = bytesRead
bytesWritten, err := convertDiskSectorsToBytes(diskStats[dev][6])
if err != nil {
return nil, fmt.Errorf("invalid value for sectors written in %s: %s", procFilePath("diskstats"), scanner.Text())
}
diskStats[dev][12] = bytesWritten
} }
return diskStats, scanner.Err() return diskStats, scanner.Err()

View File

@ -37,12 +37,4 @@ func TestDiskStats(t *testing.T) {
if want, got := "68", diskStats["mmcblk0p2"][10]; want != got { if want, got := "68", diskStats["mmcblk0p2"][10]; want != got {
t.Errorf("want diskstats mmcblk0p2 %s, got %s", want, got) t.Errorf("want diskstats mmcblk0p2 %s, got %s", want, got)
} }
if want, got := "513713216512", diskStats["sda"][11]; want != got {
t.Errorf("want diskstats sda read bytes %s, got %s", want, got)
}
if want, got := "258916880384", diskStats["sda"][12]; want != got {
t.Errorf("want diskstats sda write bytes %s, got %s", want, got)
}
} }

View File

@ -72,19 +72,19 @@ func NewFilesystemCollector() (Collector, error) {
filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes) filesystemsTypesPattern := regexp.MustCompile(*ignoredFSTypes)
sizeDesc := prometheus.NewDesc( sizeDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "size"), prometheus.BuildFQName(namespace, subsystem, "size_bytes"),
"Filesystem size in bytes.", "Filesystem size in bytes.",
filesystemLabelNames, nil, filesystemLabelNames, nil,
) )
freeDesc := prometheus.NewDesc( freeDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "free"), prometheus.BuildFQName(namespace, subsystem, "free_bytes"),
"Filesystem free space in bytes.", "Filesystem free space in bytes.",
filesystemLabelNames, nil, filesystemLabelNames, nil,
) )
availDesc := prometheus.NewDesc( availDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "avail"), prometheus.BuildFQName(namespace, subsystem, "avail_bytes"),
"Filesystem space available to non-root users in bytes.", "Filesystem space available to non-root users in bytes.",
filesystemLabelNames, nil, filesystemLabelNames, nil,
) )

File diff suppressed because it is too large Load Diff

View File

@ -57,8 +57,8 @@ func NewInfiniBandCollector() (Collector, error) {
"link_error_recovery_total": {"link_error_recovery", "Number of times the link successfully recovered from an error state"}, "link_error_recovery_total": {"link_error_recovery", "Number of times the link successfully recovered from an error state"},
"multicast_packets_received_total": {"multicast_rcv_packets", "Number of multicast packets received (including errors)"}, "multicast_packets_received_total": {"multicast_rcv_packets", "Number of multicast packets received (including errors)"},
"multicast_packets_transmitted_total": {"multicast_xmit_packets", "Number of multicast packets transmitted (including errors)"}, "multicast_packets_transmitted_total": {"multicast_xmit_packets", "Number of multicast packets transmitted (including errors)"},
"port_data_received_bytes": {"port_rcv_data", "Number of data octets received on all links"}, "port_data_received_bytes_total": {"port_rcv_data", "Number of data octets received on all links"},
"port_data_transmitted_bytes": {"port_xmit_data", "Number of data octets transmitted on all links"}, "port_data_transmitted_bytes_total": {"port_xmit_data", "Number of data octets transmitted on all links"},
"unicast_packets_received_total": {"unicast_rcv_packets", "Number of unicast packets received (including errors)"}, "unicast_packets_received_total": {"unicast_rcv_packets", "Number of unicast packets received (including errors)"},
"unicast_packets_transmitted_total": {"unicast_xmit_packets", "Number of unicast packets transmitted (including errors)"}, "unicast_packets_transmitted_total": {"unicast_xmit_packets", "Number of unicast packets transmitted (including errors)"},
} }

View File

@ -30,7 +30,7 @@ func init() {
func NewInterruptsCollector() (Collector, error) { func NewInterruptsCollector() (Collector, error) {
return &interruptsCollector{ return &interruptsCollector{
desc: typedDesc{prometheus.NewDesc( desc: typedDesc{prometheus.NewDesc(
namespace+"_interrupts", namespace+"_interrupts_total",
"Interrupt details.", "Interrupt details.",
interruptLabelNames, nil, interruptLabelNames, nil,
), prometheus.CounterValue}, ), prometheus.CounterValue},

View File

@ -49,16 +49,17 @@ func parseMemInfo(r io.Reader) (map[string]float64, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid value in meminfo: %s", err) return nil, fmt.Errorf("invalid value in meminfo: %s", err)
} }
key := parts[0][:len(parts[0])-1] // remove trailing : from key
// Active(anon) -> Active_anon
key = re.ReplaceAllString(key, "_${1}")
switch len(parts) { switch len(parts) {
case 2: // no unit case 2: // no unit
case 3: // has unit, we presume kB case 3: // has unit, we presume kB
fv *= 1024 fv *= 1024
key = key + "_bytes"
default: default:
return nil, fmt.Errorf("invalid line in meminfo: %s", line) return nil, fmt.Errorf("invalid line in meminfo: %s", line)
} }
key := parts[0][:len(parts[0])-1] // remove trailing : from key
// Active(anon) -> Active_anon
key = re.ReplaceAllString(key, "_${1}")
memInfo[key] = fv memInfo[key] = fv
} }

View File

@ -30,11 +30,11 @@ func TestMemInfo(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if want, got := 3831959552.0, memInfo["MemTotal"]; want != got { if want, got := 3831959552.0, memInfo["MemTotal_bytes"]; want != got {
t.Errorf("want memory total %f, got %f", want, got) t.Errorf("want memory total %f, got %f", want, got)
} }
if want, got := 3787456512.0, memInfo["DirectMap2M"]; want != got { if want, got := 3787456512.0, memInfo["DirectMap2M_bytes"]; want != got {
t.Errorf("want memory directMap2M %f, got %f", want, got) t.Errorf("want memory directMap2M %f, got %f", want, got)
} }
} }

View File

@ -24,7 +24,6 @@ import (
) )
type statCollector struct { type statCollector struct {
cpu *prometheus.Desc
intr *prometheus.Desc intr *prometheus.Desc
ctxt *prometheus.Desc ctxt *prometheus.Desc
forks *prometheus.Desc forks *prometheus.Desc
@ -40,28 +39,23 @@ func init() {
// NewStatCollector returns a new Collector exposing kernel/system statistics. // NewStatCollector returns a new Collector exposing kernel/system statistics.
func NewStatCollector() (Collector, error) { func NewStatCollector() (Collector, error) {
return &statCollector{ return &statCollector{
cpu: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "cpu"),
"Seconds the cpus spent in each mode.",
[]string{"cpu", "mode"}, nil,
),
intr: prometheus.NewDesc( intr: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "intr"), prometheus.BuildFQName(namespace, "", "intr_total"),
"Total number of interrupts serviced.", "Total number of interrupts serviced.",
nil, nil, nil, nil,
), ),
ctxt: prometheus.NewDesc( ctxt: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "context_switches"), prometheus.BuildFQName(namespace, "", "context_switches_total"),
"Total number of context switches.", "Total number of context switches.",
nil, nil, nil, nil,
), ),
forks: prometheus.NewDesc( forks: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "forks"), prometheus.BuildFQName(namespace, "", "forks_total"),
"Total number of forks.", "Total number of forks.",
nil, nil, nil, nil,
), ),
btime: prometheus.NewDesc( btime: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "", "boot_time"), prometheus.BuildFQName(namespace, "", "boot_time_seconds"),
"Node boot time, in unixtime.", "Node boot time, in unixtime.",
nil, nil, nil, nil,
), ),

View File

@ -71,7 +71,7 @@ func NewTimexCollector() (Collector, error) {
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
freq: typedDesc{prometheus.NewDesc( freq: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "frequency_adjustment"), prometheus.BuildFQName(namespace, subsystem, "frequency_adjustment_ratio"),
"Local clock frequency adjustment.", "Local clock frequency adjustment.",
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
@ -101,7 +101,7 @@ func NewTimexCollector() (Collector, error) {
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
ppsfreq: typedDesc{prometheus.NewDesc( ppsfreq: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_frequency"), prometheus.BuildFQName(namespace, subsystem, "pps_frequency_hertz"),
"Pulse per second frequency.", "Pulse per second frequency.",
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
@ -116,32 +116,32 @@ func NewTimexCollector() (Collector, error) {
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
stabil: typedDesc{prometheus.NewDesc( stabil: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_stability"), prometheus.BuildFQName(namespace, subsystem, "pps_stability_hertz"),
"Pulse per second stability.", "Pulse per second stability, average of recent frequency changes.",
nil, nil, nil, nil,
), prometheus.CounterValue}, ), prometheus.GaugeValue},
jitcnt: typedDesc{prometheus.NewDesc( jitcnt: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_jitter_count"), prometheus.BuildFQName(namespace, subsystem, "pps_jitter_total"),
"Pulse per second count of jitter limit exceeded events.", "Pulse per second count of jitter limit exceeded events.",
nil, nil, nil, nil,
), prometheus.CounterValue}, ), prometheus.CounterValue},
calcnt: typedDesc{prometheus.NewDesc( calcnt: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_calibration_count"), prometheus.BuildFQName(namespace, subsystem, "pps_calibration_total"),
"Pulse per second count of calibration intervals.", "Pulse per second count of calibration intervals.",
nil, nil, nil, nil,
), prometheus.CounterValue}, ), prometheus.CounterValue},
errcnt: typedDesc{prometheus.NewDesc( errcnt: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_error_count"), prometheus.BuildFQName(namespace, subsystem, "pps_error_total"),
"Pulse per second count of calibration errors.", "Pulse per second count of calibration errors.",
nil, nil, nil, nil,
), prometheus.CounterValue}, ), prometheus.CounterValue},
stbcnt: typedDesc{prometheus.NewDesc( stbcnt: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "pps_stability_exceeded_count"), prometheus.BuildFQName(namespace, subsystem, "pps_stability_exceeded_total"),
"Pulse per second count of stability limit exceeded events.", "Pulse per second count of stability limit exceeded events.",
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.CounterValue},
tai: typedDesc{prometheus.NewDesc( tai: typedDesc{prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "tai_offset"), prometheus.BuildFQName(namespace, subsystem, "tai_offset_seconds"),
"International Atomic Time (TAI) offset.", "International Atomic Time (TAI) offset.",
nil, nil, nil, nil,
), prometheus.GaugeValue}, ), prometheus.GaugeValue},
@ -173,18 +173,21 @@ func (c *timexCollector) Update(ch chan<- prometheus.Metric) error {
} else { } else {
divisor = microSeconds divisor = microSeconds
} }
// See NOTES in adjtimex(2).
const ppm16frac = 1000000.0 * 65536.0
ch <- c.syncStatus.mustNewConstMetric(syncStatus) ch <- c.syncStatus.mustNewConstMetric(syncStatus)
ch <- c.offset.mustNewConstMetric(float64(timex.Offset) / divisor) ch <- c.offset.mustNewConstMetric(float64(timex.Offset) / divisor)
ch <- c.freq.mustNewConstMetric(float64(timex.Freq)) ch <- c.freq.mustNewConstMetric(1 + float64(timex.Freq)/ppm16frac)
ch <- c.maxerror.mustNewConstMetric(float64(timex.Maxerror) / microSeconds) ch <- c.maxerror.mustNewConstMetric(float64(timex.Maxerror) / microSeconds)
ch <- c.esterror.mustNewConstMetric(float64(timex.Esterror) / microSeconds) ch <- c.esterror.mustNewConstMetric(float64(timex.Esterror) / microSeconds)
ch <- c.status.mustNewConstMetric(float64(timex.Status)) ch <- c.status.mustNewConstMetric(float64(timex.Status))
ch <- c.constant.mustNewConstMetric(float64(timex.Constant)) ch <- c.constant.mustNewConstMetric(float64(timex.Constant))
ch <- c.tick.mustNewConstMetric(float64(timex.Tick) / microSeconds) ch <- c.tick.mustNewConstMetric(float64(timex.Tick) / microSeconds)
ch <- c.ppsfreq.mustNewConstMetric(float64(timex.Ppsfreq)) ch <- c.ppsfreq.mustNewConstMetric(float64(timex.Ppsfreq) / ppm16frac)
ch <- c.jitter.mustNewConstMetric(float64(timex.Jitter) / divisor) ch <- c.jitter.mustNewConstMetric(float64(timex.Jitter) / divisor)
ch <- c.shift.mustNewConstMetric(float64(timex.Shift)) ch <- c.shift.mustNewConstMetric(float64(timex.Shift))
ch <- c.stabil.mustNewConstMetric(float64(timex.Stabil)) ch <- c.stabil.mustNewConstMetric(float64(timex.Stabil) / ppm16frac)
ch <- c.jitcnt.mustNewConstMetric(float64(timex.Jitcnt)) ch <- c.jitcnt.mustNewConstMetric(float64(timex.Jitcnt))
ch <- c.calcnt.mustNewConstMetric(float64(timex.Calcnt)) ch <- c.calcnt.mustNewConstMetric(float64(timex.Calcnt))
ch <- c.errcnt.mustNewConstMetric(float64(timex.Errcnt)) ch <- c.errcnt.mustNewConstMetric(float64(timex.Errcnt))