diff --git a/tracing/middleware.go b/tracing/middleware.go
index bfb78ca..38a980d 100644
--- a/tracing/middleware.go
+++ b/tracing/middleware.go
@@ -77,7 +77,7 @@ func ClientMiddleware(opts ...Option) client.Middleware {
 			// trace start
 			ctx, span := cfg.tracer.Start(
 				ctx,
-				clientSpanNaming(req),
+				cfg.clientSpanNameFormatter(req),
 				oteltrace.WithTimestamp(start),
 				oteltrace.WithSpanKind(oteltrace.SpanKindClient),
 			)
@@ -165,7 +165,7 @@ func ServerMiddleware(cfg *Config) app.HandlerFunc {
 		// set baggage
 		ctx = baggage.ContextWithBaggage(ctx, bags)
 
-		ctx, span := sTracer.Start(oteltrace.ContextWithRemoteSpanContext(ctx, spanCtx), serverSpanNaming(c), opts...)
+		ctx, span := sTracer.Start(oteltrace.ContextWithRemoteSpanContext(ctx, spanCtx), cfg.serverSpanNameFormatter(c), opts...)
 
 		// peer service attributes
 		span.SetAttributes(peerServiceAttributes...)
diff --git a/tracing/options.go b/tracing/options.go
index 98d56f6..087cd74 100644
--- a/tracing/options.go
+++ b/tracing/options.go
@@ -49,6 +49,9 @@ type Config struct {
 	clientHttpRouteFormatter func(req *protocol.Request) string
 	serverHttpRouteFormatter func(c *app.RequestContext) string
 
+	clientSpanNameFormatter func(req *protocol.Request) string
+	serverSpanNameFormatter func(c *app.RequestContext) string
+
 	tracerProvider    trace.TracerProvider
 	meterProvider     metric.MeterProvider
 	textMapPropagator propagation.TextMapPropagator
@@ -88,6 +91,9 @@ func defaultConfig() *Config {
 		clientHttpRouteFormatter: func(req *protocol.Request) string {
 			return string(req.Path())
 		},
+		clientSpanNameFormatter: func(req *protocol.Request) string {
+			return string(req.Path())
+		},
 		serverHttpRouteFormatter: func(c *app.RequestContext) string {
 			// FullPath returns a matched route full path. For not found routes
 			// returns an empty string.
@@ -98,6 +104,17 @@ func defaultConfig() *Config {
 			}
 			return route
 		},
+		serverSpanNameFormatter: func(c *app.RequestContext) string {
+			// Ref to https://github.com/open-telemetry/opentelemetry-specification/blob/ffddc289462dfe0c2041e3ca42a7b1df805706de/specification/trace/api.md#span
+			// FullPath returns a matched route full path. For not found routes
+			// returns an empty string.
+			route := c.FullPath()
+			// fall back to handler name
+			if route == "" {
+				route = string(c.Path())
+			}
+			return route
+		},
 		shouldIgnore: func(ctx context.Context, c *app.RequestContext) bool {
 			return false
 		},
@@ -139,6 +156,20 @@ func WithServerHttpRouteFormatter(serverHttpRouteFormatter func(c *app.RequestCo
 	})
 }
 
+// WithClientSpanNameFormatter configures clientSpanNameFormatter
+func WithClientSpanNameFormatter(clientSpanNameFormatter func(req *protocol.Request) string) Option {
+	return option(func(cfg *Config) {
+		cfg.clientSpanNameFormatter = clientSpanNameFormatter
+	})
+}
+
+// WithServerSpanNameFormatter configures serverSpanNameFormatter
+func WithServerSpanNameFormatter(serverSpanNameFormatter func(c *app.RequestContext) string) Option {
+	return option(func(cfg *Config) {
+		cfg.serverSpanNameFormatter = serverSpanNameFormatter
+	})
+}
+
 // WithShouldIgnore allows you to define the condition for enabling distributed tracing
 func WithShouldIgnore(condition ConditionFunc) Option {
 	return option(func(cfg *Config) {
diff --git a/tracing/tracer_server.go b/tracing/tracer_server.go
index b4ea744..3cb0080 100644
--- a/tracing/tracer_server.go
+++ b/tracing/tracer_server.go
@@ -90,7 +90,7 @@ func (s *serverTracer) Finish(ctx context.Context, c *app.RequestContext) {
 	// trace carrier from context
 	tc := internal.TraceCarrierFromContext(ctx)
 	if tc == nil {
-		hlog.Warnf("get tracer container failed")
+		hlog.Debugf("get tracer container failed")
 		return
 	}
 
diff --git a/tracing/utils.go b/tracing/utils.go
index 5927da5..80ab1cb 100644
--- a/tracing/utils.go
+++ b/tracing/utils.go
@@ -19,10 +19,8 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/cloudwego/hertz/pkg/app"
 	"github.com/cloudwego/hertz/pkg/common/tracer/stats"
 	"github.com/cloudwego/hertz/pkg/common/tracer/traceinfo"
-	"github.com/cloudwego/hertz/pkg/protocol"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/codes"
@@ -30,20 +28,6 @@ import (
 	"go.opentelemetry.io/otel/trace"
 )
 
-// Ref to https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#name
-// naming rule: $HandlerName:$FullPath
-func serverSpanNaming(c *app.RequestContext) string {
-	handlerName := app.GetHandlerName(c.Handler())
-	if handlerName == "" {
-		handlerName = c.HandlerName()
-	}
-	return handlerName + ":" + c.FullPath()
-}
-
-func clientSpanNaming(req *protocol.Request) string {
-	return string(req.Path())
-}
-
 func handleErr(err error) {
 	if err != nil {
 		otel.Handle(err)