Files
wehub-resource-sync 6cf6f95f58
CodeQL / Analyze (push) Has been cancelled
Sync to Gitee / Run (push) Has been cancelled
Go / build & test (1.25.x) (push) Has been cancelled
Go / build & test (1.26.x) (push) Has been cancelled
Lint / resolve module (push) Has been cancelled
Lint / lint module (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:43 +08:00

138 lines
3.4 KiB
Go

package logging
import (
"context"
"fmt"
"log/slog"
"time"
"google.golang.org/grpc/codes"
"github.com/go-kratos/kratos/v3/errors"
"github.com/go-kratos/kratos/v3/middleware"
"github.com/go-kratos/kratos/v3/transport"
"github.com/go-kratos/kratos/v3/transport/http/status"
)
// Redacter defines how to log an object
type Redacter interface {
Redact() string
}
// Server is an server logging middleware.
func Server(logger *slog.Logger) middleware.Middleware {
if logger == nil {
logger = slog.Default()
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req any) (reply any, err error) {
var (
code int32
reason string
kind string
operation string
)
// default code
code = int32(status.FromGRPCCode(codes.OK))
startTime := time.Now()
if info, ok := transport.FromServerContext(ctx); ok {
kind = info.Kind().String()
operation = info.Operation()
}
reply, err = handler(ctx, req)
if se := errors.FromError(err); se != nil {
code = se.Code
reason = se.Reason
}
level, stack := extractError(err)
attrs := []slog.Attr{
slog.String("kind", "server"),
slog.String("component", kind),
slog.String("operation", operation),
slog.String("args", extractArgs(req)),
slog.Int64("code", int64(code)),
slog.String("reason", reason),
slog.Float64("latency", time.Since(startTime).Seconds()),
}
if err != nil {
attrs = append(attrs, slog.Any("error", err))
if stack != "" {
attrs = append(attrs, slog.String("stack", stack))
}
}
logger.LogAttrs(ctx, level, "server request", attrs...)
return
}
}
}
// Client is a client logging middleware.
func Client(logger *slog.Logger) middleware.Middleware {
if logger == nil {
logger = slog.Default()
}
return func(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req any) (reply any, err error) {
var (
code int32
reason string
kind string
operation string
)
// default code
code = int32(status.FromGRPCCode(codes.OK))
startTime := time.Now()
if info, ok := transport.FromClientContext(ctx); ok {
kind = info.Kind().String()
operation = info.Operation()
}
reply, err = handler(ctx, req)
if se := errors.FromError(err); se != nil {
code = se.Code
reason = se.Reason
}
level, stack := extractError(err)
attrs := []slog.Attr{
slog.String("kind", "client"),
slog.String("component", kind),
slog.String("operation", operation),
slog.String("args", extractArgs(req)),
slog.Int64("code", int64(code)),
slog.String("reason", reason),
slog.Float64("latency", time.Since(startTime).Seconds()),
}
if err != nil {
attrs = append(attrs, slog.Any("error", err))
if stack != "" {
attrs = append(attrs, slog.String("stack", stack))
}
}
logger.LogAttrs(ctx, level, "client request", attrs...)
return
}
}
}
// extractArgs returns the string of the req
func extractArgs(req any) string {
if redacter, ok := req.(Redacter); ok {
return redacter.Redact()
}
if stringer, ok := req.(fmt.Stringer); ok {
return stringer.String()
}
return fmt.Sprintf("%+v", req)
}
// extractError returns the level and stack to attach for err.
func extractError(err error) (slog.Level, string) {
if err != nil {
return slog.LevelError, fmt.Sprintf("%+v", err)
}
return slog.LevelInfo, ""
}