diff --git a/go.mod b/go.mod index c6b3db8cff052..e1bc853c5fc26 100644 --- a/go.mod +++ b/go.mod @@ -171,7 +171,7 @@ require ( github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sensu/sensu-go/api/core/v2 v2.16.0 github.com/shirou/gopsutil/v3 v3.24.4 - github.com/showwin/speedtest-go v1.6.10 + github.com/showwin/speedtest-go v1.7.5 github.com/signalfx/golib/v3 v3.3.53 github.com/sirupsen/logrus v1.9.3 github.com/sleepinggenius2/gosmi v0.4.4 diff --git a/go.sum b/go.sum index 47c117aa60914..425be0c4dd62b 100644 --- a/go.sum +++ b/go.sum @@ -2128,8 +2128,8 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/showwin/speedtest-go v1.6.10 h1:dPxr1gVOu30KvMNl2L8UZD937Ge7zsZW0JulzYpyP48= -github.com/showwin/speedtest-go v1.6.10/go.mod h1:uLgdWCNarXxlYsL2E5TOZpCIwpgSWnEANZp7gfHXHu0= +github.com/showwin/speedtest-go v1.7.5 h1:FQ3EdM2vnfw5BRCRzGCYe8aWu70rr21Az5ZFHiW9CdE= +github.com/showwin/speedtest-go v1.7.5/go.mod h1:uLgdWCNarXxlYsL2E5TOZpCIwpgSWnEANZp7gfHXHu0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3 h1:32k2QLgsKhcEs55q4REPKyIadvid5FPy2+VMgvbmKJ0= github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3/go.mod h1:gJrXWi7wSGXfiC7+VheQaz+ypdCt5SmZNL+BRxUe7y4= diff --git a/plugins/inputs/internet_speed/README.md b/plugins/inputs/internet_speed/README.md index c1844d408b691..e9a7d68e5d3ac 100644 --- a/plugins/inputs/internet_speed/README.md +++ b/plugins/inputs/internet_speed/README.md @@ -45,6 +45,7 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. ## By default, a single sever is used for testing. This may work for most, ## however, setting to "multi" will reach out to multiple servers in an ## attempt to get closer to ideal internet speeds. + ## And "multi" will use all available servers to calculate average packet loss. # test_mode = "single" ## Server ID exclude filter @@ -63,18 +64,21 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details. It collects the following fields: -| Name | field name | type | Unit | -|----------------|------------| ------- | ---- | -| Download Speed | download | float64 | Mbps | -| Upload Speed | upload | float64 | Mbps | -| Latency | latency | float64 | ms | -| Jitter | jitter | float64 | ms | -| Location | location | string | - | +| Name | Field Name | Type | Unit | +|----------------|-------------|---------|------------| +| Download Speed | download | float64 | Mbps | +| Upload Speed | upload | float64 | Mbps | +| Latency | latency | float64 | ms | +| Jitter | jitter | float64 | ms | +| Packet Loss | packet_loss | float64 | percentage | +| Location | location | string | - | + +The `packet_loss` will return -1, if packet loss not applicable. And the following tags: | Name | tag name | -| --------- | --------- | +|-----------|-----------| | Source | source | | Server ID | server_id | | Test Mode | test_mode | @@ -82,5 +86,6 @@ And the following tags: ## Example Output ```text -internet_speed,source=speedtest02.z4internet.com:8080,server_id=54619,test_mode=single download=318.37580265897725,upload=30.444407341274385,latency=37.73174,jitter=1.99810,location="Somewhere, TX" 1675458921000000000 +internet_speed,source=speedtest02.z4internet.com:8080,server_id=54619,test_mode=single download=318.37580265897725,upload=30.444407341274385,latency=37.73174,jitter=1.99810,packet_loss=0.05377,location="Somewhere, TX" 1675458921000000000 +internet_speed,source=speedtest02.z4internet.com:8080,server_id=54619,test_mode=multi download=318.37580265897725,upload=30.444407341274385,latency=37.73174,jitter=1.99810,packet_loss=-1,location="Somewhere, TX" 1675458921000000000 ``` diff --git a/plugins/inputs/internet_speed/internet_speed.go b/plugins/inputs/internet_speed/internet_speed.go index ccd992b179136..ff3150db8c37d 100644 --- a/plugins/inputs/internet_speed/internet_speed.go +++ b/plugins/inputs/internet_speed/internet_speed.go @@ -11,6 +11,7 @@ import ( "time" "github.com/showwin/speedtest-go/speedtest" + "github.com/showwin/speedtest-go/speedtest/transport" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/filter" @@ -83,6 +84,13 @@ func (is *InternetSpeed) Gather(acc telegraf.Accumulator) error { return fmt.Errorf("ping test failed: %w", err) } + analyzer := speedtest.NewPacketLossAnalyzer(&speedtest.PacketLossAnalyzerOptions{ + PacketSendingInterval: time.Millisecond * 100, + SamplingDuration: time.Second * 15, + }) + + pLoss := -1.0 + if is.TestMode == testModeMulti { err = is.server.MultiDownloadTestContext(context.Background(), is.servers) if err != nil { @@ -90,7 +98,13 @@ func (is *InternetSpeed) Gather(acc telegraf.Accumulator) error { } err = is.server.MultiUploadTestContext(context.Background(), is.servers) if err != nil { - return fmt.Errorf("upload test failed failed: %w", err) + return fmt.Errorf("upload test failed: %w", err) + } + // Not all servers are applicable for packet loss testing. + // If err != nil, we skip it and just report a warning. + pLoss, err = analyzer.RunMulti(is.servers.Hosts()) + if err != nil { + is.Log.Warnf("packet loss test failed: %s", err) } } else { err = is.server.DownloadTest() @@ -99,16 +113,35 @@ func (is *InternetSpeed) Gather(acc telegraf.Accumulator) error { } err = is.server.UploadTest() if err != nil { - return fmt.Errorf("upload test failed failed: %w", err) + return fmt.Errorf("upload test failed: %w", err) } + // Not all servers are applicable for packet loss testing. + // If err != nil, we skip it and just report a warning. + err = analyzer.Run(is.server.Host, func(packetLoss *transport.PLoss) { + if packetLoss != nil && packetLoss.Sent != 0 { + pLoss = packetLoss.Loss() + } + }) + if err != nil { + is.Log.Warnf("packet loss test failed: %s", err) + } + } + + lossPercent := 0.0 + if pLoss == -1 { + // If all servers are not applicable, returned -1. + lossPercent = -1 + } else { + lossPercent = pLoss * 100.0 } fields := map[string]any{ - "download": is.server.DLSpeed, - "upload": is.server.ULSpeed, - "latency": timeDurationMillisecondToFloat64(is.server.Latency), - "jitter": timeDurationMillisecondToFloat64(is.server.Jitter), - "location": is.server.Name, + "download": is.server.DLSpeed.Mbps(), + "upload": is.server.ULSpeed.Mbps(), + "latency": timeDurationMillisecondToFloat64(is.server.Latency), + "jitter": timeDurationMillisecondToFloat64(is.server.Jitter), + "packet_loss": lossPercent, + "location": is.server.Name, } tags := map[string]string{ "server_id": is.server.ID, @@ -148,7 +181,7 @@ func (is *InternetSpeed) findClosestServer() error { // Return the first match or the server with the lowest latency // when filter mismatch all servers. - var min int64 = math.MaxInt64 + var minLatency int64 = math.MaxInt64 selectIndex := -1 for index, server := range is.servers { if is.serverFilter.Match(server.ID) { @@ -156,8 +189,8 @@ func (is *InternetSpeed) findClosestServer() error { break } if server.Latency > 0 { - if min > server.Latency.Milliseconds() { - min = server.Latency.Milliseconds() + if minLatency > server.Latency.Milliseconds() { + minLatency = server.Latency.Milliseconds() selectIndex = index } } diff --git a/plugins/inputs/internet_speed/sample.conf b/plugins/inputs/internet_speed/sample.conf index 4fbfdb6fb2920..65dfa7ae9839d 100644 --- a/plugins/inputs/internet_speed/sample.conf +++ b/plugins/inputs/internet_speed/sample.conf @@ -21,6 +21,7 @@ ## By default, a single sever is used for testing. This may work for most, ## however, setting to "multi" will reach out to multiple servers in an ## attempt to get closer to ideal internet speeds. + ## And "multi" will use all available servers to calculate average packet loss. # test_mode = "single" ## Server ID exclude filter