Skip to content

Commit 3e27f89

Browse files
authored
binarylog: Account for key in metadata truncation (#5851)
1 parent f54bba9 commit 3e27f89

File tree

2 files changed

+129
-1
lines changed

2 files changed

+129
-1
lines changed

gcp/observability/logging_test.go

+128
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ func cmpLoggingEntryList(got []*grpcLogEntry, want []*grpcLogEntry) error {
4444
if len(a) > len(b) {
4545
a, b = b, a
4646
}
47+
if len(a) == 0 && len(a) != len(b) { // No metadata for one and the other comparator wants metadata.
48+
return false
49+
}
4750
for k, v := range a {
4851
if b[k] != v {
4952
return false
@@ -1145,3 +1148,128 @@ func (s) TestMarshalJSON(t *testing.T) {
11451148
t.Fatalf("json.Marshal(%v) failed with error: %v", logEntry, err)
11461149
}
11471150
}
1151+
1152+
// TestMetadataTruncationAccountsKey tests that the metadata truncation takes
1153+
// into account both the key and value of metadata. It configures an
1154+
// observability system with a maximum byte length for metadata, which is
1155+
// greater than just the byte length of the metadata value but less than the
1156+
// byte length of the metadata key + metadata value. Thus, in the ClientHeader
1157+
// logging event, no metadata should be logged.
1158+
func (s) TestMetadataTruncationAccountsKey(t *testing.T) {
1159+
fle := &fakeLoggingExporter{
1160+
t: t,
1161+
}
1162+
defer func(ne func(ctx context.Context, config *config) (loggingExporter, error)) {
1163+
newLoggingExporter = ne
1164+
}(newLoggingExporter)
1165+
1166+
newLoggingExporter = func(ctx context.Context, config *config) (loggingExporter, error) {
1167+
return fle, nil
1168+
}
1169+
1170+
const mdValue = "value"
1171+
configMetadataLimit := &config{
1172+
ProjectID: "fake",
1173+
CloudLogging: &cloudLogging{
1174+
ClientRPCEvents: []clientRPCEvents{
1175+
{
1176+
Methods: []string{"*"},
1177+
MaxMetadataBytes: len(mdValue) + 1,
1178+
},
1179+
},
1180+
},
1181+
}
1182+
1183+
cleanup, err := setupObservabilitySystemWithConfig(configMetadataLimit)
1184+
if err != nil {
1185+
t.Fatalf("error setting up observability %v", err)
1186+
}
1187+
defer cleanup()
1188+
1189+
ss := &stubserver.StubServer{
1190+
UnaryCallF: func(ctx context.Context, in *grpc_testing.SimpleRequest) (*grpc_testing.SimpleResponse, error) {
1191+
return &grpc_testing.SimpleResponse{}, nil
1192+
},
1193+
}
1194+
if err := ss.Start(nil); err != nil {
1195+
t.Fatalf("Error starting endpoint server: %v", err)
1196+
}
1197+
defer ss.Stop()
1198+
1199+
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
1200+
defer cancel()
1201+
1202+
// the set config MaxMetdataBytes is in between len(mdValue) and len("key")
1203+
// + len(mdValue), and thus shouldn't log this metadata entry.
1204+
md := metadata.MD{
1205+
"key": []string{mdValue},
1206+
}
1207+
ctx = metadata.NewOutgoingContext(ctx, md)
1208+
if _, err := ss.Client.UnaryCall(ctx, &grpc_testing.SimpleRequest{Payload: &grpc_testing.Payload{Body: []byte("00000")}}); err != nil {
1209+
t.Fatalf("Unexpected error from UnaryCall: %v", err)
1210+
}
1211+
1212+
grpcLogEntriesWant := []*grpcLogEntry{
1213+
{
1214+
Type: eventTypeClientHeader,
1215+
Logger: loggerClient,
1216+
ServiceName: "grpc.testing.TestService",
1217+
MethodName: "UnaryCall",
1218+
Authority: ss.Address,
1219+
SequenceID: 1,
1220+
Payload: payload{
1221+
Metadata: map[string]string{},
1222+
},
1223+
PayloadTruncated: true,
1224+
},
1225+
{
1226+
Type: eventTypeClientMessage,
1227+
Logger: loggerClient,
1228+
ServiceName: "grpc.testing.TestService",
1229+
MethodName: "UnaryCall",
1230+
SequenceID: 2,
1231+
Authority: ss.Address,
1232+
Payload: payload{
1233+
MessageLength: 9,
1234+
Message: []uint8{},
1235+
},
1236+
PayloadTruncated: true,
1237+
},
1238+
{
1239+
Type: eventTypeServerHeader,
1240+
Logger: loggerClient,
1241+
ServiceName: "grpc.testing.TestService",
1242+
MethodName: "UnaryCall",
1243+
SequenceID: 3,
1244+
Authority: ss.Address,
1245+
Payload: payload{
1246+
Metadata: map[string]string{},
1247+
},
1248+
},
1249+
{
1250+
Type: eventTypeServerMessage,
1251+
Logger: loggerClient,
1252+
ServiceName: "grpc.testing.TestService",
1253+
MethodName: "UnaryCall",
1254+
Authority: ss.Address,
1255+
SequenceID: 4,
1256+
},
1257+
{
1258+
Type: eventTypeServerTrailer,
1259+
Logger: loggerClient,
1260+
ServiceName: "grpc.testing.TestService",
1261+
MethodName: "UnaryCall",
1262+
SequenceID: 5,
1263+
Authority: ss.Address,
1264+
Payload: payload{
1265+
Metadata: map[string]string{},
1266+
},
1267+
},
1268+
}
1269+
fle.mu.Lock()
1270+
if err := cmpLoggingEntryList(fle.entries, grpcLogEntriesWant); err != nil {
1271+
fle.mu.Unlock()
1272+
t.Fatalf("error in logging entry list comparison %v", err)
1273+
}
1274+
fle.mu.Unlock()
1275+
}

internal/binarylog/method_logger.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (ml *TruncatingMethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated
121121
// but not counted towards the size limit.
122122
continue
123123
}
124-
currentEntryLen := uint64(len(entry.Value))
124+
currentEntryLen := uint64(len(entry.GetKey())) + uint64(len(entry.GetValue()))
125125
if currentEntryLen > bytesLimit {
126126
break
127127
}

0 commit comments

Comments
 (0)