Skip to content

Commit

Permalink
Add serverstats metrics collector (#62)
Browse files Browse the repository at this point in the history
* Add serverstats collector

---------

Signed-off-by: daerSeebaer <[email protected]>
Signed-off-by: unknown <[email protected]>
Co-authored-by: Ben Kochie <[email protected]>
  • Loading branch information
daerSeebaer and SuperQ authored Feb 18, 2024
1 parent a40281d commit ce48a94
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Supported parameters include:
- `--chrony.address`: the address/port (UDP) or path to Unix socket used to connect to chrony (default: `"[::1]:323"`)
- `--collector.sources`: Enable/disable the collection of `chronyc sources` metrics. (Default: Disabled)
- `--collector.tracking`: Enable/disable the collection of `chronyc tracking` metrics. (Default: Enabled)
- `--collector.serverstats`: Enable/disable the collection of `chronyc serverstats` metrics. This collector only works when chrony is accessed through the Unix socket. (Default: Disabled)
- `--collector.dns-lookups`: Enable/disable reverse DNS Lookups. (Default: Enabled)

To disable a collector, use `--no-`. (i.e. `--no-collector.tracking`)
Expand Down
28 changes: 20 additions & 8 deletions collector/collector.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ type Exporter struct {
address string
timeout time.Duration

collectSources bool
collectTracking bool
chmodSocket bool
dnsLookups bool
collectSources bool
collectTracking bool
collectServerstats bool
chmodSocket bool
dnsLookups bool

logger log.Logger
}
Expand Down Expand Up @@ -82,17 +83,20 @@ type ChronyCollectorConfig struct {
CollectSources bool
// CollectTracking will configure the exporter to collect `chronyc tracking`.
CollectTracking bool
// CollectServerstats will configure the exporter to collect `chronyc serverstats`.
CollectServerstats bool
}

func NewExporter(conf ChronyCollectorConfig, logger log.Logger) Exporter {
return Exporter{
address: conf.Address,
timeout: conf.Timeout,

collectSources: conf.CollectSources,
collectTracking: conf.CollectTracking,
chmodSocket: conf.ChmodSocket,
dnsLookups: conf.DNSLookups,
collectSources: conf.CollectSources,
collectTracking: conf.CollectTracking,
collectServerstats: conf.CollectServerstats,
chmodSocket: conf.ChmodSocket,
dnsLookups: conf.DNSLookups,

logger: logger,
}
Expand Down Expand Up @@ -162,4 +166,12 @@ func (e Exporter) Collect(ch chan<- prometheus.Metric) {
up = 0
}
}

if e.collectServerstats {
err = e.getServerstatsMetrics(ch, client)
if err != nil {
level.Debug(e.logger).Log("msg", "Couldn't get serverstats", "err", err)
up = 0
}
}
}
186 changes: 186 additions & 0 deletions collector/serverstats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright 2024 Ben Kochie
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package collector

import (
"fmt"

"github.com/facebook/time/ntp/chrony"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
)

const (
serverstatsSubsystem = "serverstats"
)

var (
serverstatsNTPHits = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "ntp_packets_received_total"),
"The number of valid NTP requests received by the server.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNKEHits = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "nts_ke_connections_accepted_total"),
"The number of NTS-KE connections accepted by the server.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsCMDHits = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "command_packets_received_total"),
"The number of command requests received by the server.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNTPDrops = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "ntp_packets_dropped_total"),
"The number of NTP requests dropped by the server due to rate limiting.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNKEDrops = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "nts_ke_connections_dropped_total"),
"The number of NTS-KE connections dropped by the server due to rate limiting.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsCMDDrops = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "command_packets_dropped_total"),
"The number of command requests dropped by the server due to rate limiting.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsLogDrops = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "client_log_records_dropped_total"),
"The number of client log records dropped by the server to limit the memory use.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNTPAuthHits = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "auhtenticated_ntp_packets_total"),
"The number of received NTP requests that were authenticated (with a symmetric key or NTS).",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNTPInterleavedHits = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "interleaved_ntp_packets_total"),
"The number of received NTP requests that were detected to be in the interleaved mode.",
nil,
nil,
),
prometheus.CounterValue,
}

