diff --git a/app/dns/dnscommon.go b/app/dns/dnscommon.go
index de0d794c..fa3ac406 100644
--- a/app/dns/dnscommon.go
+++ b/app/dns/dnscommon.go
@@ -1,13 +1,17 @@
 package dns
 
 import (
+	"context"
 	"encoding/binary"
 	"strings"
 	"time"
 
 	"github.com/xtls/xray-core/common"
+	"github.com/xtls/xray-core/common/log"
 	"github.com/xtls/xray-core/common/errors"
 	"github.com/xtls/xray-core/common/net"
+	"github.com/xtls/xray-core/common/session"
+	"github.com/xtls/xray-core/core"
 	dns_feature "github.com/xtls/xray-core/features/dns"
 	"golang.org/x/net/dns/dnsmessage"
 )
@@ -225,3 +229,19 @@ L:
 
 	return ipRecord, nil
 }
+
+// toDnsContext create a new background context with parent inbound, session and dns log
+func toDnsContext(ctx context.Context, addr string) context.Context {
+	dnsCtx := core.ToBackgroundDetachedContext(ctx)
+	if inbound := session.InboundFromContext(ctx); inbound != nil {
+		dnsCtx = session.ContextWithInbound(dnsCtx, inbound)
+	}
+	dnsCtx = session.ContextWithContent(dnsCtx, session.ContentFromContext(ctx))
+	dnsCtx = log.ContextWithAccessMessage(dnsCtx, &log.AccessMessage{
+		From:   "DNS",
+		To:     addr,
+		Status: log.AccessAccepted,
+		Reason: "",
+	})
+	return dnsCtx
+}
diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go
index 86161a85..fecc5efb 100644
--- a/app/dns/nameserver_doh.go
+++ b/app/dns/nameserver_doh.go
@@ -52,23 +52,11 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher) (*DoHNameServ
 		TLSHandshakeTimeout: 30 * time.Second,
 		ForceAttemptHTTP2:   true,
 		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
-			dispatcherCtx := context.Background()
-
 			dest, err := net.ParseDestination(network + ":" + addr)
 			if err != nil {
 				return nil, err
 			}
-
-			dispatcherCtx = session.ContextWithContent(dispatcherCtx, session.ContentFromContext(ctx))
-			dispatcherCtx = session.ContextWithInbound(dispatcherCtx, session.InboundFromContext(ctx))
-			dispatcherCtx = log.ContextWithAccessMessage(dispatcherCtx, &log.AccessMessage{
-				From:   "DoH",
-				To:     s.dohURL,
-				Status: log.AccessAccepted,
-				Reason: "",
-			})
-
-			link, err := s.dispatcher.Dispatch(dispatcherCtx, dest)
+			link, err := s.dispatcher.Dispatch(toDnsContext(ctx, s.dohURL), dest)
 			select {
 			case <-ctx.Done():
 				return nil, ctx.Err()
@@ -115,7 +103,7 @@ func NewDoHLocalNameServer(url *url.URL) *DoHNameServer {
 			}
 			conn, err := internet.DialSystem(ctx, dest, nil)
 			log.Record(&log.AccessMessage{
-				From:   "DoH",
+				From:   "DNS",
 				To:     s.dohURL,
 				Status: log.AccessAccepted,
 				Detour: "local",
diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go
index 0748732d..bf741c23 100644
--- a/app/dns/nameserver_local.go
+++ b/app/dns/nameserver_local.go
@@ -3,7 +3,9 @@ package dns
 import (
 	"context"
 	"strings"
+	"time"
 
+	"github.com/xtls/xray-core/common/log"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/features/dns"
 	"github.com/xtls/xray-core/features/dns/localdns"
@@ -18,6 +20,7 @@ const errEmptyResponse = "No address associated with hostname"
 
 // QueryIP implements Server.
 func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) {
+	start := time.Now()
 	ips, err = s.client.LookupIP(domain, option)
 
 	if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) {
@@ -26,6 +29,7 @@ func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, op
 
 	if len(ips) > 0 {
 		newError("Localhost got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
+		log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
 	}
 
 	return
diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go
index 63be44ba..e1c005ea 100644
--- a/app/dns/nameserver_quic.go
+++ b/app/dns/nameserver_quic.go
@@ -10,6 +10,7 @@ import (
 	"github.com/lucas-clemente/quic-go"
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/buf"
+	"github.com/xtls/xray-core/common/log"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/protocol/dns"
 	"github.com/xtls/xray-core/common/session"
@@ -275,6 +276,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
 		ips, err := s.findIPsForDomain(fqdn, option)
 		if err != errRecordNotFound {
 			newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
+			log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
 			return ips, err
 		}
 	}
@@ -306,10 +308,12 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne
 		close(done)
 	}()
 	s.sendQuery(ctx, fqdn, clientIP, option)
+	start := time.Now()
 
 	for {
 		ips, err := s.findIPsForDomain(fqdn, option)
 		if err != errRecordNotFound {
+			log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
 			return ips, err
 		}
 
@@ -371,6 +375,12 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) {
 	}
 
 	conn, err := quic.DialAddrContext(context.Background(), s.destination.NetAddr(), tlsConfig.GetTLSConfig(tls.WithNextProto("http/1.1", http2.NextProtoTLS, NextProtoDQ)), quicConfig)
+	log.Record(&log.AccessMessage{
+		From:   "DNS",
+		To:     s.destination,
+		Status: log.AccessAccepted,
+		Detour: "local",
+	})
 	if err != nil {
 		return nil, err
 	}
diff --git a/app/dns/nameserver_tcp.go b/app/dns/nameserver_tcp.go
index 99206bfc..cf63ac21 100644
--- a/app/dns/nameserver_tcp.go
+++ b/app/dns/nameserver_tcp.go
@@ -11,6 +11,7 @@ import (
 
 	"github.com/xtls/xray-core/common"
 	"github.com/xtls/xray-core/common/buf"
+	"github.com/xtls/xray-core/common/log"
 	"github.com/xtls/xray-core/common/net"
 	"github.com/xtls/xray-core/common/net/cnc"
 	"github.com/xtls/xray-core/common/protocol/dns"
@@ -43,7 +44,7 @@ func NewTCPNameServer(url *url.URL, dispatcher routing.Dispatcher) (*TCPNameServ
 	}
 
 	s.dial = func(ctx context.Context) (net.Conn, error) {
-		link, err := dispatcher.Dispatch(ctx, *s.destination)
+		link, err := dispatcher.Dispatch(toDnsContext(ctx, s.destination.String()), *s.destination)
 		if err != nil {
 			return nil, err
 		}
@@ -314,6 +315,7 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net
 		ips, err := s.findIPsForDomain(fqdn, option)
 		if err != errRecordNotFound {
 			newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
+			log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
 			return ips, err
 		}
 	}
@@ -345,10 +347,12 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net
 		close(done)
 	}()
 	s.sendQuery(ctx, fqdn, clientIP, option)
+	start := time.Now()
 
 	for {
 		ips, err := s.findIPsForDomain(fqdn, option)
 		if err != errRecordNotFound {
+			log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
 			return ips, err
 		}
 
diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go
index fc8e5d68..d511df5b 100644
--- a/app/dns/nameserver_udp.go
+++ b/app/dns/nameserver_udp.go
@@ -15,7 +15,6 @@ import (
 	"github.com/xtls/xray-core/common/session"
 	"github.com/xtls/xray-core/common/signal/pubsub"
 	"github.com/xtls/xray-core/common/task"
-	"github.com/xtls/xray-core/core"
 	dns_feature "github.com/xtls/xray-core/features/dns"
 	"github.com/xtls/xray-core/features/routing"
 	"github.com/xtls/xray-core/transport/internet/udp"
@@ -195,21 +194,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
 	for _, req := range reqs {
 		s.addPendingRequest(req)
 		b, _ := dns.PackMessage(req.msg)
-		udpCtx := core.ToBackgroundDetachedContext(ctx)
-		if inbound := session.InboundFromContext(ctx); inbound != nil {
-			udpCtx = session.ContextWithInbound(udpCtx, inbound)
-		}
-
-		udpCtx = session.ContextWithContent(udpCtx, &session.Content{
-			Protocol: "dns",
-		})
-		udpCtx = log.ContextWithAccessMessage(udpCtx, &log.AccessMessage{
-			From:   "DNS",
-			To:     s.address,
-			Status: log.AccessAccepted,
-			Reason: "",
-		})
-		s.udpServer.Dispatch(udpCtx, *s.address, b)
+		s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
 	}
 }