mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2026-08-13 12:19:16 +08:00
fix: restore classic route labels
This commit is contained in:
parent
cf9cd92651
commit
28e6e39718
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/oneclickvirt/backtrace/model"
|
||||
. "github.com/oneclickvirt/defaultset"
|
||||
)
|
||||
|
||||
@ -13,22 +14,12 @@ import (
|
||||
func RenderRouteReport(report RouteReport) string {
|
||||
var builder strings.Builder
|
||||
for _, target := range report.Targets {
|
||||
label := target.Classification.Label
|
||||
if label == "" {
|
||||
label = "线路证据不足"
|
||||
}
|
||||
var rendered string
|
||||
switch {
|
||||
case target.Status != RouteProbeAvailable:
|
||||
rendered = Red("检测不到回程路由节点的IP地址")
|
||||
case target.Classification.Confidence == routeConfidenceInconclusive:
|
||||
rendered = Red(label)
|
||||
case target.Classification.Rank >= 4:
|
||||
rendered = DarkGreen(label)
|
||||
case target.Classification.Rank == 3:
|
||||
rendered = Green(label)
|
||||
default:
|
||||
rendered = White(label)
|
||||
rendered = renderLegacyRouteLabels(target)
|
||||
}
|
||||
addressWidth := 15
|
||||
if target.Target.IPVersion == "v6" {
|
||||
@ -38,3 +29,89 @@ func RenderRouteReport(report RouteReport) string {
|
||||
}
|
||||
return strings.TrimSuffix(builder.String(), "\n")
|
||||
}
|
||||
|
||||
// renderLegacyRouteLabels keeps the original terminal contract: every known
|
||||
// carrier ASN observed in the trace remains visible. The conservative
|
||||
// Classification is retained for JSON consumers, but its evidence-oriented
|
||||
// fallback labels must not replace the classic human-readable route result.
|
||||
func renderLegacyRouteLabels(target RouteTargetReport) string {
|
||||
asns := uniqueStrings(target.ObservedASNs)
|
||||
hasAS4134 := containsString(asns, "AS4134")
|
||||
hasAS4809 := containsString(asns, "AS4809")
|
||||
|
||||
ordered := make([]string, 0, len(asns)+1)
|
||||
if hasAS4809 {
|
||||
if hasAS4134 {
|
||||
ordered = append(ordered, "AS4809b")
|
||||
} else {
|
||||
ordered = append(ordered, "AS4809a")
|
||||
}
|
||||
}
|
||||
ordered = append(ordered, asns...)
|
||||
|
||||
seenLabels := make(map[string]struct{})
|
||||
labels := make([]string, 0, len(ordered))
|
||||
for _, asn := range ordered {
|
||||
if asn == "AS4809" {
|
||||
continue
|
||||
}
|
||||
label := model.M[asn]
|
||||
if label == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seenLabels[label]; exists {
|
||||
continue
|
||||
}
|
||||
seenLabels[label] = struct{}{}
|
||||
switch asn {
|
||||
case "AS9929", "AS4809a", "AS23764":
|
||||
labels = append(labels, DarkGreen(label))
|
||||
case "AS4809b", "AS58807":
|
||||
labels = append(labels, Green(label))
|
||||
default:
|
||||
labels = append(labels, White(label))
|
||||
}
|
||||
}
|
||||
if len(labels) > 0 {
|
||||
return strings.Join(labels, " ")
|
||||
}
|
||||
|
||||
// Reports produced by older API clients may contain only Classification.
|
||||
if target.Classification.Confidence != routeConfidenceInconclusive && target.Classification.Label != "" {
|
||||
switch {
|
||||
case target.Classification.Rank >= 4:
|
||||
return DarkGreen(target.Classification.Label)
|
||||
case target.Classification.Rank == 3:
|
||||
return Green(target.Classification.Label)
|
||||
default:
|
||||
return White(target.Classification.Label)
|
||||
}
|
||||
}
|
||||
return Red("检测不到已知线路的ASN")
|
||||
}
|
||||
|
||||
func uniqueStrings(values []string) []string {
|
||||
seen := make(map[string]struct{}, len(values))
|
||||
result := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
value = strings.ToUpper(strings.TrimSpace(value))
|
||||
if value == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[value]; exists {
|
||||
continue
|
||||
}
|
||||
seen[value] = struct{}{}
|
||||
result = append(result, value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func containsString(values []string, target string) bool {
|
||||
for _, value := range values {
|
||||
if value == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -39,7 +39,8 @@ func TestRunRouteReportOfflineFixture(t *testing.T) {
|
||||
if target.Classification.Code != "ct_cn2_gia" || target.Latency.Samples != 6 || target.Latency.P95MS != 30 {
|
||||
t.Fatalf("unexpected route classification or latency: %+v", target)
|
||||
}
|
||||
if strings.Contains(RenderRouteReport(report), "P95") || !strings.Contains(RenderRouteReport(report), "电信CN2GIA") {
|
||||
rendered := RenderRouteReport(report)
|
||||
if strings.Contains(rendered, "P95") || !strings.Contains(rendered, "电信CN2GT") || !strings.Contains(rendered, "电信163") {
|
||||
t.Fatalf("legacy rendering is not compact: %q", RenderRouteReport(report))
|
||||
}
|
||||
}
|
||||
@ -88,6 +89,55 @@ func TestRenderRouteReportPreservesLegacyAddressWidths(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderRouteReportUsesClassicASNLabels(t *testing.T) {
|
||||
report := RouteReport{Targets: []RouteTargetReport{
|
||||
{
|
||||
Target: RouteTarget{Name: "北京电信v4", Address: "219.141.140.10", IPVersion: "v4", Carrier: "CT"},
|
||||
Status: RouteProbeAvailable,
|
||||
ObservedASNs: []string{"AS6453", "AS4134"},
|
||||
Classification: inconclusiveClassification("ct_destination_only", "仅见电信目的网", "only one AS4134 hop"),
|
||||
},
|
||||
{
|
||||
Target: RouteTarget{Name: "上海电信v4", Address: "202.96.209.133", IPVersion: "v4", Carrier: "CT"},
|
||||
Status: RouteProbeAvailable,
|
||||
ObservedASNs: []string{"AS4809", "AS4134"},
|
||||
Classification: RouteClassification{Code: "ct_cn2_mixed", Label: "电信CN2混合 [优质线路]", Confidence: routeConfidenceMixed, Rank: 3},
|
||||
},
|
||||
{
|
||||
Target: RouteTarget{Name: "广州联通v4", Address: "210.21.196.6", IPVersion: "v4", Carrier: "CU"},
|
||||
Status: RouteProbeAvailable,
|
||||
ObservedASNs: []string{"AS4837"},
|
||||
Classification: inconclusiveClassification("cu_destination_only", "仅见联通目的网", "only one AS4837 hop"),
|
||||
},
|
||||
}}
|
||||
rendered := regexp.MustCompile(`\x1b\[[0-9;]*m`).ReplaceAllString(RenderRouteReport(report), "")
|
||||
for _, want := range []string{
|
||||
"北京电信v4 219.141.140.10 电信163 [普通线路]",
|
||||
"上海电信v4 202.96.209.133 电信CN2GT [优质线路] 电信163 [普通线路]",
|
||||
"广州联通v4 210.21.196.6 联通4837 [普通线路]",
|
||||
} {
|
||||
if !strings.Contains(rendered, want) {
|
||||
t.Fatalf("classic route label missing %q from %q", want, rendered)
|
||||
}
|
||||
}
|
||||
if strings.Contains(rendered, "仅见") || strings.Contains(rendered, "未见") {
|
||||
t.Fatalf("structured fallback label leaked into classic output: %q", rendered)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderRouteReportUsesClassicUnknownMessage(t *testing.T) {
|
||||
report := RouteReport{Targets: []RouteTargetReport{{
|
||||
Target: RouteTarget{Name: "北京电信v4", Address: "219.141.140.10", IPVersion: "v4", Carrier: "CT"},
|
||||
Status: RouteProbeAvailable,
|
||||
ObservedASNs: []string{"AS6453"},
|
||||
Classification: inconclusiveClassification("ct_unknown", "未见电信骨干", "known carrier ASNs are absent"),
|
||||
}}}
|
||||
rendered := regexp.MustCompile(`\x1b\[[0-9;]*m`).ReplaceAllString(RenderRouteReport(report), "")
|
||||
if !strings.Contains(rendered, "检测不到已知线路的ASN") || strings.Contains(rendered, "未见电信骨干") {
|
||||
t.Fatalf("unexpected classic unknown route output: %q", rendered)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRouteReportHonorsContext(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
@ -2,7 +2,7 @@ package model
|
||||
|
||||
import "time"
|
||||
|
||||
const BackTraceVersion = "v0.0.19"
|
||||
const BackTraceVersion = "v0.0.20"
|
||||
|
||||
var EnableLoger = false
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user