Repository: cloudwego/hertz
Pull request #266 (2022-09-21 20:26:03)

optimize(hlog): distinguish systemlogger and defaultlogger
#### What type of PR is this?

optimize

#### Check the PR title.
- [x] This PR title match the format: \<type\>(optional scope): \<description\>
- [x] The description of this PR title is user-oriented and clear enough for others to understand.


#### (Optional) Translate the PR title into Chinese.
区分系统日志和默认日志并添加SetCallDepth接口

#### (Optional) More detail description for this PR(en: English/zh: Chinese).

en: distinguish systemlogger and defaultlogger
zh: 区分系统日志和默认日志

codecov[bot]: # [Codecov](https://codecov.io/gh/cloudwego/hertz/pull/266?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) Report
Base: **59.17**% // Head: **59.17**% // No change to project coverage :thumbsup:
> Coverage data is based on head [(`e7f24d5`)](https://codecov.io/gh/cloudwego/hertz/pull/266?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) compared to base [(`ea2569a`)](https://codecov.io/gh/cloudwego/hertz/commit/ea2569a67415eabbf8907c90e1dd167e33096624?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).
> Patch coverage: 40.47% of modified lines in pull request are covered.

> :exclamation: Current head e7f24d5 differs from pull request most recent head c72682a. Consider uploading reports for the commit c72682a to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff            @@
##           develop     #266   +/-   ##
========================================
  Coverage    59.17%   59.17%           
========================================
  Files           79       79           
  Lines         8150     8150           
========================================
  Hits          4823     4823           
  Misses        2972     2972           
  Partials       355      355           
```


| [Impacted Files](https://codecov.io/gh/cloudwego/hertz/pull/266?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) | Coverage Δ | |
|---|---|---|
| [pkg/app/client/loadbalance/lbcache.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9jbGllbnQvbG9hZGJhbGFuY2UvbGJjYWNoZS5nbw==) | `79.45% <0.00%> (ø)` | |
| [pkg/app/client/loadbalance/weight\_random.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9jbGllbnQvbG9hZGJhbGFuY2Uvd2VpZ2h0X3JhbmRvbS5nbw==) | `88.09% <0.00%> (ø)` | |
| [pkg/network/standard/transport.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL25ldHdvcmsvc3RhbmRhcmQvdHJhbnNwb3J0Lmdv) | `0.00% <0.00%> (ø)` | |
| [pkg/protocol/http1/client.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3Byb3RvY29sL2h0dHAxL2NsaWVudC5nbw==) | `35.54% <0.00%> (ø)` | |
| [pkg/app/fs.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9mcy5nbw==) | `68.65% <10.00%> (ø)` | |
| [pkg/route/engine.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL3JvdXRlL2VuZ2luZS5nbw==) | `61.98% <11.11%> (ø)` | |
| [pkg/app/server/render/html.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9zZXJ2ZXIvcmVuZGVyL2h0bWwuZ28=) | `37.50% <77.77%> (ø)` | |
| [pkg/app/server/hertz.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9zZXJ2ZXIvaGVydHouZ28=) | `75.00% <80.00%> (ø)` | |
| [internal/stats/tracer.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-aW50ZXJuYWwvc3RhdHMvdHJhY2VyLmdv) | `90.00% <100.00%> (ø)` | |
| [pkg/app/middlewares/server/recovery/recovery.go](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego#diff-cGtnL2FwcC9taWRkbGV3YXJlcy9zZXJ2ZXIvcmVjb3ZlcnkvcmVjb3ZlcnkuZ28=) | `86.66% <100.00%> (ø)` | |
| ... and [2 more](https://codecov.io/gh/cloudwego/hertz/pull/266/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/cloudwego/hertz/pull/266?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cloudwego).


Patch:
diff --git a/internal/stats/tracer.go b/internal/stats/tracer.go
index 1d92cfb..e00b09a 100644
--- a/internal/stats/tracer.go
+++ b/internal/stats/tracer.go
@@ -67,6 +67,6 @@ func (ctl *Controller) HasTracer() bool {
 
 func (ctl *Controller) tryRecover() {
 	if err := recover(); err != nil {
-		hlog.Warnf("HERTZ: Panic happened during tracer call. This doesn't affect the http call, but may lead to lack of monitor data such as metrics and logs: %s, %s", err, string(debug.Stack()))
+		hlog.SystemLogger().Warnf("Panic happened during tracer call. This doesn't affect the http call, but may lead to lack of monitor data such as metrics and logs: %s, %s", err, string(debug.Stack()))
 	}
 }
diff --git a/pkg/app/client/loadbalance/lbcache.go b/pkg/app/client/loadbalance/lbcache.go
index c249ebe..47acdd4 100644
--- a/pkg/app/client/loadbalance/lbcache.go
+++ b/pkg/app/client/loadbalance/lbcache.go
@@ -111,7 +111,7 @@ func (b *BalancerFactory) refresh() {
 		b.cache.Range(func(key, value interface{}) bool {
 			res, err := b.resolver.Resolve(context.Background(), key.(string))
 			if err != nil {
-				hlog.Warnf("Hertz: resolver refresh failed, key=%s error=%s", key, err.Error())
+				hlog.SystemLogger().Warnf("resolver refresh failed, key=%s error=%s", key, err.Error())
 				return true
 			}
 			renameResultCacheKey(&res, b.resolver.Name())
@@ -132,7 +132,7 @@ func (b *BalancerFactory) GetInstance(ctx context.Context, req *protocol.Request
 	atomic.StoreInt32(&cacheRes.expire, 0)
 	ins := b.balancer.Pick(cacheRes.res.Load().(discovery.Result))
 	if ins == nil {
-		hlog.Errorf("HERTZ: null instance. serviceName: %s, options: %v", string(req.Host()), req.Options())
+		hlog.SystemLogger().Errorf("null instance. serviceName: %s, options: %v", string(req.Host()), req.Options())
 		return nil, errors.NewPublic("instance not found")
 	}
 	return ins, nil
diff --git a/pkg/app/client/loadbalance/weight_random.go b/pkg/app/client/loadbalance/weight_random.go
index 1fa0f57..381e576 100644
--- a/pkg/app/client/loadbalance/weight_random.go
+++ b/pkg/app/client/loadbalance/weight_random.go
@@ -59,7 +59,7 @@ func (wb *weightedBalancer) calcWeightInfo(e discovery.Result) *weightInfo {
 			w.weightSum += weight
 			cnt++
 		} else {
-			hlog.Warnf("HERTZ: Invalid weight=%d on instance address=%s", weight, e.Instances[idx].Address())
+			hlog.SystemLogger().Warnf("Invalid weight=%d on instance address=%s", weight, e.Instances[idx].Address())
 		}
 	}
 
diff --git a/pkg/app/fs.go b/pkg/app/fs.go
index 24eba04..644a9e9 100644
--- a/pkg/app/fs.go
+++ b/pkg/app/fs.go
@@ -286,7 +286,7 @@ func ServeFile(ctx *RequestContext, path string) {
 		// extend relative path to absolute path
 		var err error
 		if path, err = filepath.Abs(path); err != nil {
-			hlog.Errorf("HERTZ: Cannot resolve path=%q to absolute file error=%s", path, err)
+			hlog.SystemLogger().Errorf("Cannot resolve path=%q to absolute file error=%s", path, err)
 			ctx.AbortWithMsg("Internal Server Error", consts.StatusInternalServerError)
 			return
 		}
@@ -805,7 +805,7 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 	path = stripTrailingSlashes(path)
 
 	if n := bytes.IndexByte(path, 0); n >= 0 {
-		hlog.Errorf("HERTZ: Cannot serve path with nil byte at position=%d, path=%q", n, path)
+		hlog.SystemLogger().Errorf("Cannot serve path with nil byte at position=%d, path=%q", n, path)
 		ctx.AbortWithMsg("Are you a hacker?", consts.StatusBadRequest)
 		return
 	}
@@ -814,7 +814,7 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 		// since ctx.Path must normalize and sanitize the path.
 
 		if n := bytes.Index(path, bytestr.StrSlashDotDotSlash); n >= 0 {
-			hlog.Errorf("HERTZ: Cannot serve path with '/../' at position=%d due to security reasons, path=%q", n, path)
+			hlog.SystemLogger().Errorf("Cannot serve path with '/../' at position=%d due to security reasons, path=%q", n, path)
 			ctx.AbortWithMsg("Internal Server Error", consts.StatusInternalServerError)
 			return
 		}
@@ -842,7 +842,7 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 		ff, err = h.openFSFile(filePath, mustCompress)
 
 		if mustCompress && err == errNoCreatePermission {
-			hlog.Errorf("HERTZ: Insufficient permissions for saving compressed file for path=%q. Serving uncompressed file. "+
+			hlog.SystemLogger().Errorf("Insufficient permissions for saving compressed file for path=%q. Serving uncompressed file. "+
 				"Allow write access to the directory with this file in order to improve hertz performance", filePath)
 			mustCompress = false
 			ff, err = h.openFSFile(filePath, mustCompress)
@@ -850,12 +850,12 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 		if err == errDirIndexRequired {
 			ff, err = h.openIndexFile(ctx, filePath, mustCompress)
 			if err != nil {
-				hlog.Errorf("HERTZ: Cannot open dir index, path=%q, error=%s", filePath, err)
+				hlog.SystemLogger().Errorf("Cannot open dir index, path=%q, error=%s", filePath, err)
 				ctx.AbortWithMsg("Directory index is forbidden", consts.StatusForbidden)
 				return
 			}
 		} else if err != nil {
-			hlog.Errorf("HERTZ: Cannot open file=%q, error=%s", filePath, err)
+			hlog.SystemLogger().Errorf("Cannot open file=%q, error=%s", filePath, err)
 			if h.pathNotFound == nil {
 				ctx.AbortWithMsg("Cannot open requested path", consts.StatusNotFound)
 			} else {
@@ -892,7 +892,7 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 
 	r, err := ff.NewReader()
 	if err != nil {
-		hlog.Errorf("HERTZ: Cannot obtain file reader for path=%q, error=%s", path, err)
+		hlog.SystemLogger().Errorf("Cannot obtain file reader for path=%q, error=%s", path, err)
 		ctx.AbortWithMsg("Internal Server Error", consts.StatusInternalServerError)
 		return
 	}
@@ -910,14 +910,14 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 			startPos, endPos, err := ParseByteRange(byteRange, contentLength)
 			if err != nil {
 				r.(io.Closer).Close()
-				hlog.Errorf("HERTZ: Cannot parse byte range %q for path=%q,error=%s", byteRange, path, err)
+				hlog.SystemLogger().Errorf("Cannot parse byte range %q for path=%q,error=%s", byteRange, path, err)
 				ctx.AbortWithMsg("Range Not Satisfiable", consts.StatusRequestedRangeNotSatisfiable)
 				return
 			}
 
 			if err = r.(byteRangeUpdater).UpdateByteRange(startPos, endPos); err != nil {
 				r.(io.Closer).Close()
-				hlog.Errorf("HERTZ: Cannot seek byte range %q for path=%q, error=%s", byteRange, path, err)
+				hlog.SystemLogger().Errorf("Cannot seek byte range %q for path=%q, error=%s", byteRange, path, err)
 				ctx.AbortWithMsg("Internal Server Error", consts.StatusInternalServerError)
 				return
 			}
@@ -937,7 +937,7 @@ func (h *fsHandler) handleRequest(c context.Context, ctx *RequestContext) {
 		ctx.Response.Header.SetContentLength(contentLength)
 		if rc, ok := r.(io.Closer); ok {
 			if err := rc.Close(); err != nil {
-				hlog.Errorf("HERTZ: Cannot close file reader: error=%s", err)
+				hlog.SystemLogger().Errorf("Cannot close file reader: error=%s", err)
 				ctx.AbortWithMsg("Internal Server Error", consts.StatusInternalServerError)
 				return
 			}
diff --git a/pkg/app/middlewares/server/recovery/recovery.go b/pkg/app/middlewares/server/recovery/recovery.go
index 0710056..2dbb5cc 100644
--- a/pkg/app/middlewares/server/recovery/recovery.go
+++ b/pkg/app/middlewares/server/recovery/recovery.go
@@ -43,7 +43,7 @@ func Recovery() app.HandlerFunc {
 			if err := recover(); err != nil {
 				stack := stack(3)
 
-				hlog.CtxErrorf(c, "[Recovery] %s panic recovered:\n%s\n%s\n",
+				hlog.SystemLogger().CtxErrorf(c, "[Recovery] %s panic recovered:\n%s\n%s\n",
 					timeFormat(time.Now()), err, stack)
 				ctx.AbortWithStatus(consts.StatusInternalServerError)
 			}
diff --git a/pkg/app/server/hertz.go b/pkg/app/server/hertz.go
index b8edc92..651b1ed 100644
--- a/pkg/app/server/hertz.go
+++ b/pkg/app/server/hertz.go
@@ -67,20 +67,20 @@ func (h *Hertz) Spin() {
 	}
 
 	if err := signalWaiter(errCh); err != nil {
-		hlog.Errorf("HERTZ: Receive close signal: error=%v", err)
+		hlog.SystemLogger().Errorf("Receive close signal: error=%v", err)
 		if err := h.Engine.Close(); err != nil {
-			hlog.Errorf("HERTZ: Close error=%v", err)
+			hlog.SystemLogger().Errorf("Close error=%v", err)
 		}
 		return
 	}
 
-	hlog.Infof("HERTZ: Begin graceful shutdown, wait at most num=%d seconds...", h.GetOptions().ExitWaitTimeout/time.Second)
+	hlog.SystemLogger().Infof("Begin graceful shutdown, wait at most num=%d seconds...", h.GetOptions().ExitWaitTimeout/time.Second)
 
 	ctx, cancel := context.WithTimeout(context.Background(), h.GetOptions().ExitWaitTimeout)
 	defer cancel()
 
 	if err := h.Shutdown(ctx); err != nil {
-		hlog.Errorf("HERTZ: Shutdown error=%v", err)
+		hlog.SystemLogger().Errorf("Shutdown error=%v", err)
 	}
 }
 
@@ -124,7 +124,7 @@ func (h *Hertz) initOnRunHooks(errChan chan error) {
 			// delay register 1s
 			time.Sleep(1 * time.Second)
 			if err := opt.Registry.Register(opt.RegistryInfo); err != nil {
-				hlog.Errorf("HERTZ: Register error=%v", err)
+				hlog.SystemLogger().Errorf("Register error=%v", err)
 				// pass err to errChan
 				errChan <- err
 			}
diff --git a/pkg/app/server/render/html.go b/pkg/app/server/render/html.go
index 03cf687..f31e33a 100644
--- a/pkg/app/server/render/html.go
+++ b/pkg/app/server/render/html.go
@@ -160,13 +160,13 @@ func (h *HTMLDebug) startChecker() {
 
 	if h.RefreshInterval > 0 {
 		go func() {
-			hlog.Debugf("HERTZ[HTMLDebug]: HTML template reloader started with interval %v", h.RefreshInterval)
+			hlog.SystemLogger().Debugf("[HTMLDebug] HTML template reloader started with interval %v", h.RefreshInterval)
 			for {
 				n := time.Now()
 				if n.UTC().Sub(h.updateTimeStamp.UTC()) > h.RefreshInterval {
-					hlog.Debugf("HERTZ[HTMLDebug]: triggering HTML template reloader")
+					hlog.SystemLogger().Debugf("[HTMLDebug] triggering HTML template reloader")
 					h.reloadCh <- struct{}{}
-					hlog.Debugf("HERTZ[HTMLDebug]: HTML template has been reloaded, next reload in %v", h.RefreshInterval)
+					hlog.SystemLogger().Debugf("[HTMLDebug] HTML template has been reloaded, next reload in %v", h.RefreshInterval)
 					h.updateTimeStamp = time.Now()
 				}
 			}
@@ -181,15 +181,15 @@ func (h *HTMLDebug) startChecker() {
 	h.watcher = watcher
 	for _, f := range h.Files {
 		err := watcher.Add(f)
-		hlog.Debugf("HERTZ[HTMLDebug]: watching file: %s", f)
+		hlog.SystemLogger().Debugf("[HTMLDebug] watching file: %s", f)
 		if err != nil {
-			hlog.Errorf("HERTZ[HTMLDebug]: add watching file: %s, error happened: %v", f, err)
+			hlog.SystemLogger().Errorf("[HTMLDebug] add watching file: %s, error happened: %v", f, err)
 		}
 
 	}
 
 	go func() {
-		hlog.Debugf("HERTZ[HTMLDebug]: HTML template reloader started with file watcher")
+		hlog.SystemLogger().Debugf("[HTMLDebug] HTML template reloader started with file watcher")
 		for {
 			select {
 			case event, ok := <-watcher.Events:
@@ -197,15 +197,15 @@ func (h *HTMLDebug) startChecker() {
 					return
 				}
 				if event.Op&fsnotify.Write == fsnotify.Write {
-					hlog.Debugf("HERTZ[HTMLDebug]: modified file: %s, html render template will be reloaded at the next rendering", event.Name)
+					hlog.SystemLogger().Debugf("[HTMLDebug] modified file: %s, html render template will be reloaded at the next rendering", event.Name)
 					h.reloadCh <- struct{}{}
-					hlog.Debugf("HERTZ[HTMLDebug]: HTML template has been reloaded")
+					hlog.SystemLogger().Debugf("[HTMLDebug] HTML template has been reloaded")
 				}
 			case err, ok := <-watcher.Errors:
 				if !ok {
 					return
 				}
-				hlog.Errorf("HERTZ: error happened when watching the rendering files: %v", err)
+				hlog.SystemLogger().Errorf("error happened when watching the rendering files: %v", err)
 			}
 		}
 	}()
diff --git a/pkg/common/hlog/default.go b/pkg/common/hlog/default.go
index 3b95484..6bc624a 100644
--- a/pkg/common/hlog/default.go
+++ b/pkg/common/hlog/default.go
@@ -24,34 +24,6 @@ import (
 	"os"
 )
 
-var logger FullLogger = &defaultLogger{
-	stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
-}
-
-// SetOutput sets the output of default logger. By default, it is stderr.
-func SetOutput(w io.Writer) {
-	logger.SetOutput(w)
-}
-
-// SetLevel sets the level of logs below which logs will not be output.
-// The default log level is LevelTrace.
-// Note that this method is not concurrent-safe.
-func SetLevel(lv Level) {
-	logger.SetLevel(lv)
-}
-
-// DefaultLogger return the default logger for hertz.
-func DefaultLogger() FullLogger {
-	return logger
-}
-
-// SetLogger sets the default logger.
-// Note that this method is not concurrent-safe and must not be called
-// after the use of DefaultLogger and global functions in this package.
-func SetLogger(v FullLogger) {
-	logger = v
-}
-
 // Fatal calls the default logger's Fatal method and then os.Exit(1).
 func Fatal(v ...interface{}) {
 	logger.Fatal(v...)
@@ -160,6 +132,7 @@ func CtxTracef(ctx context.Context, format string, v ...interface{}) {
 type defaultLogger struct {
 	stdlog *log.Logger
 	level  Level
+	depth  int
 }
 
 func (ll *defaultLogger) SetOutput(w io.Writer) {
@@ -180,7 +153,7 @@ func (ll *defaultLogger) logf(lv Level, format *string, v ...interface{}) {
 	} else {
 		msg += fmt.Sprint(v...)
 	}
-	ll.stdlog.Output(4, msg)
+	ll.stdlog.Output(ll.depth, msg)
 	if lv == LevelFatal {
 		os.Exit(1)
 	}
diff --git a/pkg/common/hlog/hlog.go b/pkg/common/hlog/hlog.go
new file mode 100644
index 0000000..11a8161
--- /dev/null
+++ b/pkg/common/hlog/hlog.go
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2022 CloudWeGo Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hlog
+
+import (
+	"io"
+	"log"
+	"os"
+)
+
+const (
+	systemLogPrefix = "HERTZ: "
+)
+
+var (
+	// Provide default logger for users to use
+	logger FullLogger = &defaultLogger{
+		stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
+		depth:  4,
+	}
+
+	// Provide system logger for print system log
+	sysLogger FullLogger = &systemLogger{
+		&defaultLogger{
+			stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds),
+			depth:  4,
+		},
+		systemLogPrefix,
+	}
+)
+
+// SetOutput sets the output of default logger and system logger. By default, it is stderr.
+func SetOutput(w io.Writer) {
+	logger.SetOutput(w)
+	sysLogger.SetOutput(w)
+}
+
+// SetLevel sets the level of logs below which logs will not be output.
+// The default logger and system logger level is LevelTrace.
+// Note that this method is not concurrent-safe.
+func SetLevel(lv Level) {
+	logger.SetLevel(lv)
+	sysLogger.SetLevel(lv)
+}
+
+// DefaultLogger return the default logger for hertz.
+func DefaultLogger() FullLogger {
+	return logger
+}
+
+// SystemLogger return the system logger for hertz to print system log.
+// This function is not recommended for users to use.
+func SystemLogger() FullLogger {
+	return sysLogger
+}
+
+// SetSystemLogger sets the system logger.
+// Note that this method is not concurrent-safe and must not be called
+// This function is not recommended for users to use.
+func SetSystemLogger(v FullLogger) {
+	sysLogger = &systemLogger{v, systemLogPrefix}
+}
+
+// SetLogger sets the default logger and the system logger.
+// Note that this method is not concurrent-safe and must not be called
+// after the use of DefaultLogger and global functions in this package.
+func SetLogger(v FullLogger) {
+	logger = v
+	SetSystemLogger(v)
+}
diff --git a/pkg/common/hlog/system.go b/pkg/common/hlog/system.go
new file mode 100644
index 0000000..e931517
--- /dev/null
+++ b/pkg/common/hlog/system.go
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2022 CloudWeGo Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package hlog
+
+import (
+	"context"
+	"io"
+	"strings"
+	"sync"
+)
+
+var builderPool = sync.Pool{New: func() interface{} {
+	return &strings.Builder{} // nolint:SA6002
+}}
+
+type systemLogger struct {
+	logger FullLogger
+	prefix string
+}
+
+func (ll *systemLogger) SetOutput(w io.Writer) {
+	ll.logger.SetOutput(w)
+}
+
+func (ll *systemLogger) SetLevel(lv Level) {
+	ll.logger.SetLevel(lv)
+}
+
+func (ll *systemLogger) Fatal(v ...interface{}) {
+	ll.logger.Fatal(v...)
+}
+
+func (ll *systemLogger) Error(v ...interface{}) {
+	ll.logger.Error(v...)
+}
+
+func (ll *systemLogger) Warn(v ...interface{}) {
+	ll.logger.Warn(v...)
+}
+
+func (ll *systemLogger) Notice(v ...interface{}) {
+	ll.logger.Notice(v...)
+}
+
+func (ll *systemLogger) Info(v ...interface{}) {
+	ll.logger.Info(v...)
+}
+
+func (ll *systemLogger) Debug(v ...interface{}) {
+	ll.logger.Debug(v...)
+}
+
+func (ll *systemLogger) Trace(v ...interface{}) {
+	ll.logger.Trace(v...)
+}
+
+func (ll *systemLogger) Fatalf(format string, v ...interface{}) {
+	ll.logger.Fatalf(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Errorf(format string, v ...interface{}) {
+	ll.logger.Errorf(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Warnf(format string, v ...interface{}) {
+	ll.logger.Warnf(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Noticef(format string, v ...interface{}) {
+	ll.logger.Noticef(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Infof(format string, v ...interface{}) {
+	ll.logger.Infof(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Debugf(format string, v ...interface{}) {
+	ll.logger.Debugf(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) Tracef(format string, v ...interface{}) {
+	ll.logger.Tracef(ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxFatalf(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxFatalf(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxErrorf(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxErrorf(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxWarnf(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxWarnf(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxNoticef(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxNoticef(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxInfof(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxInfof(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxDebugf(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) CtxTracef(ctx context.Context, format string, v ...interface{}) {
+	ll.logger.CtxTracef(ctx, ll.addPrefix(format), v...)
+}
+
+func (ll *systemLogger) addPrefix(format string) string {
+	builder := builderPool.Get().(*strings.Builder)
+	builder.Grow(len(format) + len(ll.prefix))
+	builder.WriteString(ll.prefix)
+	builder.WriteString(format)
+	s := builder.String()
+	builder.Reset()
+	builderPool.Put(builder) // nolint:SA6002
+	return s
+}
diff --git a/pkg/network/netpoll/connection.go b/pkg/network/netpoll/connection.go
index a407abd..f456d3c 100644
--- a/pkg/network/netpoll/connection.go
+++ b/pkg/network/netpoll/connection.go
@@ -76,7 +76,7 @@ func (c *Conn) Flush() error {
 
 func (c *Conn) HandleSpecificError(err error, rip string) (needIgnore bool) {
 	if errors.Is(err, netpoll.ErrConnClosed) {
-		hlog.Warnf("HERTZ: Netpoll error=%s, remoteAddr=%s", err.Error(), rip)
+		hlog.SystemLogger().Warnf("Netpoll error=%s, remoteAddr=%s", err.Error(), rip)
 		return true
 	}
 	return false
diff --git a/pkg/network/netpoll/transport.go b/pkg/network/netpoll/transport.go
index 803e34e..844445e 100644
--- a/pkg/network/netpoll/transport.go
+++ b/pkg/network/netpoll/transport.go
@@ -88,7 +88,7 @@ func (t *transporter) ListenAndServe(onReq network.OnData) (err error) {
 	}
 
 	// Start Server
-	hlog.Infof("HERTZ: HTTP server listening on address=%s", t.listener.Addr().String())
+	hlog.SystemLogger().Infof("HTTP server listening on address=%s", t.listener.Addr().String())
 	t.RLock()
 	err = t.eventLoop.Serve(t.listener)
 	t.RUnlock()
diff --git a/pkg/network/standard/transport.go b/pkg/network/standard/transport.go
index 9eec350..624f941 100644
--- a/pkg/network/standard/transport.go
+++ b/pkg/network/standard/transport.go
@@ -60,12 +60,12 @@ func (t *transport) serve() (err error) {
 	if err != nil {
 		return err
 	}
-	hlog.Infof("HERTZ: HTTP server listening on address=%s", t.ln.Addr().String())
+	hlog.SystemLogger().Infof("HERTZ: HTTP server listening on address=%s", t.ln.Addr().String())
 	for {
 		conn, err := t.ln.Accept()
 		var c network.Conn
 		if err != nil {
-			hlog.Errorf("HERTZ: Error=%s", err.Error())
+			hlog.SystemLogger().Errorf("Error=%s", err.Error())
 			return err
 		}
 		if t.tls != nil {
diff --git a/pkg/protocol/cookie.go b/pkg/protocol/cookie.go
index c8de4c0..7ca22c6 100644
--- a/pkg/protocol/cookie.go
+++ b/pkg/protocol/cookie.go
@@ -546,7 +546,7 @@ func getCookieKey(dst, src []byte) []byte {
 func warnIfInvalid(value []byte) bool {
 	for i := range value {
 		if bytesconv.ValidCookieValueTable[value[i]] == 0 {
-			hlog.Warnf("HERTZ: Invalid byte %q in Cookie.Value, "+
+			hlog.SystemLogger().Warnf("Invalid byte %q in Cookie.Value, "+
 				"it may cause compatibility problems with user agents", value[i])
 			return false
 		}
diff --git a/pkg/protocol/header.go b/pkg/protocol/header.go
index 9291d5f..eb28ade 100644
--- a/pkg/protocol/header.go
+++ b/pkg/protocol/header.go
@@ -461,7 +461,7 @@ func checkWriteHeaderCode(code int) {
 	// For now, we only emit a warning for bad codes.
 	// In the future we might block things over 599 or under 100
 	if code < 100 || code > 599 {
-		hlog.Warnf("Invalid StatusCode code %v, status code should not be under 100 or over 599.\n"+
+		hlog.SystemLogger().Warnf("Invalid StatusCode code %v, status code should not be under 100 or over 599.\n"+
 			"For more info: https://www.rfc-editor.org/rfc/rfc9110.html#name-status-codes", code)
 	}
 }
diff --git a/pkg/protocol/http1/client.go b/pkg/protocol/http1/client.go
index f310ee6..cb12bc7 100644
--- a/pkg/protocol/http1/client.go
+++ b/pkg/protocol/http1/client.go
@@ -919,7 +919,7 @@ func dialAddr(addr string, dial network.Dialer, dialDualStack bool, tlsConfig *t
 	var conn network.Conn
 	var err error
 	if dial == nil {
-		hlog.Warnf("HERTZ: HostClient: no dialer specified, trying to use default dialer")
+		hlog.SystemLogger().Warnf("HostClient: no dialer specified, trying to use default dialer")
 		dial = dialer.DefaultDialer()
 	}
 	dialFunc := dial.DialConnection
diff --git a/pkg/protocol/suite/server.go b/pkg/protocol/suite/server.go
index b489e0d..8d58865 100644
--- a/pkg/protocol/suite/server.go
+++ b/pkg/protocol/suite/server.go
@@ -59,7 +59,7 @@ type ServerMap map[string]protocol.Server
 
 func (c *Config) Add(protocol string, factory ServerFactory) {
 	if fac := c.configMap[protocol]; fac != nil {
-		hlog.Warnf("HERTZ: ServerFactory of protocol: %s will be overridden by customized function", protocol)
+		hlog.SystemLogger().Warnf("ServerFactory of protocol: %s will be overridden by customized function", protocol)
 	}
 	c.configMap[protocol] = factory
 }
diff --git a/pkg/route/engine.go b/pkg/route/engine.go
index 812e7f5..45ae38f 100644
--- a/pkg/route/engine.go
+++ b/pkg/route/engine.go
@@ -273,17 +273,17 @@ func (engine *Engine) Shutdown(ctx context.Context) (err error) {
 		// ensure that the hook is executed until wait timeout or finish
 		select {
 		case <-ctx.Done():
-			hlog.Infof("HERTZ: Execute OnShutdownHooks timeout: error=%v", ctx.Err())
+			hlog.SystemLogger().Infof("Execute OnShutdownHooks timeout: error=%v", ctx.Err())
 			return
 		case <-ch:
-			hlog.Info("HERTZ: Execute OnShutdownHooks finish")
+			hlog.SystemLogger().Info("Execute OnShutdownHooks finish")
 			return
 		}
 	}()
 
 	if opt := engine.options; opt != nil && opt.Registry != nil {
 		if err = opt.Registry.Deregister(opt.RegistryInfo); err != nil {
-			hlog.Errorf("HERTZ: Deregister error=%v", err)
+			hlog.SystemLogger().Errorf("Deregister error=%v", err)
 			return err
 		}
 	}
@@ -363,7 +363,7 @@ func (engine *Engine) alpnEnable() bool {
 }
 
 func (engine *Engine) listenAndServe() error {
-	hlog.Infof("HERTZ: Using network library=%s", GetTransporterName())
+	hlog.SystemLogger().Infof("Using network library=%s", GetTransporterName())
 	return engine.transport.ListenAndServe(engine.onData)
 }
 
@@ -383,7 +383,7 @@ func (engine *Engine) getNextProto(conn network.Conn) (proto string, err error)
 	if tlsConn, ok := conn.(network.ConnTLSer); ok {
 		if engine.options.ReadTimeout > 0 {
 			if err := conn.SetReadTimeout(engine.options.ReadTimeout); err != nil {
-				hlog.Errorf("HERTZ: BUG: error in SetReadDeadline=%s: error=%s", engine.options.ReadTimeout, err)
+				hlog.SystemLogger().Errorf("BUG: error in SetReadDeadline=%s: error=%s", engine.options.ReadTimeout, err)
 			}
 		}
 		err = tlsConn.Handshake()
@@ -431,7 +431,7 @@ func errProcess(conn io.Closer, err error) {
 		}
 	}
 	// other errors
-	hlog.Errorf("HERTZ: Error=%s, remoteAddr=%s", err.Error(), rip)
+	hlog.SystemLogger().Errorf("Error=%s, remoteAddr=%s", err.Error(), rip)
 }
 
 func getRemoteAddrFromCloser(conn io.Closer) string {
@@ -477,7 +477,7 @@ func (engine *Engine) Serve(c context.Context, conn network.Conn) (err error) {
 		if bytes.Equal(buf, bytestr.StrClientPreface) && engine.protocolServers[suite.HTTP2] != nil {
 			return engine.protocolServers[suite.HTTP2].Serve(c, conn)
 		}
-		hlog.Warnf("HERTZ: HTTP2 server is not loaded, request is going to fallback to HTTP1 server")
+		hlog.SystemLogger().Warnf("HTTP2 server is not loaded, request is going to fallback to HTTP1 server")
 	}
 
 	// ALPN path
@@ -569,7 +569,7 @@ func debugPrintRoute(httpMethod, absolutePath string, handlers app.HandlersChain
 	if handlerName == "" {
 		handlerName = utils.NameOfFunction(handlers.Last())
 	}
-	hlog.Debugf("HERTZ: Method=%-6s absolutePath=%-25s --> handlerName=%s (num=%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
+	hlog.SystemLogger().Debugf("Method=%-6s absolutePath=%-25s --> handlerName=%s (num=%d handlers)", httpMethod, absolutePath, handlerName, nuHandlers)
 }
 
 func (engine *Engine) addRoute(method, path string, handlers app.HandlersChain) {
@@ -793,7 +793,7 @@ func (engine *Engine) LoadHTMLGlob(pattern string) {
 	if engine.options.AutoReloadRender {
 		files, err := filepath.Glob(pattern)
 		if err != nil {
-			hlog.Errorf("LoadHTMLGlob: %v", err)
+			hlog.SystemLogger().Errorf("LoadHTMLGlob: %v", err)
 			return
 		}
 		engine.SetAutoReloadHTMLTemplate(tmpl, files)


Test patch:
diff --git a/pkg/protocol/header_test.go b/pkg/protocol/header_test.go
index 9a75b3d..195da06 100644
--- a/pkg/protocol/header_test.go
+++ b/pkg/protocol/header_test.go
@@ -198,16 +198,16 @@ func TestCheckWriteHeaderCode(t *testing.T) {
 	buffer := bytes.NewBuffer(make([]byte, 0, 1024))
 	hlog.SetOutput(buffer)
 	checkWriteHeaderCode(99)
-	assert.True(t, strings.Contains(buffer.String(), "[Warn] Invalid StatusCode code"))
+	assert.True(t, strings.Contains(buffer.String(), "[Warn] HERTZ: Invalid StatusCode code"))
 	buffer.Reset()
 	checkWriteHeaderCode(600)
-	assert.True(t, strings.Contains(buffer.String(), "[Warn] Invalid StatusCode code"))
+	assert.True(t, strings.Contains(buffer.String(), "[Warn] HERTZ: Invalid StatusCode code"))
 	buffer.Reset()
 	checkWriteHeaderCode(100)
-	assert.False(t, strings.Contains(buffer.String(), "[Warn] Invalid StatusCode code"))
+	assert.False(t, strings.Contains(buffer.String(), "[Warn] HERTZ: Invalid StatusCode code"))
 	buffer.Reset()
 	checkWriteHeaderCode(599)
-	assert.False(t, strings.Contains(buffer.String(), "[Warn] Invalid StatusCode code"))
+	assert.False(t, strings.Contains(buffer.String(), "[Warn] HERTZ: Invalid StatusCode code"))
 }
 
 func TestResponseHeaderAdd(t *testing.T) {