Repository: go-kratos/kratos
Pull request #886 (2021-04-29 11:46:19)

log: rename Print to Log
#882


Patch:
diff --git a/errors/errors.go b/errors/errors.go
index cec7fbf4..a9b3cb0e 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -59,7 +59,7 @@ func (e *Error) WithMetadata(md map[string]string) *Error {
 // New returns an error object for the code, message.
 func New(code codes.Code, domain, reason, message string) *Error {
 	return &Error{
-		s:      status.New(codes.Code(code), message),
+		s:      status.New(code, message),
 		Domain: domain,
 		Reason: reason,
 	}
@@ -73,7 +73,7 @@ func Newf(code codes.Code, domain, reason, format string, a ...interface{}) *Err
 // Errorf returns an error object for the code, message and error info.
 func Errorf(code codes.Code, domain, reason, format string, a ...interface{}) error {
 	return &Error{
-		s:      status.New(codes.Code(code), fmt.Sprintf(format, a...)),
+		s:      status.New(code, fmt.Sprintf(format, a...)),
 		Domain: domain,
 		Reason: reason,
 	}
diff --git a/examples/blog/cmd/blog/main.go b/examples/blog/cmd/blog/main.go
index 844e1704..62e6c720 100644
--- a/examples/blog/cmd/blog/main.go
+++ b/examples/blog/cmd/blog/main.go
@@ -1,16 +1,12 @@
 package main
 
 import (
+	"context"
 	"flag"
-	"go.opentelemetry.io/otel"
-	"go.opentelemetry.io/otel/attribute"
-	"go.opentelemetry.io/otel/sdk/resource"
-	"go.opentelemetry.io/otel/semconv"
 	"os"
+	"time"
 
-	"go.opentelemetry.io/otel/exporters/trace/jaeger"
-	"go.opentelemetry.io/otel/sdk/trace"
-
+	"github.com/go-kratos/kratos/examples/blog/api/blog/v1"
 	"github.com/go-kratos/kratos/examples/blog/internal/conf"
 	"github.com/go-kratos/kratos/v2"
 	"github.com/go-kratos/kratos/v2/config"
@@ -18,6 +14,11 @@ import (
 	"github.com/go-kratos/kratos/v2/log"
 	"github.com/go-kratos/kratos/v2/transport/grpc"
 	"github.com/go-kratos/kratos/v2/transport/http"
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/exporters/trace/jaeger"
+	"go.opentelemetry.io/otel/sdk/resource"
+	tracesdk "go.opentelemetry.io/otel/sdk/trace"
+	"go.opentelemetry.io/otel/semconv"
 	"gopkg.in/yaml.v2"
 )
 
@@ -48,6 +49,25 @@ func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server) *kratos.App {
 	)
 }
 
+func tracerProvider(url string) (*tracesdk.TracerProvider, error) {
+	// Create the Jaeger exporter
+	exp, err := jaeger.NewRawExporter(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
+	if err != nil {
+		return nil, err
+	}
+	tp := tracesdk.NewTracerProvider(
+		// Always be sure to batch in production.
+		tracesdk.WithBatcher(exp),
+		// Record information about this application in an Resource.
+		tracesdk.WithResource(resource.NewWithAttributes(
+			semconv.ServiceNameKey.String(v1.BlogService_ServiceDesc.ServiceName),
+			attribute.String("environment", "development"),
+			attribute.Int64("ID", 1),
+		)),
+	)
+	return tp, nil
+}
+
 func main() {
 	flag.Parse()
 	logger := log.NewStdLogger(os.Stdout)
@@ -68,24 +88,22 @@ func main() {
 	if err := config.Scan(&bc); err != nil {
 		panic(err)
 	}
-
-	flush, err := jaeger.InstallNewPipeline(
-		jaeger.WithCollectorEndpoint("http://47.104.19.38:14268/api/traces"),
-		jaeger.WithSDKOptions(
-			trace.WithSampler(trace.AlwaysSample()),
-			trace.WithResource(resource.NewWithAttributes(
-				semconv.ServiceNameKey.String("trace-demo"),
-				attribute.String("exporter", "jaeger"),
-				attribute.Float64("float", 312.23),
-			)),
-		),
-	)
-
+	tp, err := tracerProvider("http://47.104.19.38:14268/api/traces")
 	if err != nil {
 		panic(err)
 	}
-	defer flush()
-	tp := otel.GetTracerProvider()
+
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	defer func(ctx context.Context) {
+		// Do not make the application hang when it is shutdown.
+		ctx, cancel = context.WithTimeout(ctx, time.Second*5)
+		defer cancel()
+		if err := tp.Shutdown(ctx); err != nil {
+			panic(err)
+		}
+	}(ctx)
+
 	app, cleanup, err := initApp(bc.Server, bc.Data, tp, logger)
 	if err != nil {
 		panic(err)
diff --git a/examples/blog/internal/service/article.go b/examples/blog/internal/service/article.go
index d213ff4b..2b8ba828 100644
--- a/examples/blog/internal/service/article.go
+++ b/examples/blog/internal/service/article.go
@@ -43,7 +43,7 @@ func (s *BlogService) DeleteArticle(ctx context.Context, req *pb.DeleteArticleRe
 
 func (s *BlogService) GetArticle(ctx context.Context, req *pb.GetArticleRequest) (*pb.GetArticleReply, error) {
 	tr := otel.Tracer("api")
-	_, span := tr.Start(ctx, "GetArticle")
+	ctx, span := tr.Start(ctx, "GetArticle")
 	defer span.End()
 	p, err := s.article.Get(ctx, req.Id)
 	if err != nil {
diff --git a/examples/go.mod b/examples/go.mod
index af4c820e..92e4c63f 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -25,10 +25,10 @@ require (
 	github.com/nacos-group/nacos-sdk-go v1.0.7
 	github.com/ugorji/go v1.2.3 // indirect
 	go.etcd.io/etcd/client/v3 v3.5.0-alpha.0
-	go.opentelemetry.io/otel v0.19.0
-	go.opentelemetry.io/otel/exporters/trace/jaeger v0.19.0
-	go.opentelemetry.io/otel/sdk v0.19.0
-	go.opentelemetry.io/otel/trace v0.19.0
+	go.opentelemetry.io/otel v0.20.0
+	go.opentelemetry.io/otel/exporters/trace/jaeger v0.20.0
+	go.opentelemetry.io/otel/sdk v0.20.0
+	go.opentelemetry.io/otel/trace v0.20.0
 	golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
 	golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
diff --git a/examples/go.sum b/examples/go.sum
index 2776f91a..a0cec4f2 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -489,6 +489,7 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48=
 go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
 go.opentelemetry.io/otel v0.16.0/go.mod h1:e4GKElweB8W2gWUqbghw0B8t5MCTccc9212eNHnOHwA=
 go.opentelemetry.io/otel v0.17.0/go.mod h1:Oqtdxmf7UtEvL037ohlgnaYa1h7GtMh0NcSd9eqkC9s=
@@ -496,29 +497,40 @@ go.opentelemetry.io/otel v0.18.0 h1:d5Of7+Zw4ANFOJB+TIn2K3QWsgS2Ht7OU9DqZHI6qu8=
 go.opentelemetry.io/otel v0.18.0/go.mod h1:PT5zQj4lTsR1YeARt8YNKcFb88/c2IKoSABK9mX0r78=
 go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
 go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
+go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
+go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
 go.opentelemetry.io/otel/exporters/trace/jaeger v0.18.0 h1:ypDeiBo+6UnLiuERzboaBG2K+TKkocr02CZ6A+HBWdg=
 go.opentelemetry.io/otel/exporters/trace/jaeger v0.18.0/go.mod h1:4CNKanPlRgD52TO4OQ8bgSsh2ks8wLLDzykLQQNju6E=
 go.opentelemetry.io/otel/exporters/trace/jaeger v0.19.0 h1:qU7sGQoDrlAvNYryR2SJT4ULP/q+5tE38W+h31WEE/M=
 go.opentelemetry.io/otel/exporters/trace/jaeger v0.19.0/go.mod h1:BliRm9d7rH44N6CzBQ0OPEPfMqSzf4WvFFvyoocOW9Y=
+go.opentelemetry.io/otel/exporters/trace/jaeger v0.20.0 h1:FoclOadJNul1vUiKnZU0sKFWOZtZQq3jUzSbrX2jwNM=
+go.opentelemetry.io/otel/exporters/trace/jaeger v0.20.0/go.mod h1:10qwvAmKpvwRO5lL3KQ8EWznPp89uGfhcbK152LFWsQ=
 go.opentelemetry.io/otel/metric v0.17.0/go.mod h1:hUz9lH1rNXyEwWAhIWCMFWKhYtpASgSnObJFnU26dJ0=
 go.opentelemetry.io/otel/metric v0.18.0 h1:yuZCmY9e1ZTaMlZXLrrbAPmYW6tW1A5ozOZeOYGaTaY=
 go.opentelemetry.io/otel/metric v0.18.0/go.mod h1:kEH2QtzAyBy3xDVQfGZKIcok4ZZFvd5xyKPfPcuK6pE=
 go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
 go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
+go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
+go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
 go.opentelemetry.io/otel/oteltest v0.17.0/go.mod h1:JT/LGFxPwpN+nlsTiinSYjdIx3hZIGqHCpChcIZmdoE=
 go.opentelemetry.io/otel/oteltest v0.18.0 h1:FbKDFm/LnQDOHuGjED+fy3s5YMVg0z019GJ9Er66hYo=
 go.opentelemetry.io/otel/oteltest v0.18.0/go.mod h1:NyierCU3/G8DLTva7KRzGii2fdxdR89zXKH1bNWY7Bo=
 go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
 go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
+go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
 go.opentelemetry.io/otel/sdk v0.18.0 h1:/UiFHiJxJyEoUN2tQ6l+5f0/P01V0G9YuHeVarktRDw=
 go.opentelemetry.io/otel/sdk v0.18.0/go.mod h1:nT+UdAeGQWSeTnz9vY8BBq7SEGpmWAetyo/xHUcQvxo=
 go.opentelemetry.io/otel/sdk v0.19.0 h1:13pQquZyGbIvGxBWcVzUqe8kg5VGbTBiKKKXpYCylRM=
 go.opentelemetry.io/otel/sdk v0.19.0/go.mod h1:ouO7auJYMivDjywCHA6bqTI7jJMVQV1HdKR5CmH8DGo=
+go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
+go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
 go.opentelemetry.io/otel/trace v0.17.0/go.mod h1:bIujpqg6ZL6xUTubIUgziI1jSaUPthmabA/ygf/6Cfg=
 go.opentelemetry.io/otel/trace v0.18.0 h1:ilCfc/fptVKaDMK1vWk0elxpolurJbEgey9J6g6s+wk=
 go.opentelemetry.io/otel/trace v0.18.0/go.mod h1:FzdUu3BPwZSZebfQ1vl5/tAa8LyMLXSJN57AXIt/iDk=
 go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
 go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
+go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
+go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -706,6 +718,7 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq
 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=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
diff --git a/go.sum b/go.sum
index 8d830a1b..519cce16 100644
--- a/go.sum
+++ b/go.sum
@@ -44,14 +44,26 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+go.opentelemetry.io v0.1.0 h1:EANZoRCOP+A3faIlw/iN6YEWoYb1vleZRKm1EvH8T48=
 go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
 go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
+go.opentelemetry.io/otel v0.20.0 h1:eaP0Fqu7SXHwvjiqDq83zImeehOHX8doTvU9AwXON8g=
+go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
+go.opentelemetry.io/otel/exporters/trace/jaeger v0.20.0 h1:FoclOadJNul1vUiKnZU0sKFWOZtZQq3jUzSbrX2jwNM=
+go.opentelemetry.io/otel/exporters/trace/jaeger v0.20.0/go.mod h1:10qwvAmKpvwRO5lL3KQ8EWznPp89uGfhcbK152LFWsQ=
 go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
 go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
+go.opentelemetry.io/otel/metric v0.20.0 h1:4kzhXFP+btKm4jwxpjIqjs41A7MakRFUS86bqLHTIw8=
+go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
 go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
 go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
+go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
+go.opentelemetry.io/otel/sdk v0.20.0 h1:JsxtGXd06J8jrnya7fdI/U/MR6yXA5DtbZy+qoHQlr8=
+go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
 go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
 go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
+go.opentelemetry.io/otel/trace v0.20.0 h1:1DL6EXUdcg95gukhuRRvLDO/4X5THh/5dIV52lqtnbw=
+go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
diff --git a/log/README.md b/log/README.md
index eefa9b78..4dba3be8 100644
--- a/log/README.md
+++ b/log/README.md
@@ -4,14 +4,42 @@
 
 ### Structured logging
 
-```
-logger := log.NewLogger(os.Stdout)
-logger = With(logger, "key", "value")
+```go
+logger := log.NewStdLogger(os.Stdout)
+logger = log.With(logger, "caller", log.DefaultCaller, "ts", log.DefaultTimestamp)
 
-log := log.NewHelper("github.com/project/foo", logger)
 // Levels
-log.Info("hello")
-log.Infof("hello %s", "kratos")
-log.Infow("key", "value")
+log.Debug(logger).Log("msg", "foo bar")
+log.Info(logger).Log("msg", "foo bar")
+log.Warn(logger).Log("msg", "foo bar")
+log.Error(logger).Log("msg", "foo bar")
+
+errLogger := log.Error(logger)
+errLogger.Log("msg", "xxx")
+errLogger.Log("msg", "yyy")
+errLogger.Log("msg", "zzz")
+
+errLogger.Log(
+    "http.scheme", "https",
+    "http.host", "translate.googleapis.com",
+    "http.target", "/language/translate",
+    "http.method", "post",
+    "http.status_code", 500,
+    "http.flavor", "1.1.",
+    "http.user_agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
+)
+
+// Helper
+logger := log.NewHelper("github.com/project/foo", log.DefaultLogger)
+logger.Info("hello")
+logger.Infof("foo %s", "bar")
+logger.Infow("key", "value")
+
+// Verbose
+v := NewVerbose(log.DefaultLogger, 20)
+
+v.V(10).Log("foo", "bar1")
+v.V(20).Log("foo", "bar2")
+v.V(30).Log("foo", "bar3")
 ```
 
diff --git a/log/helper.go b/log/helper.go
index ee7e867b..f2a7ad3d 100644
--- a/log/helper.go
+++ b/log/helper.go
@@ -14,71 +14,71 @@ type Helper struct {
 
 // NewHelper new a logger helper.
 func NewHelper(name string, logger Logger) *Helper {
-	log := With(logger, "module", name)
+	logger = With(logger, "module", name)
 	return &Helper{
-		debug: Debug(log),
-		info:  Info(log),
-		warn:  Warn(log),
-		err:   Error(log),
+		debug: Debug(logger),
+		info:  Info(logger),
+		warn:  Warn(logger),
+		err:   Error(logger),
 	}
 }
 
 // Debug logs a message at debug level.
 func (h *Helper) Debug(a ...interface{}) {
-	h.debug.Print("message", fmt.Sprint(a...))
+	h.debug.Log("msg", fmt.Sprint(a...))
 }
 
 // Debugf logs a message at debug level.
 func (h *Helper) Debugf(format string, a ...interface{}) {
-	h.debug.Print("message", fmt.Sprintf(format, a...))
+	h.debug.Log("msg", fmt.Sprintf(format, a...))
 }
 
 // Debugw logs a message at debug level.
-func (h *Helper) Debugw(pairs ...interface{}) {
-	h.debug.Print(pairs...)
+func (h *Helper) Debugw(kv ...interface{}) {
+	h.debug.Log(kv...)
 }
 
 // Info logs a message at info level.
 func (h *Helper) Info(a ...interface{}) {
-	h.info.Print("message", fmt.Sprint(a...))
+	h.info.Log("msg", fmt.Sprint(a...))
 }
 
 // Infof logs a message at info level.
 func (h *Helper) Infof(format string, a ...interface{}) {
-	h.info.Print("message", fmt.Sprintf(format, a...))
+	h.info.Log("msg", fmt.Sprintf(format, a...))
 }
 
 // Infow logs a message at info level.
-func (h *Helper) Infow(pairs ...interface{}) {
-	h.info.Print(pairs...)
+func (h *Helper) Infow(kv ...interface{}) {
+	h.info.Log(kv...)
 }
 
 // Warn logs a message at warn level.
 func (h *Helper) Warn(a ...interface{}) {
-	h.warn.Print("message", fmt.Sprint(a...))
+	h.warn.Log("msg", fmt.Sprint(a...))
 }
 
 // Warnf logs a message at warnf level.
 func (h *Helper) Warnf(format string, a ...interface{}) {
-	h.warn.Print("message", fmt.Sprintf(format, a...))
+	h.warn.Log("msg", fmt.Sprintf(format, a...))
 }
 
 // Warnw logs a message at warnf level.
-func (h *Helper) Warnw(pairs ...interface{}) {
-	h.warn.Print(pairs...)
+func (h *Helper) Warnw(kv ...interface{}) {
+	h.warn.Log(kv...)
 }
 
 // Error logs a message at error level.
 func (h *Helper) Error(a ...interface{}) {
-	h.err.Print("message", fmt.Sprint(a...))
+	h.err.Log("msg", fmt.Sprint(a...))
 }
 
 // Errorf logs a message at error level.
 func (h *Helper) Errorf(format string, a ...interface{}) {
-	h.err.Print("message", fmt.Sprintf(format, a...))
+	h.err.Log("msg", fmt.Sprintf(format, a...))
 }
 
 // Errorw logs a message at error level.
-func (h *Helper) Errorw(pairs ...interface{}) {
-	h.err.Print(pairs...)
+func (h *Helper) Errorw(kv ...interface{}) {
+	h.err.Log(kv...)
 }
diff --git a/log/level.go b/log/level.go
index 2082b77b..d76fc411 100644
--- a/log/level.go
+++ b/log/level.go
@@ -1,5 +1,7 @@
 package log
 
+import "strings"
+
 // Level is a logger level.
 type Level int8
 
@@ -31,3 +33,18 @@ func (l Level) String() string {
 		return ""
 	}
 }
+
+// ParseLevel parses a level string into a logger Level value.
+func ParseLevel(s string) Level {
+	switch strings.ToUpper(s) {
+	case "DEBUG":
+		return LevelDebug
+	case "INFO":
+		return LevelInfo
+	case "WARN":
+		return LevelWarn
+	case "ERROR":
+		return LevelError
+	}
+	return LevelInfo
+}
diff --git a/log/log.go b/log/log.go
index 1cebf5ab..79fec9c8 100644
--- a/log/log.go
+++ b/log/log.go
@@ -11,39 +11,47 @@ var (
 
 // Logger is a logger interface.
 type Logger interface {
-	Print(pairs ...interface{})
+	Log(kv ...interface{}) error
 }
 
 type context struct {
-	logs   []Logger
-	prefix []interface{}
+	logs      []Logger
+	prefix    []interface{}
+	hasValuer bool
 }
 
-func (c *context) Print(a ...interface{}) {
-	kvs := make([]interface{}, 0, len(c.prefix)+len(a))
+func (c *context) Log(kv ...interface{}) error {
+	kvs := make([]interface{}, 0, len(c.prefix)+len(kv))
 	kvs = append(kvs, c.prefix...)
-	kvs = append(kvs, a...)
-	for _, log := range c.logs {
-		log.Print(kvs...)
+	if c.hasValuer {
+		bindValues(kvs)
 	}
+	kvs = append(kvs, kv...)
+	for _, l := range c.logs {
+		if err := l.Log(kvs...); err != nil {
+			return err
+		}
+	}
+	return nil
 }
 
 // With with logger fields.
-func With(l Logger, a ...interface{}) Logger {
+func With(l Logger, kv ...interface{}) Logger {
 	if c, ok := l.(*context); ok {
-		kvs := make([]interface{}, 0, len(c.prefix)+len(a))
-		kvs = append(kvs, a...)
+		kvs := make([]interface{}, 0, len(c.prefix)+len(kv))
+		kvs = append(kvs, kv...)
 		kvs = append(kvs, c.prefix...)
 		return &context{
-			logs:   c.logs,
-			prefix: kvs,
+			logs:      c.logs,
+			prefix:    kvs,
+			hasValuer: containsValuer(kvs),
 		}
 	}
-	return &context{logs: []Logger{l}, prefix: a}
+	return &context{logs: []Logger{l}, prefix: kv, hasValuer: containsValuer(kv)}
 }
 
-// Wrap wraps multi logger.
-func Wrap(logs ...Logger) Logger {
+// MultiLogger wraps multi logger.
+func MultiLogger(logs ...Logger) Logger {
 	return &context{logs: logs}
 }
 
diff --git a/log/std.go b/log/std.go
index 38f2b071..2ecfdbf3 100644
--- a/log/std.go
+++ b/log/std.go
@@ -18,7 +18,7 @@ type stdLogger struct {
 // NewStdLogger new a std logger with options.
 func NewStdLogger(w io.Writer) Logger {
 	return &stdLogger{
-		log: log.New(w, "", log.LstdFlags),
+		log: log.New(w, "", 0),
 		pool: &sync.Pool{
 			New: func() interface{} {
 				return new(bytes.Buffer)
@@ -27,19 +27,20 @@ func NewStdLogger(w io.Writer) Logger {
 	}
 }
 
-// Print print the kv pairs log.
-func (l *stdLogger) Print(pairs ...interface{}) {
-	if len(pairs) == 0 {
-		return
+// Log print the kv pairs log.
+func (l *stdLogger) Log(kv ...interface{}) error {
+	if len(kv) == 0 {
+		return nil
 	}
-	if len(pairs)%2 != 0 {
-		pairs = append(pairs, "")
+	if len(kv)%2 != 0 {
+		kv = append(kv, "")
 	}
 	buf := l.pool.Get().(*bytes.Buffer)
-	for i := 0; i < len(pairs); i += 2 {
-		fmt.Fprintf(buf, "%s=%v ", pairs[i], Value(pairs[i+1]))
+	for i := 0; i < len(kv); i += 2 {
+		fmt.Fprintf(buf, "%s=%v ", kv[i], kv[i+1])
 	}
 	l.log.Output(4, buf.String())
 	buf.Reset()
 	l.pool.Put(buf)
+	return nil
 }
diff --git a/log/value.go b/log/value.go
index b43f6347..311d6843 100644
--- a/log/value.go
+++ b/log/value.go
@@ -4,6 +4,15 @@ import (
 	"runtime"
 	"strconv"
 	"strings"
+	"time"
+)
+
+var (
+	// DefaultCaller is a Valuer that returns the file and line.
+	DefaultCaller = Caller(3)
+
+	// DefaultTimestamp is a Valuer that returns the current wallclock time.
+	DefaultTimestamp = Timestamp(time.RFC3339)
 )
 
 // Valuer is returns a log value.
@@ -20,11 +29,35 @@ func Value(v interface{}) interface{} {
 // Caller returns returns a Valuer that returns a pkg/file:line description of the caller.
 func Caller(depth int) Valuer {
 	return func() interface{} {
-		_, file, line, ok := runtime.Caller(depth)
-		if !ok {
-			return nil
+		_, file, line, _ := runtime.Caller(depth)
+		if strings.LastIndex(file, "github.com/go-kratos/kratos/log") > 0 {
+			_, file, line, _ = runtime.Caller(depth + 1)
 		}
 		idx := strings.LastIndexByte(file, '/')
 		return file[idx+1:] + ":" + strconv.Itoa(line)
 	}
 }
+
+// Timestamp returns a timestamp Valuer with a custom time format.
+func Timestamp(layout string) Valuer {
+	return func() interface{} {
+		return time.Now().Format(layout)
+	}
+}
+
+func bindValues(keyvals []interface{}) {
+	for i := 1; i < len(keyvals); i += 2 {
+		if v, ok := keyvals[i].(Valuer); ok {
+			keyvals[i] = v()
+		}
+	}
+}
+
+func containsValuer(keyvals []interface{}) bool {
+	for i := 1; i < len(keyvals); i += 2 {
+		if _, ok := keyvals[i].(Valuer); ok {
+			return true
+		}
+	}
+	return false
+}
diff --git a/log/verbose.go b/log/verbose.go
index f2f44e83..c4288418 100644
--- a/log/verbose.go
+++ b/log/verbose.go
@@ -22,9 +22,10 @@ func (v Verbose) V(level Level) Verbose {
 	return Verbose{log: v.log, enabled: v.Enabled(level)}
 }
 
-// Print is equivalent to the Print function, guarded by the value of v.
-func (v Verbose) Print(a ...interface{}) {
+// Log is equivalent to the Print function, guarded by the value of v.
+func (v Verbose) Log(a ...interface{}) error {
 	if v.enabled {
-		v.log.Print(a...)
+		return v.log.Log(a...)
 	}
+	return nil
 }


Test patch:
diff --git a/log/helper_test.go b/log/helper_test.go
index 4e12bb5e..40a832cb 100644
--- a/log/helper_test.go
+++ b/log/helper_test.go
@@ -6,7 +6,7 @@ import (
 )
 
 func TestHelper(t *testing.T) {
-	logger := With(DefaultLogger, "caller", Caller(5))
+	logger := With(DefaultLogger, "caller", DefaultCaller, "ts", DefaultTimestamp)
 	log := NewHelper("test", logger)
 
 	log.Debug("test debug")
diff --git a/log/log_test.go b/log/log_test.go
index 8885a861..5eefd919 100644
--- a/log/log_test.go
+++ b/log/log_test.go
@@ -7,16 +7,25 @@ import (
 
 func TestLogger(t *testing.T) {
 	logger := DefaultLogger
-	Debug(logger).Print("log", "test debug")
-	Info(logger).Print("log", "test info")
-	Warn(logger).Print("log", "test warn")
-	Error(logger).Print("log", "test error")
+	Debug(logger).Log("msg", "test debug")
+	Info(logger).Log("msg", "test info")
+	Warn(logger).Log("msg", "test warn")
+	Error(logger).Log("msg", "test error")
+}
+
+func TestInfo(t *testing.T) {
+	logger := DefaultLogger
+	logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
+	infoLogger := Info(logger)
+	infoLogger.Log("key1", "value1")
+	infoLogger.Log("key2", "value2")
+	infoLogger.Log("key3", "value3")
 }
 
 func TestWrapper(t *testing.T) {
 	out := NewStdLogger(os.Stdout)
 	err := NewStdLogger(os.Stderr)
 
-	l := Wrap(out, err)
-	l.Print("message", "test")
+	l := With(MultiLogger(out, err), "caller", DefaultCaller, "ts", DefaultTimestamp)
+	l.Log("msg", "test")
 }
diff --git a/log/std_test.go b/log/std_test.go
index e76259b5..1480a805 100644
--- a/log/std_test.go
+++ b/log/std_test.go
@@ -4,9 +4,10 @@ import "testing"
 
 func TestStdLogger(t *testing.T) {
 	logger := DefaultLogger
+	logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
 
-	Debug(logger).Print("log", "test debug")
-	Info(logger).Print("log", "test info")
-	Warn(logger).Print("log", "test warn")
-	Error(logger).Print("log", "test error")
+	Debug(logger).Log("msg", "test debug")
+	Info(logger).Log("msg", "test info")
+	Warn(logger).Log("msg", "test warn")
+	Error(logger).Log("msg", "test error")
 }
diff --git a/log/value_test.go b/log/value_test.go
index ed00bfce..c4114227 100644
--- a/log/value_test.go
+++ b/log/value_test.go
@@ -3,6 +3,12 @@ package log
 import "testing"
 
 func TestValue(t *testing.T) {
-	logger := With(DefaultLogger, "caller", Caller(4))
-	logger.Print("message", "helloworld")
+	logger := DefaultLogger
+	logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp)
+	logger.Log("msg", "helloworld")
+
+	Debug(logger).Log("msg", "debug value")
+	Info(logger).Log("msg", "info value")
+	Warn(logger).Log("msg", "warn value")
+	Error(logger).Log("msg", "error value")
 }
diff --git a/log/verbose_test.go b/log/verbose_test.go
index 06ac3815..146149e5 100644
--- a/log/verbose_test.go
+++ b/log/verbose_test.go
@@ -3,9 +3,10 @@ package log
 import "testing"
 
 func TestVerbose(t *testing.T) {
-	v := NewVerbose(DefaultLogger, 20)
+	logger := With(DefaultLogger, "caller", DefaultCaller, "ts", DefaultTimestamp)
+	v := NewVerbose(logger, 20)
 
-	v.V(10).Print("foo", "bar1")
-	v.V(20).Print("foo", "bar2")
-	v.V(30).Print("foo", "bar3")
+	v.V(10).Log("foo", "bar1")
+	v.V(20).Log("foo", "bar2")
+	v.V(30).Log("foo", "bar3")
 }