From 7970f240de69d0da5efd1d136e8aa581274a4c4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?=
 <Fangliding.fshxy@outlook.com>
Date: Sat, 14 Sep 2024 01:05:19 +0800
Subject: [PATCH] Log: show the matching rule tag (#3809)

---
 app/dispatcher/default.go    | 8 ++++++--
 app/router/command/config.go | 4 ++++
 app/router/router.go         | 7 ++++++-
 features/routing/router.go   | 3 +++
 4 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go
index 34119ac2..f38b56c9 100644
--- a/app/dispatcher/default.go
+++ b/app/dispatcher/default.go
@@ -10,8 +10,8 @@ import (
 	"time"
 
 	"github.com/xtls/xray-core/common"
-	"github.com/xtls/xray-core/common/errors"
 	"github.com/xtls/xray-core/common/buf"
+	"github.com/xtls/xray-core/common/errors"
 	"github.com/xtls/xray-core/common/log"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/protocol"
@@ -421,7 +421,11 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
 			outTag := route.GetOutboundTag()
 			if h := d.ohm.GetHandler(outTag); h != nil {
 				isPickRoute = 2
-				errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
+				if route.GetRuleTag() == "" {
+					errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
+				} else {
+					errors.LogInfo(ctx, "Hit route rule: [", route.GetRuleTag(), "] so taking detour [", outTag, "] for [", destination, "]")
+				}
 				handler = h
 			} else {
 				errors.LogWarning(ctx, "non existing outTag: ", outTag)
diff --git a/app/router/command/config.go b/app/router/command/config.go
index a033e731..8c2e1343 100644
--- a/app/router/command/config.go
+++ b/app/router/command/config.go
@@ -28,6 +28,10 @@ func (c routingContext) GetTargetPort() net.Port {
 	return net.Port(c.RoutingContext.GetTargetPort())
 }
 
+func (c routingContext) GetRuleTag() string {
+	return ""
+}
+
 // GetSkipDNSResolve is a mock implementation here to match the interface,
 // SkipDNSResolve is set from dns module, no use if coming from a protobuf object?
 // TODO: please confirm @Vigilans
diff --git a/app/router/router.go b/app/router/router.go
index 753ddfa8..2a1e2e62 100644
--- a/app/router/router.go
+++ b/app/router/router.go
@@ -34,6 +34,7 @@ type Route struct {
 	routing.Context
 	outboundGroupTags []string
 	outboundTag       string
+	ruleTag           string
 }
 
 // Init initializes the Router.
@@ -89,7 +90,7 @@ func (r *Router) PickRoute(ctx routing.Context) (routing.Route, error) {
 	if err != nil {
 		return nil, err
 	}
-	return &Route{Context: ctx, outboundTag: tag}, nil
+	return &Route{Context: ctx, outboundTag: tag, ruleTag: rule.RuleTag}, nil
 }
 
 // AddRule implements routing.Router.
@@ -239,6 +240,10 @@ func (r *Route) GetOutboundTag() string {
 	return r.outboundTag
 }
 
+func (r *Route) GetRuleTag() string {
+	return r.ruleTag
+}
+
 func init() {
 	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
 		r := new(Router)
diff --git a/features/routing/router.go b/features/routing/router.go
index 97246cdb..174d59fd 100644
--- a/features/routing/router.go
+++ b/features/routing/router.go
@@ -30,6 +30,9 @@ type Route interface {
 
 	// GetOutboundTag returns the tag of the outbound the connection was dispatched to.
 	GetOutboundTag() string
+
+	// GetRuleTag returns the matching rule tag for debugging if exists
+	GetRuleTag() string
 }
 
 // RouterType return the type of Router interface. Can be used to implement common.HasType.