Repository: go-kratos/kratos (go)

Issue:
[Question] register grpc encoding incompatible with older code upgrade
This [PR](https://github.com/go-kratos/kratos/pull/1908), change the default behavior `encoding.RegisterCodec(codec{})`

If we already replace json encoding  use `init` before, add this part of the code will affect the existing code, it will load after, make the original ineffective.

Use `init` to set default encoding introduces an implicit change leading to inconsistent results, we'd better to declare statement directly, and provide a optional way to overwrite.


Pull request:
fix(grpc/codec): fix default behavior for gRPC encoding
fix #2093

Patch:
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 3794a7c0..16bb0a9f 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -44,7 +44,7 @@ jobs:
     steps:
       - uses: actions/checkout@v3
       - name: Set up Go
-        uses: actions/setup-go@v3.2.0
+        uses: actions/setup-go@v3.2.1
         with:
           go-version: ${{ matrix.go }}
 
diff --git a/README_zh.md b/README_zh.md
index 7bf99bf0..230f52f0 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -38,18 +38,18 @@ Kratos 一套轻量级 Go 微服务框架，包含大量微服务相关功能及
 * 工具链：包含大量工具链，比如 cache 代码生成，lint 工具等等；
 
 ## Features
-* [APIs](https://github.com/go-kratos/examples/helloworld/helloworld) ：协议通信以 HTTP/gRPC 为基础，通过 Protobuf 进行定义；
-* [Errors](https://github.com/go-kratos/examples/errors/api) ：通过 Protobuf 的 Enum 作为错误码定义，以及工具生成判定接口；
-* [Metadata](https://github.com/go-kratos/examples/metadata) ：在协议通信 HTTP/gRPC 中，通过 Middleware 规范化服务元信息传递；
-* [Config](https://github.com/go-kratos/examples/config) ：支持多数据源方式，进行配置合并铺平，通过 Atomic 方式支持动态配置；
-* [Logger](https://github.com/go-kratos/examples/log) ：标准日志接口，可方便集成三方 log 库，并可通过 fluentd 收集日志；
-* [Metrics](https://github.com/go-kratos/examples/metrics) ：统一指标接口，可以实现各种指标系统，默认集成 Prometheus；
-* [Tracing](https://github.com/go-kratos/examples/traces) ：遵循 OpenTelemetry 规范定义，以实现微服务链路追踪；
-* [Encoding](encoding)：支持 Accept 和 Content-Type 进行自动选择内容编码；
-* [Transport](transport/transport.go) ：通用的 [HTTP](https://github.com/go-kratos/examples/http/middlewares) /[gRPC](https://github.com/go-kratos/examples/middleware/main.go) 传输层，实现统一的 [Middleware](middleware) 插件支持；
-* [Registry](https://github.com/go-kratos/examples/registry) ：实现统一注册中心接口，可插件化对接各种注册中心；
-* [Validation](https://github.com/go-kratos/examples/validate): 通过Protobuf统一定义校验规则，并同时适用于HTTP/gRPC服务.
-* [SwaggerAPI](https://github.com/go-kratos/swagger-api/blob/main/examples/helloworld/server/main.go): 通过集成第三方[Swagger插件](https://github.com/go-kratos/swagger-api) 能够自动生成Swagger API json并启动一个内置的Swagger UI服务.
+* [APIs](https://go-kratos.dev/docs/component/api) ：协议通信以 HTTP/gRPC 为基础，通过 Protobuf 进行定义；
+* [Errors](https://go-kratos.dev/docs/component/errors/) ：通过 Protobuf 的 Enum 作为错误码定义，以及工具生成判定接口；
+* [Metadata](https://go-kratos.dev/docs/component/metadata) ：在协议通信 HTTP/gRPC 中，通过 Middleware 规范化服务元信息传递；
+* [Config](https://go-kratos.dev/docs/component/config) ：支持多数据源方式，进行配置合并铺平，通过 Atomic 方式支持动态配置；
+* [Logger](https://go-kratos.dev/docs/component/log) ：标准日志接口，可方便集成三方 log 库，并可通过 fluentd 收集日志；
+* [Metrics](https://go-kratos.dev/docs/component/middleware/metrics) ：统一指标接口，可以实现各种指标系统，默认集成 Prometheus；
+* [Tracing](https://go-kratos.dev/docs/component/middleware/tracing) ：遵循 OpenTelemetry 规范定义，以实现微服务链路追踪；
+* [Encoding](https://go-kratos.dev/docs/component/encoding) ：支持 Accept 和 Content-Type 进行自动选择内容编码；
+* [Transport](https://go-kratos.dev/docs/component/transport/overview) ：通用的 [HTTP](https://go-kratos.dev/docs/component/transport/http) /[gRPC](https://go-kratos.dev/docs/component/transport/grpc) 传输层，实现统一的 [Middleware](https://go-kratos.dev/docs/component/middleware/overview) 插件支持；
+* [Registry](https://go-kratos.dev/docs/component/registry) ：实现统一注册中心接口，可插件化对接各种注册中心；
+* [Validation](https://go-kratos.dev/docs/component/middleware/validate): 通过Protobuf统一定义校验规则，并同时适用于HTTP/gRPC服务.
+* [SwaggerAPI](https://go-kratos.dev/docs/guide/openapi): 通过集成第三方[Swagger插件](https://github.com/go-kratos/swagger-api) 能够自动生成Swagger API json并启动一个内置的Swagger UI服务.
 
 ## Getting Started
 ### Required
diff --git a/cmd/kratos/internal/project/add.go b/cmd/kratos/internal/project/add.go
index 98661cd1..f4c1b88c 100644
--- a/cmd/kratos/internal/project/add.go
+++ b/cmd/kratos/internal/project/add.go
@@ -12,7 +12,7 @@ import (
 )
 
 var repoAddIgnores = []string{
-	".git", ".github", "api", "README.md", "LICENSE", "go.mod", "go.sum", "third_party",
+	".git", ".github", "api", "README.md", "LICENSE", "go.mod", "go.sum", "third_party", "openapi.yaml", ".gitignore",
 }
 
 func (p *Project) Add(ctx context.Context, dir string, layout string, branch string, mod string) error {
diff --git a/cmd/kratos/internal/proto/server/server.go b/cmd/kratos/internal/proto/server/server.go
index dcc19790..3b8a8196 100644
--- a/cmd/kratos/internal/proto/server/server.go
+++ b/cmd/kratos/internal/proto/server/server.go
@@ -9,6 +9,8 @@ import (
 
 	"github.com/emicklei/proto"
 	"github.com/spf13/cobra"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 )
 
 // CmdServer the service command.
@@ -54,7 +56,7 @@ func run(cmd *cobra.Command, args []string) {
 		proto.WithService(func(s *proto.Service) {
 			cs := &Service{
 				Package: pkg,
-				Service: s.Name,
+				Service: serviceName(s.Name),
 			}
 			for _, e := range s.Elements {
 				r, ok := e.(*proto.RPC)
@@ -62,7 +64,7 @@ func run(cmd *cobra.Command, args []string) {
 					continue
 				}
 				cs.Methods = append(cs.Methods, &Method{
-					Service: s.Name, Name: ucFirst(r.Name), Request: r.RequestType,
+					Service: serviceName(s.Name), Name: serviceName(r.Name), Request: r.RequestType,
 					Reply: r.ReturnsType, Type: getMethodType(r.StreamsRequest, r.StreamsReturns),
 				})
 			}
@@ -103,10 +105,12 @@ func getMethodType(streamsRequest, streamsReturns bool) MethodType {
 	return unaryType
 }
 
-func ucFirst(str string) string {
-	if str == "" {
-		return ""
-	}
+func serviceName(name string) string {
+	return toUpperCamelCase(strings.Split(name, ".")[0])
+}
 
-	return strings.ToUpper(str[:1]) + str[1:]
+func toUpperCamelCase(s string) string {
+	s = strings.ReplaceAll(s, "_", " ")
+	s = cases.Title(language.Und, cases.NoLower).String(s)
+	return strings.ReplaceAll(s, " ", "")
 }
diff --git a/cmd/kratos/internal/run/run.go b/cmd/kratos/internal/run/run.go
index a84d45ba..ac229bab 100644
--- a/cmd/kratos/internal/run/run.go
+++ b/cmd/kratos/internal/run/run.go
@@ -23,8 +23,9 @@ var CmdRun = &cobra.Command{
 // Run run project.
 func Run(cmd *cobra.Command, args []string) {
 	var dir string
-	if len(args) > 0 {
-		dir = args[0]
+	cmdArgs, programArgs := splitArgs(cmd, args)
+	if len(cmdArgs) > 0 {
+		dir = cmdArgs[0]
 	}
 	base, err := os.Getwd()
 	if err != nil {
@@ -63,7 +64,7 @@ func Run(cmd *cobra.Command, args []string) {
 			dir = cmdPath[dir]
 		}
 	}
-	fd := exec.Command("go", "run", ".")
+	fd := exec.Command("go", append([]string{"run", "."}, programArgs...)...)
 	fd.Stdout = os.Stdout
 	fd.Stderr = os.Stderr
 	fd.Dir = dir
@@ -73,6 +74,14 @@ func Run(cmd *cobra.Command, args []string) {
 	}
 }
 
+func splitArgs(cmd *cobra.Command, args []string) (cmdArgs, programArgs []string) {
+	dashAt := cmd.ArgsLenAtDash()
+	if dashAt >= 0 {
+		return args[:dashAt], args[dashAt:]
+	}
+	return args, []string{}
+}
+
 func findCMD(base string) (map[string]string, error) {
 	wd, err := os.Getwd()
 	if err != nil {
diff --git a/contrib/config/kubernetes/go.mod b/contrib/config/kubernetes/go.mod
index aa900a17..0ba216b7 100644
--- a/contrib/config/kubernetes/go.mod
+++ b/contrib/config/kubernetes/go.mod
@@ -4,8 +4,8 @@ go 1.16
 
 require (
 	github.com/go-kratos/kratos/v2 v2.4.0
-	k8s.io/api v0.24.2
-	k8s.io/apimachinery v0.24.2
+	k8s.io/api v0.24.3
+	k8s.io/apimachinery v0.24.3
 	k8s.io/client-go v0.24.2
 )
 
diff --git a/contrib/config/kubernetes/go.sum b/contrib/config/kubernetes/go.sum
index 138314b9..6b48ebd1 100644
--- a/contrib/config/kubernetes/go.sum
+++ b/contrib/config/kubernetes/go.sum
@@ -652,10 +652,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
 k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
-k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
+k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY=
+k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI=
 k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
+k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=
+k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
 k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
 k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
diff --git a/contrib/config/polaris/config.go b/contrib/config/polaris/config.go
index f5d52d18..c3c39ece 100644
--- a/contrib/config/polaris/config.go
+++ b/contrib/config/polaris/config.go
@@ -43,11 +43,11 @@ func WithFileName(fileName string) Option {
 }
 
 type source struct {
-	client  *polaris.ConfigAPI
+	client  polaris.ConfigAPI
 	options *options
 }
 
-func New(client *polaris.ConfigAPI, opts ...Option) (config.Source, error) {
+func New(client polaris.ConfigAPI, opts ...Option) (config.Source, error) {
 	options := &options{
 		namespace: "default",
 		fileGroup: "",
@@ -74,12 +74,7 @@ func New(client *polaris.ConfigAPI, opts ...Option) (config.Source, error) {
 
 // Load return the config values
 func (s *source) Load() ([]*config.KeyValue, error) {
-	configAPI, err := polaris.NewConfigAPI()
-	if err != nil {
-		return nil, err
-	}
-
-	configFile, err := configAPI.GetConfigFile(s.options.namespace, s.options.fileGroup, s.options.fileName)
+	configFile, err := s.client.GetConfigFile(s.options.namespace, s.options.fileGroup, s.options.fileName)
 	if err != nil {
 		fmt.Println("fail to get config.", err)
 		return nil, err
diff --git a/contrib/log/logrus/logrus.go b/contrib/log/logrus/logrus.go
index 748055e5..b08ba6e1 100644
--- a/contrib/log/logrus/logrus.go
+++ b/contrib/log/logrus/logrus.go
@@ -33,6 +33,8 @@ func (l *Logger) Log(level log.Level, keyvals ...interface{}) (err error) {
 		logrusLevel = logrus.WarnLevel
 	case log.LevelError:
 		logrusLevel = logrus.ErrorLevel
+	case log.LevelFatal:
+		logrusLevel = logrus.FatalLevel
 	default:
 		logrusLevel = logrus.DebugLevel
 	}
diff --git a/contrib/metrics/prometheus/go.mod b/contrib/metrics/prometheus/go.mod
index 1b90433c..85b161bf 100644
--- a/contrib/metrics/prometheus/go.mod
+++ b/contrib/metrics/prometheus/go.mod
@@ -5,7 +5,7 @@ go 1.16
 require (
 	github.com/go-kratos/kratos/v2 v2.4.0
 	github.com/prometheus/client_golang v1.12.2
-	github.com/prometheus/common v0.32.1
+	github.com/prometheus/common v0.36.0
 )
 
 replace github.com/go-kratos/kratos/v2 => ../../../
diff --git a/contrib/metrics/prometheus/go.sum b/contrib/metrics/prometheus/go.sum
index b7e71ac8..17a8f8a2 100644
--- a/contrib/metrics/prometheus/go.sum
+++ b/contrib/metrics/prometheus/go.sum
@@ -74,10 +74,12 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-kratos/aegis v0.1.2/go.mod h1:jYeSQ3Gesba478zEnujOiG5QdsyF3Xk/8owFUeKcHxw=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
@@ -183,6 +185,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
 github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -193,8 +196,9 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
 github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/common v0.36.0 h1:78hJTing+BLYLjhXE+Z2BubeEymH5Lr0/Mt8FKkxxYo=
+github.com/prometheus/common v0.36.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
@@ -296,12 +300,15 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -353,11 +360,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -365,6 +375,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/contrib/registry/kubernetes/go.mod b/contrib/registry/kubernetes/go.mod
index 96a1fa66..cd33ac26 100644
--- a/contrib/registry/kubernetes/go.mod
+++ b/contrib/registry/kubernetes/go.mod
@@ -5,8 +5,8 @@ go 1.16
 require (
 	github.com/go-kratos/kratos/v2 v2.4.0
 	github.com/json-iterator/go v1.1.12
-	k8s.io/api v0.24.2
-	k8s.io/apimachinery v0.24.2
+	k8s.io/api v0.24.3
+	k8s.io/apimachinery v0.24.3
 	k8s.io/client-go v0.24.2
 )
 
diff --git a/contrib/registry/kubernetes/go.sum b/contrib/registry/kubernetes/go.sum
index 368dd5d5..4296b6fe 100644
--- a/contrib/registry/kubernetes/go.sum
+++ b/contrib/registry/kubernetes/go.sum
@@ -653,10 +653,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.24.2 h1:g518dPU/L7VRLxWfcadQn2OnsiGWVOadTLpdnqgY2OI=
 k8s.io/api v0.24.2/go.mod h1:AHqbSkTm6YrQ0ObxjO3Pmp/ubFF/KuM7jU+3khoBsOg=
-k8s.io/apimachinery v0.24.2 h1:5QlH9SL2C8KMcrNJPor+LbXVTaZRReml7svPEh4OKDM=
+k8s.io/api v0.24.3 h1:tt55QEmKd6L2k5DP6G/ZzdMQKvG5ro4H4teClqm0sTY=
+k8s.io/api v0.24.3/go.mod h1:elGR/XSZrS7z7cSZPzVWaycpJuGIw57j9b95/1PdJNI=
 k8s.io/apimachinery v0.24.2/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
+k8s.io/apimachinery v0.24.3 h1:hrFiNSA2cBZqllakVYyH/VyEh4B581bQRmqATJSeQTg=
+k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM=
 k8s.io/client-go v0.24.2 h1:CoXFSf8if+bLEbinDqN9ePIDGzcLtqhfd6jpfnwGOFA=
 k8s.io/client-go v0.24.2/go.mod h1:zg4Xaoo+umDsfCWr4fCnmLEtQXyCNXCvJuSsglNcV30=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
diff --git a/contrib/registry/zookeeper/go.mod b/contrib/registry/zookeeper/go.mod
index 33d5697f..e33c1d93 100644
--- a/contrib/registry/zookeeper/go.mod
+++ b/contrib/registry/zookeeper/go.mod
@@ -4,7 +4,7 @@ go 1.16
 
 require (
 	github.com/go-kratos/kratos/v2 v2.4.0
-	github.com/go-zookeeper/zk v1.0.2
+	github.com/go-zookeeper/zk v1.0.3
 	golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
 )
 
diff --git a/contrib/registry/zookeeper/go.sum b/contrib/registry/zookeeper/go.sum
index c2c80c1a..f5cce467 100644
--- a/contrib/registry/zookeeper/go.sum
+++ b/contrib/registry/zookeeper/go.sum
@@ -28,8 +28,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
 github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
-github.com/go-zookeeper/zk v1.0.2 h1:4mx0EYENAdX/B/rbunjlt5+4RTA/a9SMHBRuSKdGxPM=
-github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
+github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
+github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
 github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
diff --git a/contrib/registry/zookeeper/register.go b/contrib/registry/zookeeper/register.go
index 2aedc198..a9de43c8 100644
--- a/contrib/registry/zookeeper/register.go
+++ b/contrib/registry/zookeeper/register.go
@@ -3,6 +3,7 @@ package zookeeper
 import (
 	"context"
 	"path"
+	"time"
 
 	"github.com/go-zookeeper/zk"
 	"golang.org/x/sync/singleflight"
@@ -77,6 +78,7 @@ func (r *Registry) Register(ctx context.Context, service *registry.ServiceInstan
 	if err = r.ensureName(servicePath, data, zk.FlagEphemeral); err != nil {
 		return err
 	}
+	go r.reRegister(servicePath, data)
 	return nil
 }
 
@@ -150,3 +152,21 @@ func (r *Registry) ensureName(path string, data []byte, flags int32) error {
 	}
 	return nil
 }
+
+// reRegister re-register data node info when bad connection recovered
+func (r *Registry) reRegister(path string, data []byte) {
+	sessionID := r.conn.SessionID()
+	ticker := time.NewTicker(time.Second)
+	defer ticker.Stop()
+	for range ticker.C {
+		cur := r.conn.SessionID()
+		// sessionID changed
+		if cur > 0 && sessionID != cur {
+			// re-ensureName
+			if err := r.ensureName(path, data, zk.FlagEphemeral); err != nil {
+				return
+			}
+			sessionID = cur
+		}
+	}
+}
diff --git a/transport/grpc/codec.go b/transport/grpc/codec.go
index 1b2c8289..19670bba 100644
--- a/transport/grpc/codec.go
+++ b/transport/grpc/codec.go
@@ -3,6 +3,7 @@ package grpc
 import (
 	"fmt"
 
+	enc "github.com/go-kratos/kratos/v2/encoding"
 	"github.com/go-kratos/kratos/v2/encoding/json"
 	"google.golang.org/grpc/encoding"
 	"google.golang.org/protobuf/proto"
@@ -20,7 +21,7 @@ func (codec) Marshal(v interface{}) ([]byte, error) {
 	if !ok {
 		return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
 	}
-	return json.MarshalOptions.Marshal(vv)
+	return enc.GetCodec(json.Name).Marshal(vv)
 }
 
 func (codec) Unmarshal(data []byte, v interface{}) error {
@@ -28,9 +29,9 @@ func (codec) Unmarshal(data []byte, v interface{}) error {
 	if !ok {
 		return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
 	}
-	return json.UnmarshalOptions.Unmarshal(data, vv)
+	return enc.GetCodec(json.Name).Unmarshal(data, vv)
 }
 
 func (codec) Name() string {
-	return "json"
+	return json.Name
 }


Test patch:
diff --git a/app_test.go b/app_test.go
index 80831b54..aa0a5ef7 100644
--- a/app_test.go
+++ b/app_test.go
@@ -3,6 +3,7 @@ package kratos
 import (
 	"context"
 	"fmt"
+	"net/url"
 	"reflect"
 	"sync"
 	"testing"
@@ -92,6 +93,80 @@ func TestApp_Metadata(t *testing.T) {
 }
 
 func TestApp_Endpoint(t *testing.T) {
+	v := []string{"https://go-kratos.dev", "localhost"}
+	var endpoints []*url.URL
+	for _, urlStr := range v {
+		if endpoint, err := url.Parse(urlStr); err != nil {
+			t.Errorf("invalid endpoint:%v", urlStr)
+		} else {
+			endpoints = append(endpoints, endpoint)
+		}
+	}
+	o := New(Endpoint(endpoints...))
+	if instance, err := o.buildInstance(); err != nil {
+		t.Error("build instance failed")
+	} else {
+		o.instance = instance
+	}
+	if !reflect.DeepEqual(o.Endpoint(), v) {
+		t.Errorf("Endpoint() = %v, want %v", o.Endpoint(), v)
+	}
+}
+
+func TestApp_buildInstance(t *testing.T) {
+	want := struct {
+		id        string
+		name      string
+		version   string
+		metadata  map[string]string
+		endpoints []string
+	}{
+		id:      "1",
+		name:    "kratos",
+		version: "v1.0.0",
+		metadata: map[string]string{
+			"a": "1",
+			"b": "2",
+		},
+		endpoints: []string{"https://go-kratos.dev", "localhost"},
+	}
+	var endpoints []*url.URL
+	for _, urlStr := range want.endpoints {
+		if endpoint, err := url.Parse(urlStr); err != nil {
+			t.Errorf("invalid endpoint:%v", urlStr)
+		} else {
+			endpoints = append(endpoints, endpoint)
+		}
+	}
+	app := New(
+		ID(want.id),
+		Name(want.name),
+		Version(want.version),
+		Metadata(want.metadata),
+		Endpoint(endpoints...),
+	)
+	if got, err := app.buildInstance(); err != nil {
+		t.Error("build got failed")
+	} else {
+		if got.ID != want.id {
+			t.Errorf("ID() = %v, want %v", got.ID, want.id)
+		}
+		if got.Name != want.name {
+			t.Errorf("Name() = %v, want %v", got.Name, want.name)
+		}
+		if got.Version != want.version {
+			t.Errorf("Version() = %v, want %v", got.Version, want.version)
+		}
+		if !reflect.DeepEqual(got.Endpoints, want.endpoints) {
+			t.Errorf("Endpoint() = %v, want %v", got.Endpoints, want.endpoints)
+		}
+		if !reflect.DeepEqual(got.Metadata, want.metadata) {
+			t.Errorf("Metadata() = %v, want %v", got.Metadata, want.metadata)
+		}
+	}
+}
+
+func TestApp_Context(t *testing.T) {
 	type fields struct {
 		id       string
 		version  string
diff --git a/cmd/kratos/internal/proto/server/server_test.go b/cmd/kratos/internal/proto/server/server_test.go
index a2cbeee6..fa90f692 100644
--- a/cmd/kratos/internal/proto/server/server_test.go
+++ b/cmd/kratos/internal/proto/server/server_test.go
@@ -2,7 +2,7 @@ package server
 
 import "testing"
 
-func Test_ucFirst(t *testing.T) {
+func Test_serviceName(t *testing.T) {
 	type args struct {
 		str string
 	}
@@ -12,40 +12,50 @@ func Test_ucFirst(t *testing.T) {
 		want string
 	}{
 		{
-			name: "ucFirst on lowercase words",
+			name: "serviceName on lowercase words",
 			args: args{str: "helloworld"},
 			want: "Helloworld",
 		},
 		{
-			name: "ucFirst on uppercase words",
+			name: "serviceName on uppercase words",
 			args: args{str: "HELLOWORLD"},
 			want: "HELLOWORLD",
 		},
 		{
-			name: "ucFirst on lowercase words with spaces",
+			name: "serviceName on lowercase words with spaces",
 			args: args{str: "hello world"},
-			want: "Hello world",
+			want: "HelloWorld",
 		},
 		{
-			name: "ucFirst on uppercase words with spaces",
+			name: "serviceName on uppercase words with spaces",
 			args: args{str: "HELLO WORLD"},
-			want: "HELLO WORLD",
+			want: "HELLOWORLD",
 		},
 		{
-			name: "ucFirst on Lower Camel Case words",
+			name: "serviceName on Lower Camel Case words",
 			args: args{str: "helloWorld"},
 			want: "HelloWorld",
 		},
 		{
-			name: "ucFirst on Upper Camel Case words",
+			name: "serviceName on Lower Camel Case words",
+			args: args{str: "helloWorld"},
+			want: "HelloWorld",
+		},
+		{
+			name: "serviceName on Upper Camel Case words",
 			args: args{str: "HelloWorld"},
 			want: "HelloWorld",
 		},
+		{
+			name: "serviceName on Upper Camel Case words",
+			args: args{str: "hello_world"},
+			want: "HelloWorld",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			if got := ucFirst(tt.args.str); got != tt.want {
-				t.Errorf("ucFirst() = %v, want %v", got, tt.want)
+			if got := serviceName(tt.args.str); got != tt.want {
+				t.Errorf("serviceName() = %v, want %v", got, tt.want)
 			}
 		})
 	}
diff --git a/config/env/env_test.go b/config/env/env_test.go
index b024dbee..6574c1d8 100644
--- a/config/env/env_test.go
+++ b/config/env/env_test.go
@@ -417,3 +417,13 @@ func Test_matchPrefix(t *testing.T) {
 		})
 	}
 }
+
+func Test_env_watch(t *testing.T) {
+	prefixs := []string{"BAR", "FOO"}
+	source := NewSource(prefixs...)
+	w, err := source.Watch()
+	if err != nil {
+		t.Errorf("expect no err, got %v", err)
+	}
+	_ = w.Stop()
+}
diff --git a/config/options_test.go b/config/options_test.go
index 3c46bc30..51e8b48e 100644
--- a/config/options_test.go
+++ b/config/options_test.go
@@ -2,6 +2,7 @@ package config
 
 import (
 	"reflect"
+	"strings"
 	"testing"
 )
 
@@ -197,3 +198,31 @@ func TestDefaultResolver(t *testing.T) {
 		})
 	}
 }
+
+func TestExpand(t *testing.T) {
+	tests := []struct {
+		input   string
+		mapping func(string) string
+		want    string
+	}{
+		{
+			input: "${a}",
+			mapping: func(s string) string {
+				return strings.ToUpper(s)
+			},
+			want: "A",
+		},
+		{
+			input: "a",
+			mapping: func(s string) string {
+				return strings.ToUpper(s)
+			},
+			want: "a",
+		},
+	}
+	for _, tt := range tests {
+		if got := expand(tt.input, tt.mapping); got != tt.want {
+			t.Errorf("expand() want: %s, got: %s", tt.want, got)
+		}
+	}
+}
diff --git a/config/reader_test.go b/config/reader_test.go
index b8a5d23a..f2ac1444 100644
--- a/config/reader_test.go
+++ b/config/reader_test.go
@@ -208,3 +208,118 @@ func TestReader_Source(t *testing.T) {
 		t.Fatal("[]byte(`{\"a\":{\"b\":{\"X\":1}}}`) is not equal to b")
 	}
 }
+
+func TestCloneMap(t *testing.T) {
+	tests := []struct {
+		input map[string]interface{}
+		want  map[string]interface{}
+	}{
+		{
+			input: map[string]interface{}{
+				"a": 1,
+				"b": "2",
+				"c": true,
+			},
+			want: map[string]interface{}{
+				"a": 1,
+				"b": "2",
+				"c": true,
+			},
+		},
+		{
+			input: map[string]interface{}{},
+			want:  map[string]interface{}{},
+		},
+		{
+			input: nil,
+			want:  map[string]interface{}{},
+		},
+	}
+	for _, tt := range tests {
+		if got, err := cloneMap(tt.input); err != nil {
+			t.Errorf("expect no err, got %v", err)
+		} else if !reflect.DeepEqual(got, tt.want) {
+			t.Errorf("cloneMap(%v) = %v, want %v", tt.input, got, tt.want)
+		}
+	}
+}
+
+func TestConvertMap(t *testing.T) {
+	tests := []struct {
+		input interface{}
+		want  interface{}
+	}{
+		{
+			input: map[string]interface{}{
+				"a": 1,
+				"b": "2",
+				"c": true,
+				"d": []byte{65, 66, 67},
+			},
+			want: map[string]interface{}{
+				"a": 1,
+				"b": "2",
+				"c": true,
+				"d": "ABC",
+			},
+		},
+		{
+			input: []interface{}{1, 2.0, "3", true, nil, []interface{}{1, 2.0, "3", true, nil}},
+			want:  []interface{}{1, 2.0, "3", true, nil, []interface{}{1, 2.0, "3", true, nil}},
+		},
+		{
+			input: []byte{65, 66, 67},
+			want:  "ABC",
+		},
+	}
+	for _, tt := range tests {
+		if got := convertMap(tt.input); !reflect.DeepEqual(got, tt.want) {
+			t.Errorf("convertMap(%v) = %v, want %v", tt.input, got, tt.want)
+		}
+	}
+}
+
+func TestReadValue(t *testing.T) {
+	m := map[string]interface{}{
+		"a": 1,
+		"b": map[string]interface{}{
+			"c": "3",
+			"d": map[string]interface{}{
+				"e": true,
+			},
+		},
+	}
+	va := atomicValue{}
+	va.Store(1)
+
+	vbc := atomicValue{}
+	vbc.Store("3")
+
+	vbde := atomicValue{}
+	vbde.Store(true)
+
+	tests := []struct {
+		path string
+		want atomicValue
+	}{
+		{
+			path: "a",
+			want: va,
+		},
+		{
+			path: "b.c",
+			want: vbc,
+		},
+		{
+			path: "b.d.e",
+			want: vbde,
+		},
+	}
+	for _, tt := range tests {
+		if got, found := readValue(m, tt.path); !found {
+			t.Errorf("expect found %v in %v, but not.", tt.path, m)
+		} else if got.Load() != tt.want.Load() {
+			t.Errorf("readValue(%v, %v) = %v, want %v", m, tt.path, got, tt.want)
+		}
+	}
+}
diff --git a/contrib/config/polaris/config_test.go b/contrib/config/polaris/config_test.go
index 6dfaeb3f..fc864526 100644
--- a/contrib/config/polaris/config_test.go
+++ b/contrib/config/polaris/config_test.go
@@ -213,7 +213,7 @@ func TestConfig(t *testing.T) {
 	if err != nil {
 		t.Fatal(err)
 	}
-	config, err := New(&configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
+	config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -285,7 +285,7 @@ func TestExtToFormat(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	config, err := New(&configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
+	config, err := New(configAPI, WithNamespace(namespace), WithFileGroup(fileGroup), WithFileName(name))
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/contrib/log/logrus/logrus_test.go b/contrib/log/logrus/logrus_test.go
index 5a6e69ae..3dcd6e64 100644
--- a/contrib/log/logrus/logrus_test.go
+++ b/contrib/log/logrus/logrus_test.go
@@ -32,6 +32,13 @@ func TestLoggerLog(t *testing.T) {
 			kvs:       []interface{}{"case", "level unmatch", "msg", "1"},
 			want:      "",
 		},
+		"fatal level": {
+			level:     logrus.InfoLevel,
+			formatter: &logrus.JSONFormatter{},
+			logLevel:  log.LevelFatal,
+			kvs:       []interface{}{"case", "json format", "msg", "1"},
+			want:      `{"case":"json format","level":"fatal","msg":"1"`,
+		},
 		"no tags": {
 			level:     logrus.InfoLevel,
 			formatter: &logrus.JSONFormatter{},
diff --git a/contrib/registry/polaris/registry_test.go b/contrib/registry/polaris/registry_test.go
index d0564051..7d696064 100644
--- a/contrib/registry/polaris/registry_test.go
+++ b/contrib/registry/polaris/registry_test.go
@@ -5,20 +5,26 @@ import (
 	"testing"
 	"time"
 
-	"github.com/polarismesh/polaris-go/pkg/config"
-
-	"github.com/go-kratos/kratos/v2/log"
 	"github.com/go-kratos/kratos/v2/registry"
+
+	"github.com/polarismesh/polaris-go/pkg/config"
 )
 
-// TestRegistry . TestRegistryManyService
+// TestRegistry
 func TestRegistry(t *testing.T) {
 	conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
 
 	r := NewRegistryWithConfig(
 		conf,
-		WithTimeout(time.Second*10),
-		WithTTL(100),
+		WithTimeout(time.Second),
+		WithHeartbeat(true),
+		WithHealthy(true),
+		WithIsolate(true),
+		WithNamespace("default"),
+		WithProtocol("tcp"),
+		WithRetryCount(0),
+		WithWeight(100),
+		WithTTL(10),
 	)
 
 	ctx := context.Background()
@@ -35,171 +41,197 @@ func TestRegistry(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	err = r.Deregister(ctx, svc)
+	time.Sleep(time.Second)
+
+	result, err := r.GetService(context.Background(), "kratos-provider-0-tcp")
 	if err != nil {
 		t.Fatal(err)
 	}
-}
 
-// TestRegistryMany . TestRegistryManyService
-func TestRegistryMany(t *testing.T) {
-	conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
+	if len(result) != 1 {
+		t.Fatal("register error")
+	}
 
-	r := NewRegistryWithConfig(
-		conf,
-		WithTimeout(time.Second*10),
-		WithTTL(100),
-	)
+	for _, item := range result {
+		if item.Name != "kratos-provider-0-tcp" || item.Endpoints[0] != "tcp://127.0.0.1:9000" {
+			t.Fatal("register error")
+		}
+	}
 
-	svc := &registry.ServiceInstance{
-		Name:      "kratos-provider-1-",
-		Version:   "test",
-		Metadata:  map[string]string{"app": "kratos"},
-		Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"},
+	watch, err := r.Watch(ctx, "kratos-provider-0-tcp")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Test update
+	svc.Version = "release1.0.0"
+
+	if err = r.Register(ctx, svc); err != nil {
+		t.Fatal(err)
 	}
+
+	result, err = watch.Next()
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(result) != 1 || result[0].Version != "release1.0.0" {
+		t.Fatal("register error")
+	}
+	// Test add instance
 	svc1 := &registry.ServiceInstance{
-		Name:      "kratos-provider-2-",
+		Name:      "kratos-provider-0-",
 		Version:   "test",
 		Metadata:  map[string]string{"app": "kratos"},
 		Endpoints: []string{"tcp://127.0.0.1:9001?isSecure=false"},
 	}
-	svc2 := &registry.ServiceInstance{
-		Name:      "kratos-provider-3-",
-		Version:   "test",
-		Metadata:  map[string]string{"app": "kratos"},
-		Endpoints: []string{"tcp://127.0.0.1:9002?isSecure=false"},
-	}
 
-	err := r.Register(context.Background(), svc)
-	if err != nil {
+	if err = r.Register(ctx, svc1); err != nil {
 		t.Fatal(err)
 	}
 
-	err = r.Register(context.Background(), svc1)
-	if err != nil {
+	if _, err = watch.Next(); err != nil {
 		t.Fatal(err)
 	}
 
-	err = r.Register(context.Background(), svc2)
+	result, err = r.GetService(ctx, "kratos-provider-0-tcp")
+
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	err = r.Deregister(context.Background(), svc)
-	if err != nil {
-		t.Fatal(err)
+	if len(result) != 2 {
+		t.Fatal("register error")
 	}
 
-	err = r.Deregister(context.Background(), svc1)
-	if err != nil {
+	if err = r.Deregister(ctx, svc); err != nil {
+		t.Fatal(err)
+	}
+	if err = r.Deregister(ctx, svc1); err != nil {
 		t.Fatal(err)
 	}
 
-	err = r.Deregister(context.Background(), svc2)
+	result, err = watch.Next()
 	if err != nil {
 		t.Fatal(err)
 	}
+
+	if len(result) != 0 {
+		t.Fatal("register error")
+	}
 }
 
-// TestGetService . TestGetService
-func TestGetService(t *testing.T) {
+// TestRegistryMany
+func TestRegistryMany(t *testing.T) {
 	conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
 
 	r := NewRegistryWithConfig(
 		conf,
-		WithTimeout(time.Second*10),
-		WithTTL(100),
+		WithTimeout(time.Second),
+		WithHeartbeat(true),
+		WithHealthy(true),
+		WithIsolate(true),
+		WithNamespace("default"),
+		WithProtocol("tcp"),
+		WithRetryCount(0),
+		WithWeight(100),
+		WithTTL(10),
 	)
 
 	ctx := context.Background()
 
+	// Multi endpoint
 	svc := &registry.ServiceInstance{
-		Name:      "kratos-provider-4-",
+		Name:      "kratos-provider-1-",
 		Version:   "test",
 		Metadata:  map[string]string{"app": "kratos"},
-		Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"},
+		Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false", "tcp://127.0.0.1:9001?isSecure=false"},
+	}
+	// Normal
+	svc1 := &registry.ServiceInstance{
+		Name:      "kratos-provider-2-",
+		Version:   "test",
+		Metadata:  map[string]string{"app": "kratos"},
+		Endpoints: []string{"tcp://127.0.0.1:9002?isSecure=false"},
+	}
+	// Without metadata
+	svc2 := &registry.ServiceInstance{
+		Name:      "kratos-provider-3-",
+		Version:   "test",
+		Endpoints: []string{"tcp://127.0.0.1:9003?isSecure=false"},
 	}
 
-	err := r.Register(ctx, svc)
-	if err != nil {
+	if err := r.Register(ctx, svc); err != nil {
 		t.Fatal(err)
 	}
-	time.Sleep(time.Second * 1)
-	serviceInstances, err := r.GetService(ctx, "kratos-provider-4-tcp")
-	if err != nil {
+
+	if err := r.Register(ctx, svc1); err != nil {
 		t.Fatal(err)
 	}
-	for _, instance := range serviceInstances {
-		log.Info(instance)
-	}
 
-	err = r.Deregister(ctx, svc)
-	if err != nil {
+	if err := r.Register(ctx, svc2); err != nil {
 		t.Fatal(err)
 	}
-}
 
-// TestWatch . TestWatch
-func TestWatch(t *testing.T) {
-	conf := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
+	time.Sleep(3 * time.Second)
 
-	r := NewRegistryWithConfig(
-		conf,
-		WithTimeout(time.Second*10),
-		WithTTL(100),
-	)
+	result1, err := r.GetService(ctx, "kratos-provider-1-tcp")
 
-	svc := &registry.ServiceInstance{
-		Name:      "kratos-provider-4-",
-		Version:   "test",
-		Metadata:  map[string]string{"app": "kratos"},
-		Endpoints: []string{"tcp://127.0.0.1:9000?isSecure=false"},
+	if err != nil || len(result1) != 2 || result1[0].Name != "kratos-provider-1-tcp" {
+		t.Fatal(err)
 	}
 
-	watch, err := r.Watch(context.Background(), "kratos-provider-4-tcp")
-	if err != nil {
+	result2, err := r.GetService(ctx, "kratos-provider-2-tcp")
+
+	if err != nil || len(result2) != 1 || result2[0].Name != "kratos-provider-2-tcp" || result2[0].Endpoints[0] != "tcp://127.0.0.1:9002" {
 		t.Fatal(err)
 	}
 
-	err = r.Register(context.Background(), svc)
-	if err != nil {
+	result3, err := r.GetService(ctx, "kratos-provider-3-tcp")
+
+	if err != nil || len(result3) != 1 || result3[0].Name != "kratos-provider-3-tcp" || result3[0].Endpoints[0] != "tcp://127.0.0.1:9003" {
 		t.Fatal(err)
 	}
-	// watch svc
-	time.Sleep(time.Second * 1)
 
-	// svc register, AddEvent
-	next, err := watch.Next()
+	watch1, err := r.Watch(ctx, "kratos-provider-1-tcp")
 	if err != nil {
 		t.Fatal(err)
 	}
-	for _, instance := range next {
-		// it will output one instance
-		log.Info(instance)
+	watch2, err := r.Watch(ctx, "kratos-provider-2-tcp")
+	if err != nil {
+		t.Fatal(err)
 	}
-
-	err = r.Deregister(context.Background(), svc)
+	watch3, err := r.Watch(ctx, "kratos-provider-3-tcp")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	// svc deregister, DeleteEvent
-	next, err = watch.Next()
-	if err != nil {
+	if err = r.Deregister(ctx, svc); err != nil {
 		t.Fatal(err)
 	}
-	for _, instance := range next {
-		// it will output nothing
-		log.Info(instance)
+
+	result1, err = watch1.Next()
+	if err != nil || len(result1) != 0 {
+		t.Fatal("deregister error")
 	}
 
-	err = watch.Stop()
+	err = r.Deregister(ctx, svc1)
 	if err != nil {
 		t.Fatal(err)
 	}
-	_, err = watch.Next()
-	if err == nil {
-		// if nil, stop failed
-		t.Fatal()
+
+	result2, err = watch2.Next()
+	if err != nil || len(result2) != 0 {
+		t.Fatal("deregister error")
+	}
+	err = r.Deregister(ctx, svc2)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	result3, err = watch3.Next()
+	if err != nil || len(result3) != 0 {
+		t.Fatal("deregister error")
 	}
 }
diff --git a/internal/endpoint/endpoint_test.go b/internal/endpoint/endpoint_test.go
index 356d8fbc..e8870c80 100644
--- a/internal/endpoint/endpoint_test.go
+++ b/internal/endpoint/endpoint_test.go
@@ -139,3 +139,87 @@ func TestParseEndpoint(t *testing.T) {
 		})
 	}
 }
+
+func TestIsSecure(t *testing.T) {
+	tests := []struct {
+		url  *url.URL
+		want bool
+	}{
+		{
+			url:  &url.URL{Scheme: "http", Host: "127.0.0.1"},
+			want: false,
+		},
+		{
+			url:  &url.URL{Scheme: "http", Host: "127.0.0.1", RawQuery: "isSecure=false"},
+			want: false,
+		},
+		{
+			url:  &url.URL{Scheme: "grpc", Host: "127.0.0.1", RawQuery: "isSecure=true"},
+			want: true,
+		},
+	}
+	for _, tt := range tests {
+		if got := IsSecure(tt.url); got != tt.want {
+			t.Errorf("IsSecure() = %v, want %v", got, tt.want)
+		}
+	}
+}
+
+func TestLegacyURLToNew(t *testing.T) {
+	tests := []struct {
+		url  *url.URL
+		want *url.URL
+	}{
+		{
+			url:  &url.URL{Scheme: "http", Host: "www.google.com", RawQuery: "isSecure=true"},
+			want: &url.URL{Scheme: "https", Host: "www.google.com"},
+		},
+		{
+			url:  &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"},
+			want: &url.URL{Scheme: "https", Host: "www.google.com", RawQuery: "isSecure=true"},
+		},
+		{
+			url:  &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"},
+			want: &url.URL{Scheme: "http", Host: "go-kratos.dev", RawQuery: "isSecure=false"},
+		},
+	}
+	for _, tt := range tests {
+		if got := legacyURLToNew(tt.url); !reflect.DeepEqual(got, tt.want) {
+			t.Errorf("legacyURLToNew() = %v, want %v", got, tt.want)
+		}
+	}
+}
+
+func TestSchema(t *testing.T) {
+	tests := []struct {
+		schema string
+		secure bool
+		want   string
+	}{
+		{
+			schema: "http",
+			secure: true,
+			want:   "https",
+		},
+		{
+			schema: "http",
+			secure: false,
+			want:   "http",
+		},
+		{
+			schema: "grpc",
+			secure: true,
+			want:   "grpcs",
+		},
+		{
+			schema: "grpc",
+			secure: false,
+			want:   "grpc",
+		},
+	}
+	for _, tt := range tests {
+		if got := Scheme(tt.schema, tt.secure); got != tt.want {
+			t.Errorf("Schema() = %v, want %v", got, tt.want)
+		}
+	}
+}
diff --git a/log/global_test.go b/log/global_test.go
index 91482331..04bb19c5 100644
--- a/log/global_test.go
+++ b/log/global_test.go
@@ -11,7 +11,12 @@ import (
 
 func TestGlobalLog(t *testing.T) {
 	buffer := &bytes.Buffer{}
-	SetLogger(NewStdLogger(buffer))
+	logger := NewStdLogger(buffer)
+	SetLogger(logger)
+
+	if GetLogger() != logger {
+		t.Error("GetLogger() is not equal to logger")
+	}
 
 	testCases := []struct {
 		level   Level
@@ -48,19 +53,38 @@ func TestGlobalLog(t *testing.T) {
 		msg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...)
 		switch tc.level {
 		case LevelDebug:
+			Debug(msg)
+			expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg))
 			Debugf(tc.content[0].(string), tc.content[1:]...)
 			expected = append(expected, fmt.Sprintf("%s msg=%s", "DEBUG", msg))
+			Debugw("log", msg)
+			expected = append(expected, fmt.Sprintf("%s log=%s", "DEBUG", msg))
 		case LevelInfo:
+			Info(msg)
+			expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg))
 			Infof(tc.content[0].(string), tc.content[1:]...)
 			expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", msg))
+			Infow("log", msg)
+			expected = append(expected, fmt.Sprintf("%s log=%s", "INFO", msg))
 		case LevelWarn:
+			Warn(msg)
+			expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg))
 			Warnf(tc.content[0].(string), tc.content[1:]...)
 			expected = append(expected, fmt.Sprintf("%s msg=%s", "WARN", msg))
+			Warnw("log", msg)
+			expected = append(expected, fmt.Sprintf("%s log=%s", "WARN", msg))
 		case LevelError:
+			Error(msg)
+			expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg))
 			Errorf(tc.content[0].(string), tc.content[1:]...)
 			expected = append(expected, fmt.Sprintf("%s msg=%s", "ERROR", msg))
+			Errorw("log", msg)
+			expected = append(expected, fmt.Sprintf("%s log=%s", "ERROR", msg))
 		}
 	}
+	Log(LevelInfo, DefaultMessageKey, "test log")
+	expected = append(expected, fmt.Sprintf("%s msg=%s", "INFO", "test log"))
+
 	expected = append(expected, "")
 
 	t.Logf("Content: %s", buffer.String())
@@ -85,7 +109,7 @@ func TestGlobalLogUpdate(t *testing.T) {
 	}
 }
 
-func TestGolbalContext(t *testing.T) {
+func TestGlobalContext(t *testing.T) {
 	buffer := &bytes.Buffer{}
 	SetLogger(NewStdLogger(buffer))
 	Context(context.Background()).Infof("111")
diff --git a/log/log_test.go b/log/log_test.go
index 542bb79d..38417f76 100644
--- a/log/log_test.go
+++ b/log/log_test.go
@@ -7,7 +7,8 @@ import (
 
 func TestInfo(t *testing.T) {
 	logger := DefaultLogger
-	logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller)
+	logger = With(logger, "ts", DefaultTimestamp)
+	logger = With(logger, "caller", DefaultCaller)
 	_ = logger.Log(LevelInfo, "key1", "value1")
 }