github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/middleware/logging/context.go (about)

     1  package logging
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  
     7  	log "github.com/authzed/spicedb/internal/logging"
     8  
     9  	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors"
    10  	"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
    11  	"google.golang.org/grpc"
    12  	"google.golang.org/grpc/metadata"
    13  )
    14  
    15  // FieldSpec provides a mapping between a metadata context field and a logging field.
    16  type FieldSpec struct {
    17  	metadataKey string
    18  	tagKey      string
    19  }
    20  
    21  // ExtractMetadataField creates a specification for converting gRPC metadata fields
    22  // to log tags.
    23  func ExtractMetadataField(metadataKey, tagKey string) FieldSpec {
    24  	return FieldSpec{metadataKey, tagKey}
    25  }
    26  
    27  type extractMetadata struct {
    28  	fields []FieldSpec
    29  }
    30  
    31  func (r *extractMetadata) ServerReporter(ctx context.Context, _ interceptors.CallMeta) (interceptors.Reporter, context.Context) {
    32  	md, ok := metadata.FromIncomingContext(ctx)
    33  	if ok {
    34  		fields := logging.Fields{}
    35  		logContext := log.With()
    36  		for _, field := range r.fields {
    37  			value, ok := md[field.metadataKey]
    38  			if ok {
    39  				joinedValue := strings.Join(value, ",")
    40  				fields = append(fields, field.tagKey)
    41  				fields = append(fields, joinedValue)
    42  				logContext = logContext.Str(field.tagKey, joinedValue)
    43  			}
    44  		}
    45  
    46  		ctx = logging.InjectFields(ctx, fields)
    47  		loggerForContext := logContext.Logger()
    48  		ctx = loggerForContext.WithContext(ctx)
    49  	}
    50  
    51  	return interceptors.NoopReporter{}, ctx
    52  }
    53  
    54  // UnaryServerInterceptor creates an interceptor for extracting fields from requests
    55  // and setting them as log tags.
    56  func UnaryServerInterceptor(fields ...FieldSpec) grpc.UnaryServerInterceptor {
    57  	return interceptors.UnaryServerInterceptor(&extractMetadata{fields})
    58  }
    59  
    60  // StreamServerInterceptor creates an interceptor for extracting fields from requests
    61  // and setting them as log tags.
    62  func StreamServerInterceptor(fields ...FieldSpec) grpc.StreamServerInterceptor {
    63  	return interceptors.StreamServerInterceptor(&extractMetadata{fields})
    64  }