Documentation

Overview

Package appstatus can attach/reterieve an application-specific response status to/from an error. It designed to prevent accidental exposure of internal statuses to RPC clients, for example Spanner's statuses.

Attaching a status

Use ToError, Error and Errorf to create new status-annotated errors. Use Attach and Attachf to annotate existing errors with a status.

if req.PageSize < 0  {
   return appstatus.Errorf(codes.InvalidArgument, "page size cannot be negative")
}
if err := checkState(); err != nil {
  return appstatus.Attachf(err, codes.PreconditionFailed, "invalid state")
}

This may be done deep in the function call hierarchy.

Do not use appstatus in code where you don't explicitly intend to return a specific status. This is unnecessary because any unrecognized error is treated as internal and because it is explicitly prohibited to attach a status to an error chain multiple times. When a status is attached, the package supports its propagation all the way to the requester unless there is code that explicitly throws it away.

Returning a status

Use GRPCifyAndLog right before returning the error from a gRPC method handler. Usually it is done in a Postlude of a service decorator, see ../cmd/svcdec.

func NewMyServer() pb.MyServer {
  return &pb.DecoratedMyServer{
    Service:  &actualImpl{},
    Postlude: func(ctx context.Context, methodName string, rsp proto.Message, err error) error {
      return appstatus.GRPCifyAndLog(ctx, err)
    },
  }
}

It recognizes only appstatus-annotated errors and treats any other error as internal. This behavior is important to avoid accidentally returning errors from Spanner or other client library that also uses grpc/status package to communicate status code from *other* services. For example, if a there is a typo in Spanner SQL statement, spanner package may return a status-annotated errors with code NotFound. In this case, our RPC must respond with internal error and not NotFound.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Attach

func Attach(err error, status *status.Status) error

    Attach attaches an application-specific status to the error. The status will be shared with the RPC client as is. If err already has an application-specific status attached, panics.

    func Attachf

    func Attachf(err error, code codes.Code, format string, args ...interface{}) error

      Attachf is a shortcut for Attach(err, status.Newf(...))

      func BadRequest

      func BadRequest(err error, details ...*errdetails.BadRequest) error

        BadRequest annotates err as a bad request. The error message is shared with the requester as is.

        func Error

        func Error(code codes.Code, msg string) error

          Error returns an error with an application-specific status. The message will be shared with the RPC client as is.

          func Errorf

          func Errorf(code codes.Code, format string, args ...interface{}) error

            Errorf returns an error with an application-specific status. The message will be shared with the RPC client as is.

            func GRPCifyAndLog

            func GRPCifyAndLog(ctx context.Context, err error) error

              GRPCifyAndLog returns a GRPC error. If the error doesn't have a GRPC status attached by this package, internal error is assumed. Any internal or unknown errors are logged.

              func Get

              func Get(err error) (st *status.Status, ok bool)

                Get returns an application-specific Status attached to err using this package. If not explicitly set or if err is nil, then ok is false.

                func MustWithDetails

                func MustWithDetails(s *status.Status, details ...proto.Message) *status.Status

                  MustWithDetails adds details to a status and asserts it is successful.

                  func ToError

                  func ToError(s *status.Status) error

                    ToError converts an application-specific status to an error.

                    Types

                    This section is empty.