Repository: go-kratos/kratos
Pull request #2454 (2022-10-17 18:32:38)

build(deps): bump google.golang.org/grpc from 1.46.2 to 1.50.1 in /contrib/registry/etcd
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.46.2 to 1.50.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/grpc/grpc-go/releases">google.golang.org/grpc's releases</a>.</em></p>
<blockquote>
<h2>Release 1.50.1</h2>
<p>New Features</p>
<ul>
<li>gcp/observability: support new configuration defined in public preview user guide</li>
</ul>
<h2>Release 1.50.0</h2>
<h1>Behavior Changes</h1>
<ul>
<li>client: use proper &quot;@&quot; semantics for connecting to abstract unix sockets. (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5678">#5678</a>)
<ul>
<li>This is technically a bug fix; the result is that the address was including a trailing NULL byte, which it should not have.  This may break users creating the socket in Go by prefixing a NULL instead of an &quot;@&quot;, though, so calling it out as a behavior change.</li>
</ul>
<ul>
<li>Special Thanks: <a href="https://github.com/jachor"><code>@​jachor</code></a></li>
</ul>
</li>
</ul>
<h1>New Features</h1>
<ul>
<li>metadata: add experimental <code>ValueFromIncomingContext</code> to more efficiently retrieve a single value (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5596">#5596</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/horpto"><code>@​horpto</code></a></li>
</ul>
</li>
<li>stats: provide peer information in <code>HandleConn</code> context (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5589">#5589</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/feihu-stripe"><code>@​feihu-stripe</code></a></li>
</ul>
</li>
<li>xds: add support for Outlier Detection, enabled by default (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5435">#5435</a>, <a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5673">#5673</a>)</li>
</ul>
<h1>Bug Fixes</h1>
<ul>
<li>client: fix deadlock in transport caused by GOAWAY racing with stream creation (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5652">#5652</a>)
<ul>
<li>This should only occur with an HTTP/2 server that does not follow best practices of an advisory GOAWAY (not a grpc-go server).</li>
</ul>
</li>
<li>xds/xdsclient: fix a bug which was causing routes with <code>cluster_specifier_plugin</code> set to be NACKed when GRPC_EXPERIMENTAL_XDS_RLS_LB was off (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5670">#5670</a>)</li>
<li>xds/xdsclient: NACK cluster resource if <code>config_source_specifier</code> in <code>lrs_server</code> is not <code>self</code> (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5613">#5613</a>)</li>
<li>xds/ringhash: fix a bug which sometimes prevents the LB policy from retrying connection attempts (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5601">#5601</a>)</li>
<li>xds/ringhash: do nothing when asked to exit <code>IDLE</code> instead of falling back on the default channel behavior of connecting to all addresses (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5614">#5614</a>)</li>
<li>xds/rls: fix a bug which was causing the channel to be stuck in <code>IDLE</code> (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5656">#5656</a>)</li>
<li>alts: fix a bug which was setting <code>WaitForReady</code> on handshaker service RPCs, thereby delaying fallback when required (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5620">#5620</a>)</li>
<li>gcp/observability: fix End() to cleanup global state correctly (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5623">#5623</a>)</li>
</ul>
<h2>Release 1.49.0</h2>
<h1>New Features</h1>
<ul>
<li>gcp/observability: add support for Environment Variable <code>GRPC_CONFIG_OBSERVABILITY_JSON</code> (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5525">#5525</a>)</li>
<li>gcp/observability: add support for custom tags (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5565">#5565</a>)</li>
</ul>
<h1>Behavior Changes</h1>
<ul>
<li>server: reduce log level from Warning to Info for early connection establishment errors (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5524">#5524</a>)
<ul>
<li>Special Thanks: <a href="https://github.com/jpkrohling"><code>@​jpkrohling</code></a></li>
</ul>
</li>
</ul>
<h1>Bug Fixes</h1>
<ul>
<li>client: fix race in flow control that could lead to unexpected EOF errors (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5494">#5494</a>)</li>
<li>client: fix a race that could cause RPCs to time out instead of failing more quickly with UNAVAILABLE (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5503">#5503</a>)</li>
<li>client &amp; server: fix a panic caused by passing a <code>nil</code> stats handler to <code>grpc.WithStatsHandler</code> or <code>grpc.StatsHandler</code> (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5543">#5543</a>)</li>
<li>transport/server: fix a race that could cause a stray header to be sent (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5513">#5513</a>)</li>
<li>balancer: give precedence to <code>IDLE</code> over <code>TRANSIENT_FAILURE</code> when aggregating connectivity state (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5473">#5473</a>)</li>
<li>xds/xdsclient: request correct resource name when user specifies a new style resource name with empty authority (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5488">#5488</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/grpc/grpc-go/commit/4c776ec01572d55249df309251900554b46adb41"><code>4c776ec</code></a> Cherry-pick observability changes from master to v1.50.x and update version t...</li>
<li><a href="https://github.com/grpc/grpc-go/commit/6576007e5601072d8ddf9a01720851cd12e2b7bc"><code>6576007</code></a> Change version to 1.50.1-dev (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5686">#5686</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/c1d7d7acc3d14788d07ded8be9cd74c136084294"><code>c1d7d7a</code></a> Change version to 1.50.0 (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5685">#5685</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/1451c62ccdce325a1f2fdbcc90db89518a36dc06"><code>1451c62</code></a> internal/transport: optimize grpc-message encoding/decoding (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5654">#5654</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/be4b63b1fcab082f7e19b540abfbbaf13958addd"><code>be4b63b</code></a> test: minor test cleanup (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5679">#5679</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/d83070ec0d9043f713b6a63e1963c593b447208c"><code>d83070e</code></a> Changed Outlier Detection Env Var to default true (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5673">#5673</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/54521b22e01f52e6e112b33bbca8b2255915dd40"><code>54521b2</code></a> client: remove trailing null from unix abstract socket address (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5678">#5678</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/36e481079b224e01fc3134ba7f1b2717f0c4d4ef"><code>36e4810</code></a> orca: cleanup old code, and get grpc package to use new code (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5627">#5627</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/e8866a83edf51f5a5dcd8097c6d4fe4cdef49cdd"><code>e8866a8</code></a> build: harden GitHub Workflow permissions (<a href="https://github-redirect.dependabot.com/grpc/grpc-go/issues/5660">#5660</a>)</li>
<li><a href="https://github.com/grpc/grpc-go/commit/8458251c6b35036b90788223f67fc9e4396cd32c"><code>8458251</code></a> xdsclient: ignore routes with cluster_specifier_plugin when GRPC_EXPERIMENTAL...</li>
<li>Additional commits viewable in <a href="https://github.com/grpc/grpc-go/compare/v1.46.2...v1.50.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.46.2&new-version=1.50.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/app.go b/app.go
index e3d81ffb..436be294 100644
--- a/app.go
+++ b/app.go
@@ -89,8 +89,15 @@ func (a *App) Run() error {
 	a.mu.Lock()
 	a.instance = instance
 	a.mu.Unlock()
-	eg, ctx := errgroup.WithContext(NewContext(a.ctx, a))
+	sctx := NewContext(a.ctx, a)
+	eg, ctx := errgroup.WithContext(sctx)
 	wg := sync.WaitGroup{}
+
+	for _, fn := range a.opts.beforeStart {
+		if err = fn(sctx); err != nil {
+			return err
+		}
+	}
 	for _, srv := range a.opts.servers {
 		srv := srv
 		eg.Go(func() error {
@@ -102,17 +109,23 @@ func (a *App) Run() error {
 		wg.Add(1)
 		eg.Go(func() error {
 			wg.Done() // here is to ensure server start has begun running before register, so defer is not needed
-			return srv.Start(NewContext(a.opts.ctx, a))
+			return srv.Start(sctx)
 		})
 	}
 	wg.Wait()
 	if a.opts.registrar != nil {
 		rctx, rcancel := context.WithTimeout(ctx, a.opts.registrarTimeout)
 		defer rcancel()
-		if err := a.opts.registrar.Register(rctx, instance); err != nil {
+		if err = a.opts.registrar.Register(rctx, instance); err != nil {
+			return err
+		}
+	}
+	for _, fn := range a.opts.afterStart {
+		if err = fn(sctx); err != nil {
 			return err
 		}
 	}
+
 	c := make(chan os.Signal, 1)
 	signal.Notify(c, a.opts.sigs...)
 	eg.Go(func() error {
@@ -123,28 +136,36 @@ func (a *App) Run() error {
 			return a.Stop()
 		}
 	})
-	if err := eg.Wait(); err != nil && !errors.Is(err, context.Canceled) {
+	if err = eg.Wait(); err != nil && !errors.Is(err, context.Canceled) {
 		return err
 	}
-	return nil
+	for _, fn := range a.opts.afterStop {
+		err = fn(sctx)
+	}
+	return err
 }
 
 // Stop gracefully stops the application.
-func (a *App) Stop() error {
+func (a *App) Stop() (err error) {
+	sctx := NewContext(a.ctx, a)
+	for _, fn := range a.opts.beforeStop {
+		err = fn(sctx)
+	}
+
 	a.mu.Lock()
 	instance := a.instance
 	a.mu.Unlock()
 	if a.opts.registrar != nil && instance != nil {
 		ctx, cancel := context.WithTimeout(NewContext(a.ctx, a), a.opts.registrarTimeout)
 		defer cancel()
-		if err := a.opts.registrar.Deregister(ctx, instance); err != nil {
+		if err = a.opts.registrar.Deregister(ctx, instance); err != nil {
 			return err
 		}
 	}
 	if a.cancel != nil {
 		a.cancel()
 	}
-	return nil
+	return err
 }
 
 func (a *App) buildInstance() (*registry.ServiceInstance, error) {
diff --git a/config/config.go b/config/config.go
index 126a6379..9efc9024 100644
--- a/config/config.go
+++ b/config/config.go
@@ -15,13 +15,13 @@ import (
 	"github.com/go-kratos/kratos/v2/log"
 )
 
+var _ Config = (*config)(nil)
+
 var (
 	// ErrNotFound is key not found.
 	ErrNotFound = errors.New("key not found")
 	// ErrTypeAssert is type assert error.
 	ErrTypeAssert = errors.New("type assert error")
-
-	_ Config = (*config)(nil)
 )
 
 // Observer is config observer.
@@ -44,7 +44,7 @@ type config struct {
 	watchers  []Watcher
 }
 
-// New new a config with options.
+// New a config with options.
 func New(opts ...Option) Config {
 	o := options{
 		decoder:  defaultDecoder,
@@ -62,11 +62,11 @@ func New(opts ...Option) Config {
 func (c *config) watch(w Watcher) {
 	for {
 		kvs, err := w.Next()
-		if errors.Is(err, context.Canceled) {
-			log.Infof("watcher's ctx cancel : %v", err)
-			return
-		}
 		if err != nil {
+			if errors.Is(err, context.Canceled) {
+				log.Infof("watcher's ctx cancel : %v", err)
+				return
+			}
 			time.Sleep(time.Second)
 			log.Errorf("failed to watch next config: %v", err)
 			continue
diff --git a/config/env/env.go b/config/env/env.go
index 30e011b9..cdd30e1b 100644
--- a/config/env/env.go
+++ b/config/env/env.go
@@ -19,11 +19,11 @@ func (e *env) Load() (kv []*config.KeyValue, err error) {
 	return e.load(os.Environ()), nil
 }
 
-func (e *env) load(envStrings []string) []*config.KeyValue {
+func (e *env) load(envs []string) []*config.KeyValue {
 	var kv []*config.KeyValue
-	for _, envstr := range envStrings {
+	for _, env := range envs {
 		var k, v string
-		subs := strings.SplitN(envstr, "=", 2) //nolint:gomnd
+		subs := strings.SplitN(env, "=", 2) //nolint:gomnd
 		k = subs[0]
 		if len(subs) > 1 {
 			v = subs[1]
diff --git a/config/env/watcher.go b/config/env/watcher.go
index 2ea02931..84e7932e 100644
--- a/config/env/watcher.go
+++ b/config/env/watcher.go
@@ -6,13 +6,13 @@ import (
 	"github.com/go-kratos/kratos/v2/config"
 )
 
+var _ config.Watcher = (*watcher)(nil)
+
 type watcher struct {
 	ctx    context.Context
 	cancel context.CancelFunc
 }
 
-var _ config.Watcher = (*watcher)(nil)
-
 func NewWatcher() (config.Watcher, error) {
 	ctx, cancel := context.WithCancel(context.Background())
 	return &watcher{ctx: ctx, cancel: cancel}, nil
diff --git a/config/file/watcher.go b/config/file/watcher.go
index ea309fa8..1f067d1a 100644
--- a/config/file/watcher.go
+++ b/config/file/watcher.go
@@ -10,6 +10,8 @@ import (
 	"github.com/go-kratos/kratos/v2/config"
 )
 
+var _ config.Watcher = (*watcher)(nil)
+
 type watcher struct {
 	f  *file
 	fw *fsnotify.Watcher
@@ -18,8 +20,6 @@ type watcher struct {
 	cancel context.CancelFunc
 }
 
-var _ config.Watcher = (*watcher)(nil)
-
 func newWatcher(f *file) (config.Watcher, error) {
 	fw, err := fsnotify.NewWatcher()
 	if err != nil {
diff --git a/config/reader.go b/config/reader.go
index 6858a0f6..2202de6b 100644
--- a/config/reader.go
+++ b/config/reader.go
@@ -8,11 +8,11 @@ import (
 	"strings"
 	"sync"
 
-	"github.com/go-kratos/kratos/v2/log"
-
 	"github.com/imdario/mergo"
 	"google.golang.org/protobuf/encoding/protojson"
 	"google.golang.org/protobuf/proto"
+
+	"github.com/go-kratos/kratos/v2/log"
 )
 
 // Reader is config reader.
@@ -38,9 +38,7 @@ func newReader(opts options) Reader {
 }
 
 func (r *reader) Merge(kvs ...*KeyValue) error {
-	r.lock.Lock()
-	merged, err := cloneMap(r.values)
-	r.lock.Unlock()
+	merged, err := r.cloneMap()
 	if err != nil {
 		return err
 	}
@@ -79,6 +77,12 @@ func (r *reader) Resolve() error {
 	return r.opts.resolver(r.values)
 }
 
+func (r *reader) cloneMap() (map[string]interface{}, error) {
+	r.lock.Lock()
+	defer r.lock.Unlock()
+	return cloneMap(r.values)
+}
+
 func cloneMap(src map[string]interface{}) (map[string]interface{}, error) {
 	// https://gist.github.com/soroushjp/0ec92102641ddfc3ad5515ca76405f4d
 	var buf bytes.Buffer
@@ -90,12 +94,12 @@ func cloneMap(src map[string]interface{}) (map[string]interface{}, error) {
 	if err != nil {
 		return nil, err
 	}
-	var copy map[string]interface{}
-	err = dec.Decode(&copy)
+	var clone map[string]interface{}
+	err = dec.Decode(&clone)
 	if err != nil {
 		return nil, err
 	}
-	return copy, nil
+	return clone, nil
 }
 
 func convertMap(src interface{}) interface{} {
diff --git a/config/value.go b/config/value.go
index 9f6234f5..11de2e89 100644
--- a/config/value.go
+++ b/config/value.go
@@ -1,7 +1,7 @@
 package config
 
 import (
-	stdjson "encoding/json"
+	"encoding/json"
 	"fmt"
 	"reflect"
 	"strconv"
@@ -10,7 +10,7 @@ import (
 
 	"google.golang.org/protobuf/proto"
 
-	"github.com/go-kratos/kratos/v2/encoding/json"
+	kratosjson "github.com/go-kratos/kratos/v2/encoding/json"
 )
 
 var (
@@ -36,6 +36,10 @@ type atomicValue struct {
 	atomic.Value
 }
 
+func (v *atomicValue) typeAssertError() error {
+	return fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+}
+
 func (v *atomicValue) Bool() (bool, error) {
 	switch val := v.Load().(type) {
 	case bool:
@@ -43,7 +47,7 @@ func (v *atomicValue) Bool() (bool, error) {
 	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, string:
 		return strconv.ParseBool(fmt.Sprint(val))
 	}
-	return false, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	return false, v.typeAssertError()
 }
 
 func (v *atomicValue) Int() (int64, error) {
@@ -73,35 +77,37 @@ func (v *atomicValue) Int() (int64, error) {
 	case float64:
 		return int64(val), nil
 	case string:
-		return strconv.ParseInt(val, 10, 64) //nolint:gomnd
+		return strconv.ParseInt(val, 10, 64)
 	}
-	return 0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	return 0, v.typeAssertError()
 }
 
 func (v *atomicValue) Slice() ([]Value, error) {
-	if vals, ok := v.Load().([]interface{}); ok {
-		var slices []Value
-		for _, val := range vals {
-			a := &atomicValue{}
-			a.Store(val)
-			slices = append(slices, a)
-		}
-		return slices, nil
+	vals, ok := v.Load().([]interface{})
+	if !ok {
+		return nil, v.typeAssertError()
 	}
-	return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	slices := make([]Value, 0, len(vals))
+	for _, val := range vals {
+		a := new(atomicValue)
+		a.Store(val)
+		slices = append(slices, a)
+	}
+	return slices, nil
 }
 
 func (v *atomicValue) Map() (map[string]Value, error) {
-	if vals, ok := v.Load().(map[string]interface{}); ok {
-		m := make(map[string]Value, len(vals))
-		for key, val := range vals {
-			a := new(atomicValue)
-			a.Store(val)
-			m[key] = a
-		}
-		return m, nil
+	vals, ok := v.Load().(map[string]interface{})
+	if !ok {
+		return nil, v.typeAssertError()
+	}
+	m := make(map[string]Value, len(vals))
+	for key, val := range vals {
+		a := new(atomicValue)
+		a.Store(val)
+		m[key] = a
 	}
-	return nil, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	return m, nil
 }
 
 func (v *atomicValue) Float() (float64, error) {
@@ -131,9 +137,9 @@ func (v *atomicValue) Float() (float64, error) {
 	case float64:
 		return val, nil
 	case string:
-		return strconv.ParseFloat(val, 64) //nolint:gomnd
+		return strconv.ParseFloat(val, 64)
 	}
-	return 0.0, fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	return 0.0, v.typeAssertError()
 }
 
 func (v *atomicValue) String() (string, error) {
@@ -147,7 +153,7 @@ func (v *atomicValue) String() (string, error) {
 	case fmt.Stringer:
 		return val.String(), nil
 	}
-	return "", fmt.Errorf("type assert to %v failed", reflect.TypeOf(v.Load()))
+	return "", v.typeAssertError()
 }
 
 func (v *atomicValue) Duration() (time.Duration, error) {
@@ -159,14 +165,14 @@ func (v *atomicValue) Duration() (time.Duration, error) {
 }
 
 func (v *atomicValue) Scan(obj interface{}) error {
-	data, err := stdjson.Marshal(v.Load())
+	data, err := json.Marshal(v.Load())
 	if err != nil {
 		return err
 	}
 	if pb, ok := obj.(proto.Message); ok {
-		return json.UnmarshalOptions.Unmarshal(data, pb)
+		return kratosjson.UnmarshalOptions.Unmarshal(data, pb)
 	}
-	return stdjson.Unmarshal(data, obj)
+	return json.Unmarshal(data, obj)
 }
 
 type errValue struct {
diff --git a/contrib/config/apollo/README.md b/contrib/config/apollo/README.md
index c03cff0f..a8eea493 100644
--- a/contrib/config/apollo/README.md
+++ b/contrib/config/apollo/README.md
@@ -97,5 +97,4 @@ config := map[string]interface{}{
         }
     }
 }
-_ = config
 ```
diff --git a/contrib/config/apollo/apollo.go b/contrib/config/apollo/apollo.go
index 259ed1a9..bd96ef11 100644
--- a/contrib/config/apollo/apollo.go
+++ b/contrib/config/apollo/apollo.go
@@ -3,15 +3,14 @@ package apollo
 import (
 	"strings"
 
-	"github.com/go-kratos/kratos/v2/config"
-	"github.com/go-kratos/kratos/v2/log"
-
 	"github.com/apolloconfig/agollo/v4"
 	"github.com/apolloconfig/agollo/v4/constant"
 	apolloConfig "github.com/apolloconfig/agollo/v4/env/config"
 	"github.com/apolloconfig/agollo/v4/extension"
 
+	"github.com/go-kratos/kratos/v2/config"
 	"github.com/go-kratos/kratos/v2/encoding"
+	"github.com/go-kratos/kratos/v2/log"
 )
 
 type apollo struct {
diff --git a/contrib/config/apollo/watcher.go b/contrib/config/apollo/watcher.go
index 94ef2ef0..ea5ef108 100644
--- a/contrib/config/apollo/watcher.go
+++ b/contrib/config/apollo/watcher.go
@@ -4,12 +4,11 @@ import (
 	"context"
 	"strings"
 
-	"github.com/go-kratos/kratos/v2/encoding"
+	"github.com/apolloconfig/agollo/v4/storage"
 
 	"github.com/go-kratos/kratos/v2/config"
+	"github.com/go-kratos/kratos/v2/encoding"
 	"github.com/go-kratos/kratos/v2/log"
-
-	"github.com/apolloconfig/agollo/v4/storage"
 )
 
 type watcher struct {
diff --git a/contrib/config/consul/README.md b/contrib/config/consul/README.md
index a3d39a18..9dc5a066 100644
--- a/contrib/config/consul/README.md
+++ b/contrib/config/consul/README.md
@@ -2,20 +2,21 @@
 
 ```go
 import (
-    "github.com/go-kratos/kratos/contrib/config/consul/v2"
     "github.com/hashicorp/consul/api"
+
+    "github.com/go-kratos/kratos/contrib/config/consul/v2"
 )
-func main() {
 
+func main() {
     consulClient, err := api.NewClient(&api.Config{
-    Address: "127.0.0.1:8500",
+        Address: "127.0.0.1:8500",
     })
     if err != nil {
         panic(err)
     }
     cs, err := consul.New(consulClient, consul.WithPath("app/cart/configs/"))
-    //consul中需要标注文件后缀，kratos读取配置需要适配文件后缀
-    //The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration.
+    // consul中需要标注文件后缀，kratos读取配置需要适配文件后缀
+    // The file suffix needs to be marked, and kratos needs to adapt the file suffix to read the configuration.
     if err != nil {
         panic(err)
     }
diff --git a/contrib/config/etcd/README.md b/contrib/config/etcd/README.md
index d69edd82..131619c1 100644
--- a/contrib/config/etcd/README.md
+++ b/contrib/config/etcd/README.md
@@ -4,10 +4,11 @@
 import (
 	"log"
 
-	cfg "github.com/go-kratos/kratos/contrib/config/etcd/v2"
-	"github.com/go-kratos/kratos/v2/config"
 	clientv3 "go.etcd.io/etcd/client/v3"
 	"google.golang.org/grpc"
+
+	cfg "github.com/go-kratos/kratos/contrib/config/etcd/v2"
+	"github.com/go-kratos/kratos/v2/config"
 )
 
 // create an etcd client
diff --git a/contrib/config/nacos/README.md b/contrib/config/nacos/README.md
index 71f186bf..fa54caf0 100644
--- a/contrib/config/nacos/README.md
+++ b/contrib/config/nacos/README.md
@@ -2,9 +2,10 @@
 
 ```go
 import (
-	kconfig "github.com/go-kratos/kratos/v2/config"
 	"github.com/nacos-group/nacos-sdk-go/clients"
 	"github.com/nacos-group/nacos-sdk-go/common/constant"
+
+	kconfig "github.com/go-kratos/kratos/v2/config"
 )
 
 
diff --git a/contrib/config/nacos/go.mod b/contrib/config/nacos/go.mod
index 7890e058..6917f24a 100644
--- a/contrib/config/nacos/go.mod
+++ b/contrib/config/nacos/go.mod
@@ -5,6 +5,9 @@ go 1.16
 require (
 	github.com/go-kratos/kratos/v2 v2.4.0
 	github.com/nacos-group/nacos-sdk-go v1.0.9
+)
+
+require (
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/contrib/config/polaris/README.md b/contrib/config/polaris/README.md
index 4c2694c4..b39e78f2 100644
--- a/contrib/config/polaris/README.md
+++ b/contrib/config/polaris/README.md
@@ -2,24 +2,23 @@
 
 ```go
 import (
- "log"
-
- "github.com/polarismesh/polaris-go"
- "github.com/go-kratos/kratos/contrib/config/polaris/v2"
+	"log"
+	
+	"github.com/polarismesh/polaris-go"
+	
+	"github.com/go-kratos/kratos/contrib/config/polaris/v2"
 )
 
 func main() {
+    configApi, err := polaris.NewConfigAPI()
+    if err != nil {
+    	log.Fatalln(err)
+    }
 
- configApi, err := polaris.NewConfigAPI()
- if err != nil {
-  log.Fatalln(err)
- }
-
- source, err := New(&configApi, WithNamespace("default"), WithFileGroup("default"), WithFileName("default.yaml"))
-
- if err != nil {
-  log.Fatalln(err)
- }
- source.Load()
+    source, err := New(&configApi, WithNamespace("default"), WithFileGroup("default"), WithFileName("default.yaml"))
+    if err != nil {
+    	log.Fatalln(err)
+    }
+    source.Load()
 }
 ```
diff --git a/contrib/config/polaris/config.go b/contrib/config/polaris/config.go
index c3c39ece..288db438 100644
--- a/contrib/config/polaris/config.go
+++ b/contrib/config/polaris/config.go
@@ -6,9 +6,9 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/go-kratos/kratos/v2/config"
-
 	"github.com/polarismesh/polaris-go"
+
+	"github.com/go-kratos/kratos/v2/config"
 )
 
 // Option is polaris config option.
diff --git a/contrib/config/polaris/watcher.go b/contrib/config/polaris/watcher.go
index dcd89055..70305550 100644
--- a/contrib/config/polaris/watcher.go
+++ b/contrib/config/polaris/watcher.go
@@ -5,11 +5,11 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/go-kratos/kratos/v2/config"
-	"github.com/go-kratos/kratos/v2/log"
-
 	"github.com/polarismesh/polaris-go"
 	"github.com/polarismesh/polaris-go/pkg/model"
+
+	"github.com/go-kratos/kratos/v2/config"
+	"github.com/go-kratos/kratos/v2/log"
 )
 
 type Watcher struct {
@@ -28,7 +28,7 @@ func getFullPath(namespace string, fileGroup string, fileName string) string {
 	return fmt.Sprintf("%s/%s/%s", namespace, fileGroup, fileName)
 }
 
-func recieve(event model.ConfigFileChangeEvent) {
+func receive(event model.ConfigFileChangeEvent) {
 	meta := event.ConfigFileMetadata
 	ec := eventChanMap[getFullPath(meta.GetNamespace(), meta.GetFileGroup(), meta.GetFileName())]
 	defer func() {
@@ -42,7 +42,7 @@ func recieve(event model.ConfigFileChangeEvent) {
 }
 
 func newWatcher(configFile polaris.ConfigFile) *Watcher {
-	configFile.AddChangeListener(recieve)
+	configFile.AddChangeListener(receive)
 
 	fullPath := getFullPath(configFile.GetNamespace(), configFile.GetFileGroup(), configFile.GetFileName())
 	if _, ok := eventChanMap[fullPath]; !ok {
diff --git a/contrib/log/aliyun/go.mod b/contrib/log/aliyun/go.mod
index 83029a5d..1c7b5fa8 100644
--- a/contrib/log/aliyun/go.mod
+++ b/contrib/log/aliyun/go.mod
@@ -3,7 +3,7 @@ module github.com/go-kratos/kratos/contrib/log/aliyun/v2
 go 1.16
 
 require (
-	github.com/aliyun/aliyun-log-go-sdk v0.1.37
+	github.com/aliyun/aliyun-log-go-sdk v0.1.39
 	github.com/go-kratos/kratos/v2 v2.5.2
 	google.golang.org/protobuf v1.28.1
 )
diff --git a/contrib/log/aliyun/go.sum b/contrib/log/aliyun/go.sum
index 6fde6344..ab2ac488 100644
--- a/contrib/log/aliyun/go.sum
+++ b/contrib/log/aliyun/go.sum
@@ -12,8 +12,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/aliyun/aliyun-log-go-sdk v0.1.37 h1:GvswbgLqVOHNeMWssQ9zA+R7YVDP6arLUP92bKyGZNw=
-github.com/aliyun/aliyun-log-go-sdk v0.1.37/go.mod h1:1QQ59pEJiVVXqKgbHcU6FWIgxT5RKBt+CT8AiQ2bEts=
+github.com/aliyun/aliyun-log-go-sdk v0.1.39 h1:oVLVcRbtOhE4a1D4HhO3OYPR6wMADl3ldcbVeFrRL8Y=
+github.com/aliyun/aliyun-log-go-sdk v0.1.39/go.mod h1:1QQ59pEJiVVXqKgbHcU6FWIgxT5RKBt+CT8AiQ2bEts=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
diff --git a/contrib/metrics/datadog/go.mod b/contrib/metrics/datadog/go.mod
index 42d43453..aebfd094 100644
--- a/contrib/metrics/datadog/go.mod
+++ b/contrib/metrics/datadog/go.mod
@@ -4,8 +4,11 @@ go 1.16
 
 require (
 	github.com/DataDog/datadog-go v4.8.3+incompatible
-	github.com/Microsoft/go-winio v0.5.2 // indirect
 	github.com/go-kratos/kratos/v2 v2.5.2
 )
 
+require (
+	github.com/Microsoft/go-winio v0.5.2 // indirect
+)
+
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/registry/consul/go.mod b/contrib/registry/consul/go.mod
index db17f9cc..c94149ce 100644
--- a/contrib/registry/consul/go.mod
+++ b/contrib/registry/consul/go.mod
@@ -4,7 +4,7 @@ go 1.16
 
 require (
 	github.com/go-kratos/kratos/v2 v2.5.2
-	github.com/hashicorp/consul/api v1.14.0
+	github.com/hashicorp/consul/api v1.15.3
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/registry/consul/go.sum b/contrib/registry/consul/go.sum
index 41a5b15b..b1d94b2a 100644
--- a/contrib/registry/consul/go.sum
+++ b/contrib/registry/consul/go.sum
@@ -91,10 +91,10 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.14.0 h1:Y64GIJ8hYTu+tuGekwO4G4ardXoiCivX9wv1iP/kihk=
-github.com/hashicorp/consul/api v1.14.0/go.mod h1:bcaw5CSZ7NE9qfOfKCI1xb7ZKjzu/MyvQkCLTfqLqxQ=
-github.com/hashicorp/consul/sdk v0.10.0 h1:rGLEh2AWK4K0KCMvqWAz2EYxQqgciIfMagWZ0nVe5MI=
-github.com/hashicorp/consul/sdk v0.10.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw=
+github.com/hashicorp/consul/api v1.15.3 h1:WYONYL2rxTXtlekAqblR2SCdJsizMDIj/uXb5wNy9zU=
+github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY=
+github.com/hashicorp/consul/sdk v0.11.0 h1:HRzj8YSCln2yGgCumN5CL8lYlD3gBurnervJRJAZyC4=
+github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
diff --git a/contrib/registry/etcd/go.mod b/contrib/registry/etcd/go.mod
index c4c072ec..f6ae3437 100644
--- a/contrib/registry/etcd/go.mod
+++ b/contrib/registry/etcd/go.mod
@@ -5,7 +5,7 @@ go 1.16
 require (
 	github.com/go-kratos/kratos/v2 v2.5.2
 	go.etcd.io/etcd/client/v3 v3.5.4
-	google.golang.org/grpc v1.46.2
+	google.golang.org/grpc v1.50.1
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/registry/etcd/go.sum b/contrib/registry/etcd/go.sum
index e847ccab..36c78cdc 100644
--- a/contrib/registry/etcd/go.sum
+++ b/contrib/registry/etcd/go.sum
@@ -268,8 +268,9 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
 google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
+google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/encoding/form/form.go b/encoding/form/form.go
index 823aeb8f..ba0447e4 100644
--- a/encoding/form/form.go
+++ b/encoding/form/form.go
@@ -4,10 +4,10 @@ import (
 	"net/url"
 	"reflect"
 
-	"github.com/go-kratos/kratos/v2/encoding"
-
 	"github.com/go-playground/form/v4"
 	"google.golang.org/protobuf/proto"
+
+	"github.com/go-kratos/kratos/v2/encoding"
 )
 
 const (
@@ -70,7 +70,8 @@ func (c codec) Unmarshal(data []byte, v interface{}) error {
 	}
 	if m, ok := v.(proto.Message); ok {
 		return DecodeValues(m, vs)
-	} else if m, ok := reflect.Indirect(reflect.ValueOf(v)).Interface().(proto.Message); ok {
+	}
+	if m, ok := rv.Interface().(proto.Message); ok {
 		return DecodeValues(m, vs)
 	}
 
diff --git a/encoding/form/proto_decode.go b/encoding/form/proto_decode.go
index fcf46d46..1620162b 100644
--- a/encoding/form/proto_decode.go
+++ b/encoding/form/proto_decode.go
@@ -10,13 +10,12 @@ import (
 	"time"
 
 	"google.golang.org/protobuf/encoding/protojson"
-	"google.golang.org/protobuf/types/known/structpb"
-
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
 	"google.golang.org/protobuf/reflect/protoregistry"
 	"google.golang.org/protobuf/types/known/durationpb"
 	"google.golang.org/protobuf/types/known/fieldmaskpb"
+	"google.golang.org/protobuf/types/known/structpb"
 	"google.golang.org/protobuf/types/known/timestamppb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
 )
@@ -122,15 +121,13 @@ func populateRepeatedField(fd protoreflect.FieldDescriptor, list protoreflect.Li
 }
 
 func populateMapField(fd protoreflect.FieldDescriptor, mp protoreflect.Map, fieldPath []string, values []string) error {
-	flen := len(fieldPath)
-	vlen := len(values)
 	// post sub key.
-	nkey := flen - 1
+	nkey := len(fieldPath) - 1
 	key, err := parseField(fd.MapKey(), fieldPath[nkey])
 	if err != nil {
 		return fmt.Errorf("parsing map key %q: %w", fd.FullName().Name(), err)
 	}
-	vkey := vlen - 1
+	vkey := len(values) - 1
 	value, err := parseField(fd.MapValue(), values[vkey])
 	if err != nil {
 		return fmt.Errorf("parsing map value %q: %w", fd.FullName().Name(), err)
diff --git a/log/filter.go b/log/filter.go
index ab572377..993fa926 100644
--- a/log/filter.go
+++ b/log/filter.go
@@ -64,20 +64,18 @@ func (f *Filter) Log(level Level, keyvals ...interface{}) error {
 	if level < f.level {
 		return nil
 	}
-	// fkv is used to provide a slice to contains both logger.prefix and keyvals for filter
-	var fkv []interface{}
+	// prefixkv contains the slice of arguments defined as prefixes during the log initialization
+	var prefixkv []interface{}
 	l, ok := f.logger.(*logger)
 	if ok && len(l.prefix) > 0 {
-		fkv = make([]interface{}, 0, len(l.prefix)+len(keyvals))
-		fkv = append(fkv, l.prefix...)
-		fkv = append(fkv, keyvals...)
+		prefixkv = make([]interface{}, 0, len(l.prefix))
+		prefixkv = append(prefixkv, l.prefix...)
 	}
-	if !ok {
-		fkv = keyvals
-	}
-	if f.filter != nil && f.filter(level, fkv...) {
+
+	if f.filter != nil && (f.filter(level, prefixkv...) || f.filter(level, keyvals...)) {
 		return nil
 	}
+
 	if len(f.key) > 0 || len(f.value) > 0 {
 		for i := 0; i < len(keyvals); i += 2 {
 			v := i + 1
diff --git a/options.go b/options.go
index 8f9e9a55..5e337a38 100644
--- a/options.go
+++ b/options.go
@@ -30,6 +30,12 @@ type options struct {
 	registrarTimeout time.Duration
 	stopTimeout      time.Duration
 	servers          []transport.Server
+
+	// Before and After funcs
+	beforeStart []func(context.Context) error
+	beforeStop  []func(context.Context) error
+	afterStart  []func(context.Context) error
+	afterStop   []func(context.Context) error
 }
 
 // ID with service id.
@@ -91,3 +97,33 @@ func RegistrarTimeout(t time.Duration) Option {
 func StopTimeout(t time.Duration) Option {
 	return func(o *options) { o.stopTimeout = t }
 }
+
+// Before and Afters
+
+// BeforeStart run funcs before app starts
+func BeforeStart(fn func(context.Context) error) Option {
+	return func(o *options) {
+		o.beforeStart = append(o.beforeStart, fn)
+	}
+}
+
+// BeforeStop run funcs before app stops
+func BeforeStop(fn func(context.Context) error) Option {
+	return func(o *options) {
+		o.beforeStop = append(o.beforeStop, fn)
+	}
+}
+
+// AfterStart run funcs after app starts
+func AfterStart(fn func(context.Context) error) Option {
+	return func(o *options) {
+		o.afterStart = append(o.afterStart, fn)
+	}
+}
+
+// AfterStop run funcs after app stops
+func AfterStop(fn func(context.Context) error) Option {
+	return func(o *options) {
+		o.afterStop = append(o.afterStop, fn)
+	}
+}
diff --git a/selector/node/ewma/node.go b/selector/node/ewma/node.go
index 46d5c41c..3b8d2a02 100644
--- a/selector/node/ewma/node.go
+++ b/selector/node/ewma/node.go
@@ -161,7 +161,7 @@ func (n *Node) Pick() selector.DoneFunc {
 			}
 			var netErr net.Error
 			if errors.Is(context.DeadlineExceeded, di.Err) || errors.Is(context.Canceled, di.Err) ||
-				errors.IsServiceUnavailable(di.Err) || errors.IsGatewayTimeout(di.Err) || errors.As(di.Err, netErr) {
+				errors.IsServiceUnavailable(di.Err) || errors.IsGatewayTimeout(di.Err) || errors.As(di.Err, &netErr) {
 				success = 0
 			}
 		}


Test patch:
diff --git a/app_test.go b/app_test.go
index 2b8c8c9d..92a2ca29 100644
--- a/app_test.go
+++ b/app_test.go
@@ -47,6 +47,22 @@ func TestApp(t *testing.T) {
 		Name("kratos"),
 		Version("v1.0.0"),
 		Server(hs, gs),
+		BeforeStart(func(_ context.Context) error {
+			t.Log("BeforeStart...")
+			return nil
+		}),
+		BeforeStop(func(_ context.Context) error {
+			t.Log("BeforeStop...")
+			return nil
+		}),
+		AfterStart(func(_ context.Context) error {
+			t.Log("AfterStart...")
+			return nil
+		}),
+		AfterStop(func(_ context.Context) error {
+			t.Log("AfterStop...")
+			return nil
+		}),
 		Registrar(&mockRegistry{service: make(map[string]*registry.ServiceInstance)}),
 	)
 	time.AfterFunc(time.Second, func() {
diff --git a/config/config_test.go b/config/config_test.go
index 5699dec1..9a166b00 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -2,7 +2,6 @@ package config
 
 import (
 	"errors"
-	"reflect"
 	"testing"
 )
 
@@ -124,7 +123,7 @@ func TestConfig(t *testing.T) {
 	)
 	err = c.Close()
 	if err != nil {
-		t.Fatal("t is not nil")
+		t.Fatal(err)
 	}
 
 	jSource := newTestJSONSource(_testJSON)
@@ -139,21 +138,21 @@ func TestConfig(t *testing.T) {
 
 	err = cf.Load()
 	if err != nil {
-		t.Fatal("t is not nil")
+		t.Fatal(err)
 	}
 
-	val, err := cf.Value("data.database.driver").String()
+	driver, err := cf.Value("data.database.driver").String()
 	if err != nil {
-		t.Fatal("t is not nil")
+		t.Fatal(err)
 	}
-	if !reflect.DeepEqual(databaseDriver, val) {
-		t.Fatal(`databaseDriver is not equal to val`)
+	if databaseDriver != driver {
+		t.Fatal("databaseDriver is not equal to val")
 	}
 
 	err = cf.Watch("endpoints", func(key string, value Value) {
 	})
 	if err != nil {
-		t.Fatal("t is not nil")
+		t.Fatal(err)
 	}
 
 	jSource.sig <- struct{}{}
@@ -162,24 +161,24 @@ func TestConfig(t *testing.T) {
 	var testConf testConfigStruct
 	err = cf.Scan(&testConf)
 	if err != nil {
-		t.Fatal("t is not nil")
+		t.Fatal(err)
 	}
-	if !reflect.DeepEqual(httpAddr, testConf.Server.HTTP.Addr) {
-		t.Fatal(`httpAddr is not equal to testConf.Server.HTTP.Addr`)
+	if httpAddr != testConf.Server.HTTP.Addr {
+		t.Errorf("testConf.Server.HTTP.Addr want: %s, got: %s", httpAddr, testConf.Server.HTTP.Addr)
 	}
-	if !reflect.DeepEqual(httpTimeout, testConf.Server.HTTP.Timeout) {
-		t.Fatal(`httpTimeout is not equal to testConf.Server.HTTP.Timeout`)
+	if httpTimeout != testConf.Server.HTTP.Timeout {
+		t.Errorf("testConf.Server.HTTP.Timeout want: %.1f, got: %.1f", httpTimeout, testConf.Server.HTTP.Timeout)
 	}
-	if !reflect.DeepEqual(true, testConf.Server.HTTP.EnableSSL) {
-		t.Fatal(`testConf.Server.HTTP.EnableSSL is not equal to true`)
+	if !testConf.Server.HTTP.EnableSSL {
+		t.Error("testConf.Server.HTTP.EnableSSL is not equal to true")
 	}
-	if !reflect.DeepEqual(grpcPort, testConf.Server.GRPC.Port) {
-		t.Fatal(`grpcPort is not equal to testConf.Server.GRPC.Port`)
+	if grpcPort != testConf.Server.GRPC.Port {
+		t.Errorf("testConf.Server.GRPC.Port want: %d, got: %d", grpcPort, testConf.Server.GRPC.Port)
 	}
-	if !reflect.DeepEqual(endpoint1, testConf.Endpoints[0]) {
-		t.Fatal(`endpoint1 is not equal to testConf.Endpoints[0]`)
+	if endpoint1 != testConf.Endpoints[0] {
+		t.Errorf("testConf.Endpoints[0] want: %s, got: %s", endpoint1, testConf.Endpoints[0])
 	}
-	if !reflect.DeepEqual(len(testConf.Endpoints), 2) {
-		t.Fatal(`len(testConf.Endpoints) is not equal to 2`)
+	if len(testConf.Endpoints) != 2 {
+		t.Error("len(testConf.Endpoints) is not equal to 2")
 	}
 }
diff --git a/config/file/file_test.go b/config/file/file_test.go
index 0eb2b24f..ac6ad843 100644
--- a/config/file/file_test.go
+++ b/config/file/file_test.go
@@ -121,10 +121,10 @@ func testWatchFile(t *testing.T, path string) {
 	}
 	kvs, err := watch.Next()
 	if err != nil {
-		t.Errorf(`watch.Next() error(%v)`, err)
+		t.Errorf("watch.Next() error(%v)", err)
 	}
 	if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {
-		t.Errorf(`string(kvs[0].Value(%v) is  not equal to _testJSONUpdate(%v)`, kvs[0].Value, _testJSONUpdate)
+		t.Errorf("string(kvs[0].Value(%v) is  not equal to _testJSONUpdate(%v)", kvs[0].Value, _testJSONUpdate)
 	}
 
 	newFilepath := filepath.Join(filepath.Dir(path), "test1.json")
@@ -133,15 +133,15 @@ func testWatchFile(t *testing.T, path string) {
 	}
 	kvs, err = watch.Next()
 	if err == nil {
-		t.Errorf(`watch.Next() error(%v)`, err)
+		t.Errorf("watch.Next() error(%v)", err)
 	}
 	if kvs != nil {
-		t.Errorf(`watch.Next() error(%v)`, err)
+		t.Errorf("watch.Next() error(%v)", err)
 	}
 
 	err = watch.Stop()
 	if err != nil {
-		t.Errorf(`watch.Stop() error(%v)`, err)
+		t.Errorf("watch.Stop() error(%v)", err)
 	}
 
 	if err := os.Rename(newFilepath, path); err != nil {
@@ -171,10 +171,10 @@ func testWatchDir(t *testing.T, path, file string) {
 
 	kvs, err := watch.Next()
 	if err != nil {
-		t.Errorf(`watch.Next() error(%v)`, err)
+		t.Errorf("watch.Next() error(%v)", err)
 	}
 	if !reflect.DeepEqual(string(kvs[0].Value), _testJSONUpdate) {
-		t.Errorf(`string(kvs[0].Value(%v) is  not equal to _testJSONUpdate(%v)`, kvs[0].Value, _testJSONUpdate)
+		t.Errorf("string(kvs[0].Value(%s) is  not equal to _testJSONUpdate(%v)", kvs[0].Value, _testJSONUpdate)
 	}
 }
 
diff --git a/config/file/format_test.go b/config/file/format_test.go
index 509a9837..75185cc6 100644
--- a/config/file/format_test.go
+++ b/config/file/format_test.go
@@ -1,6 +1,8 @@
 package file
 
-import "testing"
+import (
+	"testing"
+)
 
 func TestFormat(t *testing.T) {
 	tests := []struct {
diff --git a/config/options_test.go b/config/options_test.go
index 51e8b48e..c8cd2bf8 100644
--- a/config/options_test.go
+++ b/config/options_test.go
@@ -15,7 +15,7 @@ func TestDefaultDecoder(t *testing.T) {
 	target := make(map[string]interface{})
 	err := defaultDecoder(src, target)
 	if err != nil {
-		t.Fatal("err is not nil")
+		t.Fatal(err)
 	}
 	if !reflect.DeepEqual(target, map[string]interface{}{"service": []byte("config")}) {
 		t.Fatal(`target is not equal to map[string]interface{}{"service": "config"}`)
@@ -29,7 +29,7 @@ func TestDefaultDecoder(t *testing.T) {
 	target = make(map[string]interface{})
 	err = defaultDecoder(src, target)
 	if err != nil {
-		t.Fatal("err is not nil")
+		t.Fatal(err)
 	}
 	if !reflect.DeepEqual(map[string]interface{}{
 		"service": map[string]interface{}{
@@ -150,7 +150,7 @@ func TestDefaultResolver(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			err := defaultResolver(data)
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			rd := reader{
 				values: data,
@@ -161,25 +161,25 @@ func TestDefaultResolver(t *testing.T) {
 				case int:
 					if actual, err = v.Int(); err == nil {
 						if !reflect.DeepEqual(test.expect.(int), int(actual.(int64))) {
-							t.Fatal(`expect is not equal to actual`)
+							t.Fatal("expect is not equal to actual")
 						}
 					}
 				case string:
 					if actual, err = v.String(); err == nil {
 						if !reflect.DeepEqual(test.expect, actual) {
-							t.Fatal(`expect is not equal to actual`)
+							t.Fatal("expect is not equal to actual")
 						}
 					}
 				case bool:
 					if actual, err = v.Bool(); err == nil {
 						if !reflect.DeepEqual(test.expect, actual) {
-							t.Fatal(`expect is not equal to actual`)
+							t.Fatal("expect is not equal to actual")
 						}
 					}
 				case float64:
 					if actual, err = v.Float(); err == nil {
 						if !reflect.DeepEqual(test.expect, actual) {
-							t.Fatal(`expect is not equal to actual`)
+							t.Fatal("expect is not equal to actual")
 						}
 					}
 				default:
diff --git a/config/reader_test.go b/config/reader_test.go
index 15f8f617..11b2d990 100644
--- a/config/reader_test.go
+++ b/config/reader_test.go
@@ -29,7 +29,7 @@ func TestReader_Merge(t *testing.T) {
 		Format: "json",
 	})
 	if err == nil {
-		t.Fatal(`err is nil`)
+		t.Fatal("err is nil")
 	}
 
 	err = r.Merge(&KeyValue{
@@ -38,15 +38,15 @@ func TestReader_Merge(t *testing.T) {
 		Format: "json",
 	})
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	vv, ok := r.Value("nice")
 	if !ok {
-		t.Fatal(`ok is false`)
+		t.Fatal("ok is false")
 	}
 	vvv, err := vv.String()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	if vvv != "boat" {
 		t.Fatal(`vvv is not equal to "boat"`)
@@ -58,18 +58,18 @@ func TestReader_Merge(t *testing.T) {
 		Format: "json",
 	})
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	vv, ok = r.Value("x")
 	if !ok {
-		t.Fatal(`ok is false`)
+		t.Fatal("ok is false")
 	}
 	vvx, err := vv.Int()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
-	if int64(2) != vvx {
-		t.Fatal(`vvx is not equal to 2`)
+	if vvx != 2 {
+		t.Fatal("vvx is not equal to 2")
 	}
 }
 
@@ -118,27 +118,27 @@ a:
 			r := newReader(opts)
 			err := r.Merge(&test.kv)
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			vv, ok := r.Value("a.b.X")
 			if !ok {
-				t.Fatal(`ok is false`)
+				t.Fatal("ok is false")
 			}
 			vvv, err := vv.Int()
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			if int64(1) != vvv {
-				t.Fatal(`vvv is not equal to 1`)
+				t.Fatal("vvv is not equal to 1")
 			}
 
 			vv, ok = r.Value("a.b.Y")
 			if !ok {
-				t.Fatal(`ok is false`)
+				t.Fatal("ok is false")
 			}
 			vvy, err := vv.String()
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			if vvy != "lol" {
 				t.Fatal(`vvy is not equal to "lol"`)
@@ -146,29 +146,29 @@ a:
 
 			vv, ok = r.Value("a.b.z")
 			if !ok {
-				t.Fatal(`ok is false`)
+				t.Fatal("ok is false")
 			}
 			vvz, err := vv.Bool()
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			if !vvz {
-				t.Fatal(`vvz is not equal to true`)
+				t.Fatal("vvz is not equal to true")
 			}
 
 			_, ok = r.Value("aasasdg=234l.asdfk,")
 			if ok {
-				t.Fatal(`ok is true`)
+				t.Fatal("ok is true")
 			}
 
 			_, ok = r.Value("aas......asdg=234l.asdfk,")
 			if ok {
-				t.Fatal(`ok is true`)
+				t.Fatal("ok is true")
 			}
 
 			_, ok = r.Value("a.b.Y.")
 			if ok {
-				t.Fatal(`ok is true`)
+				t.Fatal("ok is true")
 			}
 		})
 	}
@@ -192,11 +192,11 @@ func TestReader_Source(t *testing.T) {
 		Format: "json",
 	})
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	b, err := r.Source()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	if !reflect.DeepEqual([]byte(`{"a":{"b":{"X":1}}}`), b) {
 		t.Fatal("[]byte(`{\"a\":{\"b\":{\"X\":1}}}`) is not equal to b")
diff --git a/config/value_test.go b/config/value_test.go
index 68558bf8..df771757 100644
--- a/config/value_test.go
+++ b/config/value_test.go
@@ -6,17 +6,17 @@ import (
 	"time"
 )
 
-func Test_atomicValue_Bool(t *testing.T) {
+func TestAtomicValue_Bool(t *testing.T) {
 	vlist := []interface{}{"1", "t", "T", "true", "TRUE", "True", true, 1, int32(1)}
 	for _, x := range vlist {
 		v := atomicValue{}
 		v.Store(x)
 		b, err := v.Bool()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if !b {
-			t.Fatal(`b is not equal to true`)
+			t.Fatal("b is not equal to true")
 		}
 	}
 
@@ -26,10 +26,10 @@ func Test_atomicValue_Bool(t *testing.T) {
 		v.Store(x)
 		b, err := v.Bool()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b {
-			t.Fatal(`b is not equal to false`)
+			t.Fatal("b is not equal to false")
 		}
 	}
 
@@ -39,22 +39,22 @@ func Test_atomicValue_Bool(t *testing.T) {
 		v.Store(x)
 		_, err := v.Bool()
 		if err == nil {
-			t.Fatal(`err is nil`)
+			t.Fatal("err is nil")
 		}
 	}
 }
 
-func Test_atomicValue_Int(t *testing.T) {
+func TestAtomicValue_Int(t *testing.T) {
 	vlist := []interface{}{"123123", float64(123123), int64(123123), int32(123123), 123123}
 	for _, x := range vlist {
 		v := atomicValue{}
 		v.Store(x)
 		b, err := v.Int()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b != 123123 {
-			t.Fatal(`b is not equal to 123123`)
+			t.Fatal("b is not equal to 123123")
 		}
 	}
 
@@ -64,22 +64,22 @@ func Test_atomicValue_Int(t *testing.T) {
 		v.Store(x)
 		_, err := v.Int()
 		if err == nil {
-			t.Fatal(`err is nil`)
+			t.Fatal("err is nil")
 		}
 	}
 }
 
-func Test_atomicValue_Float(t *testing.T) {
+func TestAtomicValue_Float(t *testing.T) {
 	vlist := []interface{}{"123123.1", 123123.1}
 	for _, x := range vlist {
 		v := atomicValue{}
 		v.Store(x)
 		b, err := v.Float()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b != 123123.1 {
-			t.Fatal(`b is not equal to 123123.1`)
+			t.Fatal("b is not equal to 123123.1")
 		}
 	}
 
@@ -89,7 +89,7 @@ func Test_atomicValue_Float(t *testing.T) {
 		v.Store(x)
 		_, err := v.Float()
 		if err == nil {
-			t.Fatal(`err is nil`)
+			t.Fatal("err is nil")
 		}
 	}
 }
@@ -103,17 +103,17 @@ func (t ts) String() string {
 	return fmt.Sprintf("%s%d", t.Name, t.Age)
 }
 
-func Test_atomicValue_String(t *testing.T) {
+func TestAtomicValue_String(t *testing.T) {
 	vlist := []interface{}{"1", float64(1), int64(1), 1, int64(1)}
 	for _, x := range vlist {
 		v := atomicValue{}
 		v.Store(x)
 		b, err := v.String()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b != "1" {
-			t.Fatal(`b is not equal to 1`)
+			t.Fatal("b is not equal to 1")
 		}
 	}
 
@@ -121,7 +121,7 @@ func Test_atomicValue_String(t *testing.T) {
 	v.Store(true)
 	b, err := v.String()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	if b != "true" {
 		t.Fatal(`b is not equal to "true"`)
@@ -134,48 +134,48 @@ func Test_atomicValue_String(t *testing.T) {
 	})
 	b, err = v.String()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	if b != "test10" {
 		t.Fatal(`b is not equal to "test10"`)
 	}
 }
 
-func Test_atomicValue_Duration(t *testing.T) {
+func TestAtomicValue_Duration(t *testing.T) {
 	vlist := []interface{}{int64(5)}
 	for _, x := range vlist {
 		v := atomicValue{}
 		v.Store(x)
 		b, err := v.Duration()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b != time.Duration(5) {
-			t.Fatal(`b is not equal to time.Duration(5)`)
+			t.Fatal("b is not equal to time.Duration(5)")
 		}
 	}
 }
 
-func Test_atomicValue_Slice(t *testing.T) {
+func TestAtomicValue_Slice(t *testing.T) {
 	vlist := []interface{}{int64(5)}
 	v := atomicValue{}
 	v.Store(vlist)
 	slices, err := v.Slice()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	for _, v := range slices {
 		b, err := v.Duration()
 		if err != nil {
-			t.Fatal(`err is not nil`)
+			t.Fatal(err)
 		}
 		if b != time.Duration(5) {
-			t.Fatal(`b is not equal to time.Duration(5)`)
+			t.Fatal("b is not equal to time.Duration(5)")
 		}
 	}
 }
 
-func Test_atomicValue_Map(t *testing.T) {
+func TestAtomicValue_Map(t *testing.T) {
 	vlist := make(map[string]interface{})
 	vlist["5"] = int64(5)
 	vlist["text"] = "text"
@@ -183,21 +183,21 @@ func Test_atomicValue_Map(t *testing.T) {
 	v.Store(vlist)
 	m, err := v.Map()
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 	for k, v := range m {
 		if k == "5" {
 			b, err := v.Duration()
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			if b != time.Duration(5) {
-				t.Fatal(`b is not equal to time.Duration(5)`)
+				t.Fatal("b is not equal to time.Duration(5)")
 			}
 		} else {
 			b, err := v.String()
 			if err != nil {
-				t.Fatal(`err is not nil`)
+				t.Fatal(err)
 			}
 			if b != "text" {
 				t.Fatal(`b is not equal to "text"`)
@@ -206,20 +206,19 @@ func Test_atomicValue_Map(t *testing.T) {
 	}
 }
 
-func Test_atomicValue_Scan(t *testing.T) {
-	var err error
+func TestAtomicValue_Scan(t *testing.T) {
 	v := atomicValue{}
-	err = v.Scan(&struct {
+	err := v.Scan(&struct {
 		A string `json:"a"`
 	}{"a"})
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 
 	err = v.Scan(&struct {
 		A string `json:"a"`
 	}{"a"})
 	if err != nil {
-		t.Fatal(`err is not nil`)
+		t.Fatal(err)
 	}
 }
diff --git a/contrib/config/kubernetes/config_test.go b/contrib/config/kubernetes/config_test.go
index 6cdb1c98..b110b634 100644
--- a/contrib/config/kubernetes/config_test.go
+++ b/contrib/config/kubernetes/config_test.go
@@ -8,14 +8,14 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/go-kratos/kratos/v2/config"
-
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/util/homedir"
+
+	"github.com/go-kratos/kratos/v2/config"
 )
 
 const (
diff --git a/contrib/log/aliyun/aliyun_test.go b/contrib/log/aliyun/aliyun_test.go
index c4bfde48..e3e224ad 100644
--- a/contrib/log/aliyun/aliyun_test.go
+++ b/contrib/log/aliyun/aliyun_test.go
@@ -116,21 +116,22 @@ func TestToString(t *testing.T) {
 		{"float64", 6.66, "6.66"},
 		{"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll
 		{"float32", float32(6.66), "6.66"},
-		{"max float32", math.MaxFloat32, "340282346638528860000000000000000000000"},
-		{"int", int(math.MaxInt64), "9223372036854775807"},
+		{"max float32", float32(math.MaxFloat32), "340282350000000000000000000000000000000"},
+		{"int", math.MaxInt64, "9223372036854775807"},
 		{"uint", uint(math.MaxUint64), "18446744073709551615"},
-		{"int8", math.MaxInt8, "127"},
-		{"uint8", math.MaxUint8, "255"},
-		{"int16", math.MaxInt16, "32767"},
-		{"uint16", math.MaxUint16, "65535"},
-		{"int32", math.MaxInt32, "2147483647"},
-		{"uint32", math.MaxUint32, "4294967295"},
-		{"int64", math.MaxInt64, "9223372036854775807"},
+		{"int8", int8(math.MaxInt8), "127"},
+		{"uint8", uint8(math.MaxUint8), "255"},
+		{"int16", int16(math.MaxInt16), "32767"},
+		{"uint16", uint16(math.MaxUint16), "65535"},
+		{"int32", int32(math.MaxInt32), "2147483647"},
+		{"uint32", uint32(math.MaxUint32), "4294967295"},
+		{"int64", int64(math.MaxInt64), "9223372036854775807"},
 		{"uint64", uint64(math.MaxUint64), "18446744073709551615"},
 		{"string", "abc", "abc"},
 		{"bool", false, "false"},
 		{"[]byte", []byte("abc"), "abc"},
 		{"struct", struct{ Name string }{}, `{"Name":""}`},
+		{"nil", nil, ""},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/contrib/log/tencent/tencent_test.go b/contrib/log/tencent/tencent_test.go
index 76bc13bd..df1fe367 100644
--- a/contrib/log/tencent/tencent_test.go
+++ b/contrib/log/tencent/tencent_test.go
@@ -120,21 +120,22 @@ func TestToString(t *testing.T) {
 		{"float64", 6.66, "6.66"},
 		{"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll
 		{"float32", float32(6.66), "6.66"},
-		{"max float32", math.MaxFloat32, "340282346638528860000000000000000000000"},
-		{"int", int(math.MaxInt64), "9223372036854775807"},
+		{"max float32", float32(math.MaxFloat32), "340282350000000000000000000000000000000"},
+		{"int", math.MaxInt64, "9223372036854775807"},
 		{"uint", uint(math.MaxUint64), "18446744073709551615"},
-		{"int8", math.MaxInt8, "127"},
-		{"uint8", math.MaxUint8, "255"},
-		{"int16", math.MaxInt16, "32767"},
-		{"uint16", math.MaxUint16, "65535"},
-		{"int32", math.MaxInt32, "2147483647"},
-		{"uint32", math.MaxUint32, "4294967295"},
-		{"int64", math.MaxInt64, "9223372036854775807"},
+		{"int8", int8(math.MaxInt8), "127"},
+		{"uint8", uint8(math.MaxUint8), "255"},
+		{"int16", int16(math.MaxInt16), "32767"},
+		{"uint16", uint16(math.MaxUint16), "65535"},
+		{"int32", int32(math.MaxInt32), "2147483647"},
+		{"uint32", uint32(math.MaxUint32), "4294967295"},
+		{"int64", int64(math.MaxInt64), "9223372036854775807"},
 		{"uint64", uint64(math.MaxUint64), "18446744073709551615"},
 		{"string", "abc", "abc"},
 		{"bool", false, "false"},
 		{"[]byte", []byte("abc"), "abc"},
 		{"struct", struct{ Name string }{}, `{"Name":""}`},
+		{"nil", nil, ""},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
diff --git a/encoding/form/form_test.go b/encoding/form/form_test.go
index ddd053cd..647d1edc 100644
--- a/encoding/form/form_test.go
+++ b/encoding/form/form_test.go
@@ -26,14 +26,12 @@ type TestModel struct {
 	Name string `json:"name"`
 }
 
-const contentType = "x-www-form-urlencoded"
-
 func TestFormCodecMarshal(t *testing.T) {
 	req := &LoginRequest{
 		Username: "kratos",
 		Password: "kratos_pwd",
 	}
-	content, err := encoding.GetCodec(contentType).Marshal(req)
+	content, err := encoding.GetCodec(Name).Marshal(req)
 	if err != nil {
 		t.Errorf("marshal error: %v", err)
 	}
@@ -45,7 +43,7 @@ func TestFormCodecMarshal(t *testing.T) {
 		Username: "kratos",
 		Password: "",
 	}
-	content, err = encoding.GetCodec(contentType).Marshal(req)
+	content, err = encoding.GetCodec(Name).Marshal(req)
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
@@ -57,7 +55,7 @@ func TestFormCodecMarshal(t *testing.T) {
 		ID:   1,
 		Name: "kratos",
 	}
-	content, err = encoding.GetCodec(contentType).Marshal(m)
+	content, err = encoding.GetCodec(Name).Marshal(m)
 	t.Log(string(content))
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
@@ -72,13 +70,13 @@ func TestFormCodecUnmarshal(t *testing.T) {
 		Username: "kratos",
 		Password: "kratos_pwd",
 	}
-	content, err := encoding.GetCodec(contentType).Marshal(req)
+	content, err := encoding.GetCodec(Name).Marshal(req)
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
 
 	bindReq := new(LoginRequest)
-	err = encoding.GetCodec(contentType).Unmarshal(content, bindReq)
+	err = encoding.GetCodec(Name).Unmarshal(content, bindReq)
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
@@ -119,7 +117,7 @@ func TestProtoEncodeDecode(t *testing.T) {
 		String_:   &wrapperspb.StringValue{Value: "go-kratos"},
 		Bytes:     &wrapperspb.BytesValue{Value: []byte("123")},
 	}
-	content, err := encoding.GetCodec(contentType).Marshal(in)
+	content, err := encoding.GetCodec(Name).Marshal(in)
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
@@ -130,7 +128,7 @@ func TestProtoEncodeDecode(t *testing.T) {
 		t.Errorf("rawpath is not equal to %v", string(content))
 	}
 	in2 := &complex.Complex{}
-	err = encoding.GetCodec(contentType).Unmarshal(content, in2)
+	err = encoding.GetCodec(Name).Unmarshal(content, in2)
 	if err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
@@ -163,7 +161,7 @@ func TestProtoEncodeDecode(t *testing.T) {
 func TestDecodeStructPb(t *testing.T) {
 	req := new(ectest.StructPb)
 	query := `data={"name":"kratos"}&data_list={"name1": "kratos"}&data_list={"name2": "go-kratos"}`
-	if err := encoding.GetCodec(contentType).Unmarshal([]byte(query), req); err != nil {
+	if err := encoding.GetCodec(Name).Unmarshal([]byte(query), req); err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
 	if !reflect.DeepEqual("kratos", req.Data.GetFields()["name"].GetStringValue()) {
@@ -186,7 +184,7 @@ func TestDecodeBytesValuePb(t *testing.T) {
 	val := base64.URLEncoding.EncodeToString([]byte(url))
 	content := "bytes=" + val
 	in2 := &complex.Complex{}
-	if err := encoding.GetCodec(contentType).Unmarshal([]byte(content), in2); err != nil {
+	if err := encoding.GetCodec(Name).Unmarshal([]byte(content), in2); err != nil {
 		t.Errorf("expect %v, got %v", nil, err)
 	}
 	if !reflect.DeepEqual(url, string(in2.Bytes.Value)) {
diff --git a/encoding/form/proto_decode_test.go b/encoding/form/proto_decode_test.go
new file mode 100644
index 00000000..fbe23433
--- /dev/null
+++ b/encoding/form/proto_decode_test.go
@@ -0,0 +1,217 @@
+package form
+
+import (
+	"fmt"
+	"net/url"
+	"reflect"
+	"strconv"
+	"testing"
+
+	"google.golang.org/protobuf/reflect/protoreflect"
+
+	"github.com/go-kratos/kratos/v2/internal/testdata/complex"
+)
+
+func TestDecodeValues(t *testing.T) {
+	form, err := url.ParseQuery("a=19&age=18&b=true&bool=false&byte=MTIz&bytes=MTIz&count=3&d=22.22&double=12.33&duration=" +
+		"2m0.000000022s&field=1%2C2&float=12.34&id=2233&int32=32&int64=64&numberOne=2233&price=11.23&sex=woman&simples=3344&" +
+		"simples=5566&string=go-kratos&timestamp=1970-01-01T00%3A00%3A20.000000002Z&uint32=32&uint64=64&very_simple.component=5566")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	comp := &complex.Complex{}
+	err = DecodeValues(comp, form)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if comp.Id != int64(2233) {
+		t.Errorf("want %v, got %v", int64(2233), comp.Id)
+	}
+	if comp.NoOne != "2233" {
+		t.Errorf("want %v, got %v", "2233", comp.NoOne)
+	}
+	if comp.Simple == nil {
+		t.Fatalf("want %v, got %v", nil, comp.Simple)
+	}
+	if comp.Simple.Component != "5566" {
+		t.Errorf("want %v, got %v", "5566", comp.Simple.Component)
+	}
+	if len(comp.Simples) != 2 {
+		t.Fatalf("want %v, got %v", 2, len(comp.Simples))
+	}
+	if comp.Simples[0] != "3344" {
+		t.Errorf("want %v, got %v", "3344", comp.Simples[0])
+	}
+	if comp.Simples[1] != "5566" {
+		t.Errorf("want %v, got %v", "5566", comp.Simples[1])
+	}
+}
+
+func TestGetFieldDescriptor(t *testing.T) {
+	comp := &complex.Complex{}
+
+	field := getFieldDescriptor(comp.ProtoReflect(), "id")
+	if field.Kind() != protoreflect.Int64Kind {
+		t.Errorf("want: %d, got: %d", protoreflect.Int64Kind, field.Kind())
+	}
+
+	field = getFieldDescriptor(comp.ProtoReflect(), "simples")
+	if field.Kind() != protoreflect.StringKind {
+		t.Errorf("want: %d, got: %d", protoreflect.StringKind, field.Kind())
+	}
+}
+
+func TestPopulateRepeatedField(t *testing.T) {
+	query, err := url.ParseQuery("simples=3344&simples=5566")
+	if err != nil {
+		t.Fatal(err)
+	}
+	comp := &complex.Complex{}
+	field := getFieldDescriptor(comp.ProtoReflect(), "simples")
+
+	err = populateRepeatedField(field, comp.ProtoReflect().Mutable(field).List(), query["simples"])
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual([]string{"3344", "5566"}, comp.GetSimples()) {
+		t.Errorf("want: %v, got: %v", []string{"3344", "5566"}, comp.GetSimples())
+	}
+}
+
+func TestPopulateMapField(t *testing.T) {
+	query, err := url.ParseQuery("map%5Bkratos%5D=https://go-kratos.dev/")
+	if err != nil {
+		t.Fatal(err)
+	}
+	comp := &complex.Complex{}
+	field := getFieldDescriptor(comp.ProtoReflect(), "map")
+	// Fill the comp map field with the url query values
+	err = populateMapField(field, comp.ProtoReflect().Mutable(field).Map(), []string{"kratos"}, query["map[kratos]"])
+	if err != nil {
+		t.Fatal(err)
+	}
+	// Get the comp map field value
+	if query["map[kratos]"][0] != comp.Map["kratos"] {
+		t.Errorf("want: %s, got: %s", query["map[kratos]"], comp.Map["kratos"])
+	}
+}
+
+func TestParseField(t *testing.T) {
+	tests := []struct {
+		name                    string
+		fieldName               string
+		protoReflectKind        protoreflect.Kind
+		value                   string
+		targetProtoReflectValue protoreflect.Value
+		targetErr               error
+	}{
+		{
+			name:                    "BoolKind",
+			fieldName:               "b",
+			protoReflectKind:        protoreflect.BoolKind,
+			value:                   "true",
+			targetProtoReflectValue: protoreflect.ValueOfBool(true),
+			targetErr:               nil,
+		},
+		{
+			name:                    "BoolKind",
+			fieldName:               "b",
+			protoReflectKind:        protoreflect.BoolKind,
+			value:                   "a",
+			targetProtoReflectValue: protoreflect.Value{},
+			targetErr:               &strconv.NumError{Func: "ParseBool", Num: "a", Err: strconv.ErrSyntax},
+		},
+		{
+			name:                    "EnumKind",
+			fieldName:               "sex",
+			protoReflectKind:        protoreflect.EnumKind,
+			value:                   "1",
+			targetProtoReflectValue: protoreflect.ValueOfEnum(1),
+			targetErr:               nil,
+		},
+		{
+			name:                    "EnumKind",
+			fieldName:               "sex",
+			protoReflectKind:        protoreflect.EnumKind,
+			value:                   "2",
+			targetProtoReflectValue: protoreflect.Value{},
+			targetErr:               fmt.Errorf("%q is not a valid value", "2"),
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			comp := &complex.Complex{}
+			field := getFieldDescriptor(comp.ProtoReflect(), test.fieldName)
+			if test.protoReflectKind != field.Kind() {
+				t.Fatalf("want: %d, got: %d", test.protoReflectKind, field.Kind())
+			}
+			val, err := parseField(field, test.value)
+			if !reflect.DeepEqual(test.targetErr, err) {
+				t.Fatalf("want: %s, got: %s", test.targetErr, err)
+			}
+			if !reflect.DeepEqual(test.targetProtoReflectValue, val) {
+				t.Errorf("want: %s, got: %s", test.targetProtoReflectValue, val)
+			}
+		})
+	}
+}
+
+func TestJsonSnakeCase(t *testing.T) {
+	tests := []struct {
+		camelCase string
+		snakeCase string
+	}{
+		{
+			"userId", "user_id",
+		},
+		{
+			"user", "user",
+		},
+		{
+			"userIdAndUsername", "user_id_and_username",
+		},
+		{
+			"", "",
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.camelCase, func(t *testing.T) {
+			snake := jsonSnakeCase(test.camelCase)
+			if snake != test.snakeCase {
+				t.Errorf("want: %s, got: %s", test.snakeCase, snake)
+			}
+		})
+	}
+}
+
+func TestIsASCIIUpper(t *testing.T) {
+	tests := []struct {
+		b     byte
+		upper bool
+	}{
+		{
+			'A', true,
+		},
+		{
+			'a', false,
+		},
+		{
+			',', false,
+		},
+		{
+			'1', false,
+		},
+		{
+			' ', false,
+		},
+	}
+	for _, test := range tests {
+		t.Run(string(test.b), func(t *testing.T) {
+			upper := isASCIIUpper(test.b)
+			if test.upper != upper {
+				t.Errorf("'%s' is not ascii upper", string(test.b))
+			}
+		})
+	}
+}
diff --git a/log/filter_test.go b/log/filter_test.go
index d3c8d548..3e36deac 100644
--- a/log/filter_test.go
+++ b/log/filter_test.go
@@ -102,13 +102,19 @@ func TestFilterFuncWitchLoggerPrefix(t *testing.T) {
 			want:   "",
 		},
 		{
+			// Filtered value
 			logger: NewFilter(With(NewStdLogger(buf), "caller", "caller"), FilterFunc(testFilterFuncWithLoggerPrefix)),
-			want:   "INFO caller=caller msg=msg\n",
+			want:   "INFO caller=caller msg=msg filtered=***\n",
+		},
+		{
+			// NO prefix
+			logger: NewFilter(With(NewStdLogger(buf)), FilterFunc(testFilterFuncWithLoggerPrefix)),
+			want:   "INFO msg=msg filtered=***\n",
 		},
 	}
 
 	for _, tt := range tests {
-		err := tt.logger.Log(LevelInfo, "msg", "msg")
+		err := tt.logger.Log(LevelInfo, "msg", "msg", "filtered", "true")
 		if err != nil {
 			t.Fatal("err should be nil")
 		}
@@ -128,6 +134,9 @@ func testFilterFuncWithLoggerPrefix(level Level, keyvals ...interface{}) bool {
 		if keyvals[i] == "prefix" {
 			return true
 		}
+		if keyvals[i] == "filtered" {
+			keyvals[i+1] = fuzzyStr
+		}
 	}
 	return false
 }
diff --git a/options_test.go b/options_test.go
index 438de3d8..60600173 100644
--- a/options_test.go
+++ b/options_test.go
@@ -152,3 +152,39 @@ func TestStopTimeout(t *testing.T) {
 		t.Fatal("o.stopTimeout is not equal to v")
 	}
 }
+
+func TestBeforeStart(t *testing.T) {
+	o := &options{}
+	v := func(_ context.Context) error {
+		t.Log("BeforeStart...")
+		return nil
+	}
+	BeforeStart(v)(o)
+}
+
+func TestBeforeStop(t *testing.T) {
+	o := &options{}
+	v := func(_ context.Context) error {
+		t.Log("BeforeStop...")
+		return nil
+	}
+	BeforeStop(v)(o)
+}
+
+func TestAfterStart(t *testing.T) {
+	o := &options{}
+	v := func(_ context.Context) error {
+		t.Log("AfterStart...")
+		return nil
+	}
+	AfterStart(v)(o)
+}
+
+func TestAfterStop(t *testing.T) {
+	o := &options{}
+	v := func(_ context.Context) error {
+		t.Log("AfterStop...")
+		return nil
+	}
+	AfterStop(v)(o)
+}
diff --git a/selector/node/ewma/node_test.go b/selector/node/ewma/node_test.go
index feac09f2..d930495c 100644
--- a/selector/node/ewma/node_test.go
+++ b/selector/node/ewma/node_test.go
@@ -2,6 +2,7 @@ package ewma
 
 import (
 	"context"
+	"net"
 	"reflect"
 	"testing"
 	"time"
@@ -100,11 +101,15 @@ func TestDirectErrorHandler(t *testing.T) {
 			Endpoints: []string{"http://127.0.0.1:9090"},
 			Metadata:  map[string]string{"weight": "10"},
 		}))
-
+	errs := []error{
+		context.DeadlineExceeded,
+		context.Canceled,
+		net.ErrClosed,
+	}
 	for i := 0; i < 5; i++ {
 		var err error
 		if i != 0 {
-			err = context.DeadlineExceeded
+			err = errs[i%len(errs)]
 		}
 		done := wn.Pick()
 		if done == nil {