serverstatsNTPTimestamps = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "ntp_timestamps_held"),
"The number of pairs of receive and transmit timestamps that the server is currently holding in memory for clients using the interleaved mode.",
nil,
nil,
),
prometheus.GaugeValue,
}

serverstatsNTPSpanSeconds = typedDesc{
prometheus.NewDesc(
prometheus.BuildFQName(namespace, serverstatsSubsystem, "ntp_timestamp_span_seconds"),
"The interval (in seconds) covered by the currently held NTP timestamps.",
nil,
nil,
),
prometheus.GaugeValue,
}
)

func (e Exporter) getServerstatsMetrics(ch chan<- prometheus.Metric, client chrony.Client) error {
packet, err := client.Communicate(chrony.NewServerStatsPacket())
if err != nil {
return err
}
level.Debug(e.logger).Log("msg", "Got 'serverstats' response", "serverstats_packet", packet.GetStatus())

serverstats, ok := packet.(*chrony.ReplyServerStats3)
if !ok {
return fmt.Errorf("got wrong 'serverstats' response: %q", packet)
}

ch <- serverstatsNTPHits.mustNewConstMetric(float64(serverstats.NTPHits))
level.Debug(e.logger).Log("msg", "Serverstats NTP Hits", "ntp_hits", serverstats.NTPHits)

ch <- serverstatsNKEHits.mustNewConstMetric(float64(serverstats.NKEHits))
level.Debug(e.logger).Log("msg", "Serverstats NKE Hits", "nke_hits", serverstats.NKEHits)

ch <- serverstatsCMDHits.mustNewConstMetric(float64(serverstats.CMDHits))
level.Debug(e.logger).Log("msg", "Serverstats CMD Hits", "cmd_hits", serverstats.CMDHits)

ch <- serverstatsNTPDrops.mustNewConstMetric(float64(serverstats.NTPDrops))
level.Debug(e.logger).Log("msg", "Serverstats NTP Drops", "ntp_drops", serverstats.NTPDrops)

ch <- serverstatsNKEDrops.mustNewConstMetric(float64(serverstats.NKEDrops))
level.Debug(e.logger).Log("msg", "Serverstats NKE Drops", "nke_drops", serverstats.NKEDrops)

ch <- serverstatsCMDDrops.mustNewConstMetric(float64(serverstats.CMDDrops))
level.Debug(e.logger).Log("msg", "Serverstats CMD Drops", "cmd_drops", serverstats.CMDDrops)

ch <- serverstatsLogDrops.mustNewConstMetric(float64(serverstats.LogDrops))
level.Debug(e.logger).Log("msg", "Serverstats Log Drops", "log_drops", serverstats.LogDrops)

ch <- serverstatsNTPAuthHits.mustNewConstMetric(float64(serverstats.NTPAuthHits))
level.Debug(e.logger).Log("msg", "Serverstats Authenticated Packets", "auth_hits", serverstats.NTPAuthHits)

ch <- serverstatsNTPInterleavedHits.mustNewConstMetric(float64(serverstats.NTPInterleavedHits))
level.Debug(e.logger).Log("msg", "Serverstats Interleaved Packets", "interleaved_hits", serverstats.NTPInterleavedHits)

ch <- serverstatsNTPTimestamps.mustNewConstMetric(float64(serverstats.NTPTimestamps))
level.Debug(e.logger).Log("msg", "Serverstats Timestamps Held", "ntp_timestamps_held", serverstats.NTPTimestamps)

ch <- serverstatsNTPSpanSeconds.mustNewConstMetric(float64(serverstats.NTPSpanSeconds))
level.Debug(e.logger).Log("msg", "Serverstats Timestamps Span", "ntp_timestamps_span", serverstats.NTPSpanSeconds)

return nil
}
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func main() {
"Collect sources metrics",
).Default("false").BoolVar(&conf.CollectSources)

kingpin.Flag(
"collector.serverstats",
"Collect serverstats metrics",
).Default("false").BoolVar(&conf.CollectServerstats)

kingpin.Flag(
"collector.chmod-socket",
"Chmod 0666 the receiving unix datagram socket",
Expand Down

0 comments on commit ce48a94

Please sign in to comment.