time.After -> ticker
diff --git a/session.go b/session.go
index cf63342..d64805e 100644
--- a/session.go
+++ b/session.go
@@ -24,6 +24,7 @@
 	"io"
 	"net"
 	"runtime"
+	"runtime/debug"
 	"sync"
 	"time"
 )
@@ -543,18 +544,20 @@
 	}
 
 	go func() {
+		ticker := time.NewTicker(s.period)
 		defer func() {
+			ticker.Stop()
 			if r := recover(); r != nil {
-				log.Errorf("Heartbeat panic occurs, error is %s", r)
+				log.Errorf("Heartbeat panic occurs, error is %s, stack %v", r, string(debug.Stack()))
 			}
 		}()
 		for {
 			select {
 			case <-s.done: // s.done is a blocked channel. if it has not been closed, the default branch will be invoked.
 				return
-			case <-time.After(s.period):
+			case <-ticker.C:
 				if err := heartbeat(s); err != nil {
-					log.Errorf("Heartbeat with error: %s", err)
+					log.Errorf("sessioin %s, heartbeat with error: %s", s.sessionToken(), err)
 				}
 			}
 		}