ecosystem の middleware にロギングあり
https://github.com/grpc-ecosystem/go-grpc-middleware/tree/master/logging/logrus
exsample_test.go には
var (
logrusLogger *logrus.Logger
customFunc grpc_logrus.CodeToLevel
)
// Initialization shows a relatively complex initialization sequence.
func Example_initialization() {
// Logrus entry is used, allowing pre-definition of certain fields by the user.
logrusEntry := logrus.NewEntry(logrusLogger)
// Shared options for the logger, with a custom gRPC code to log level function.
opts := []grpc_logrus.Option{
grpc_logrus.WithLevels(customFunc),
}
// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well.
grpc_logrus.ReplaceGrpcLogger(logrusEntry)
// Create a server, make sure we put the grpc_ctxtags context before everything else.
_ = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
),
)
}
とあるけど, 当然 logrusLogger や customFunc は初期化が必要。
func の方はデフォルトのものを用意してくれてる。logger フォーマットとか level 分けとかは任意。
func init() {
logrusLogger = logrus.New()
if isDebuggable {
logrusLogger.Level = logrus.DebugLevel
}
logrusLogger.Formatter = &logrus.JSONFormatter{
FieldMap: logrus.FieldMap{
logrus.FieldKeyTime: "timestamp",
logrus.FieldKeyLevel: "level",
logrus.FieldKeyMsg: "message",
},
TimestampFormat: time.RFC3339Nano,
}
logrusLogger.Out = os.Stdout
codeToLevel = grpc_logrus.DefaultCodeToLevel
}
単純に return err
すると, status Unknown
で返る。外部パッケージを扱う時のハンドリングはそれでいいかも。アプリ内でコンテキストを分けたい場合は以下。
// CharactorNotFoundError charactor not found
func CharactorNotFoundError(id uint64) error {
return status.Errorf(codes.NotFound, "user not found [id: %d]", id)
}
// CharactorAlreadyExistsError charactor already exists
func CharactorAlreadyExistsError(id uint64) error {
return status.Errorf(codes.AlreadyExists, "user already exists [id: %d]", id)
}
// RuntimeError internal server error
func RuntimeError(err error) error {
return status.Error(codes.Internal, err.Error())
}
↓に書いてる通りで動く。ゴルーチンで grpc サーバーを立てて, client ダミーを作ってレスポンスを見れる。httptest より早くて楽ちん。