0
# Binary Logging
1
2
Request and response logging system for debugging, auditing, and compliance. Binary logging captures detailed information about gRPC calls including headers, messages, and metadata in a structured format.
3
4
## Capabilities
5
6
### BinaryLogs
7
8
Factory class for creating binary logging instances with different configurations and sinks.
9
10
```java { .api }
11
/**
12
* Utility class for creating binary logging instances.
13
* Provides factory methods for different logging configurations.
14
*/
15
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017")
16
public final class BinaryLogs {
17
18
/**
19
* Creates a binary log that writes to a temp file
20
* @return BinaryLog instance configured with temporary file sink
21
* @throws IOException if temp file cannot be created
22
*/
23
public static BinaryLog createBinaryLog() throws IOException;
24
25
/**
26
* Creates a binary log with custom sink and config string
27
* @param sink Custom sink for writing log messages
28
* @param configStr Config string as defined by A16-binary-logging specification
29
* @return BinaryLog instance with custom configuration
30
* @throws IOException if sink initialization fails
31
*/
32
public static BinaryLog createBinaryLog(
33
BinaryLogSink sink,
34
String configStr
35
) throws IOException;
36
37
/**
38
* Creates a binary log with sink only (deprecated)
39
* @param sink Custom sink for writing log messages
40
* @return BinaryLog instance
41
* @throws IOException if sink initialization fails
42
* @deprecated Use createBinaryLog(BinaryLogSink, String) instead
43
*/
44
@Deprecated
45
public static BinaryLog createBinaryLog(BinaryLogSink sink) throws IOException;
46
}
47
```
48
49
**Usage Examples:**
50
51
```java
52
import io.grpc.BinaryLog;
53
import io.grpc.ServerBuilder;
54
import io.grpc.protobuf.services.BinaryLogs;
55
import io.grpc.protobuf.services.TempFileSink;
56
57
// Simple binary logging to temp file
58
BinaryLog binaryLog = BinaryLogs.createBinaryLog();
59
60
Server server = ServerBuilder.forPort(8080)
61
.setBinaryLog(binaryLog)
62
.addService(new MyService())
63
.build();
64
65
// Custom binary logging with configuration
66
BinaryLogSink customSink = new TempFileSink();
67
String config = "*"; // Log all methods
68
BinaryLog configuredLog = BinaryLogs.createBinaryLog(customSink, config);
69
70
Server configuredServer = ServerBuilder.forPort(8081)
71
.setBinaryLog(configuredLog)
72
.addService(new MyService())
73
.build();
74
```
75
76
### BinaryLogSink
77
78
Interface for implementing custom binary log sinks that handle the actual storage or transmission of log data.
79
80
```java { .api }
81
/**
82
* Interface for accepting binary log messages.
83
* Implement this interface to create custom log storage backends.
84
*/
85
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4017")
86
public interface BinaryLogSink extends Closeable {
87
88
/**
89
* Writes the message to the destination
90
* @param message Protocol buffer message containing log data
91
*/
92
void write(MessageLite message);
93
}
94
```
95
96
**Custom Sink Implementation Example:**
97
98
```java
99
import io.grpc.protobuf.services.BinaryLogSink;
100
import com.google.protobuf.MessageLite;
101
import java.io.FileOutputStream;
102
import java.io.IOException;
103
104
public class CustomFileSink implements BinaryLogSink {
105
private final FileOutputStream outputStream;
106
107
public CustomFileSink(String filename) throws IOException {
108
this.outputStream = new FileOutputStream(filename, true); // Append mode
109
}
110
111
@Override
112
public void write(MessageLite message) {
113
try {
114
// Write message size first
115
byte[] messageBytes = message.toByteArray();
116
byte[] sizeBytes = ByteBuffer.allocate(4)
117
.putInt(messageBytes.length)
118
.array();
119
120
outputStream.write(sizeBytes);
121
outputStream.write(messageBytes);
122
outputStream.flush();
123
124
} catch (IOException e) {
125
System.err.println("Failed to write binary log: " + e.getMessage());
126
}
127
}
128
129
@Override
130
public void close() throws IOException {
131
if (outputStream != null) {
132
outputStream.close();
133
}
134
}
135
}
136
```
137
138
### TempFileSink
139
140
Built-in implementation that writes logs to temporary files.
141
142
```java { .api }
143
/**
144
* Binary log sink that writes to temporary files.
145
* Useful for development and debugging scenarios.
146
*/
147
public class TempFileSink implements BinaryLogSink {
148
/** Creates a temp file sink that writes to a system temporary file */
149
public TempFileSink() throws IOException;
150
}
151
```
152
153
**Usage Example:**
154
155
```java
156
import io.grpc.protobuf.services.TempFileSink;
157
import io.grpc.protobuf.services.BinaryLogs;
158
159
// Create binary log with temp file sink
160
BinaryLogSink tempSink = new TempFileSink();
161
BinaryLog binaryLog = BinaryLogs.createBinaryLog(tempSink, "*");
162
163
// Use with server
164
Server server = ServerBuilder.forPort(8080)
165
.setBinaryLog(binaryLog)
166
.addService(new MyService())
167
.build();
168
169
// Don't forget to close the sink when done
170
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
171
try {
172
tempSink.close();
173
} catch (IOException e) {
174
System.err.println("Error closing binary log sink: " + e.getMessage());
175
}
176
}));
177
```
178
179
## Configuration Patterns
180
181
Binary logging supports flexible configuration using the A16-binary-logging specification:
182
183
### Basic Configuration Examples
184
185
```java
186
// Log all methods on all services
187
String configAll = "*";
188
BinaryLog logAll = BinaryLogs.createBinaryLog(sink, configAll);
189
190
// Log specific service
191
String configService = "com.example.UserService/*";
192
BinaryLog logService = BinaryLogs.createBinaryLog(sink, configService);
193
194
// Log specific method
195
String configMethod = "com.example.UserService/GetUser";
196
BinaryLog logMethod = BinaryLogs.createBinaryLog(sink, configMethod);
197
198
// Log with size limits (header and message bytes)
199
String configLimited = "*{h:256;m:1024}";
200
BinaryLog logLimited = BinaryLogs.createBinaryLog(sink, configLimited);
201
```
202
203
### Advanced Configuration
204
205
```java
206
public class BinaryLogConfiguration {
207
208
public static BinaryLog createProductionLog() throws IOException {
209
// Production logging: limited data, specific services only
210
BinaryLogSink sink = new ProductionLogSink();
211
212
// Log only critical services with size limits
213
String config = "com.example.PaymentService/*{h:512;m:2048}," +
214
"com.example.AuthService/*{h:256;m:1024}";
215
216
return BinaryLogs.createBinaryLog(sink, config);
217
}
218
219
public static BinaryLog createDevelopmentLog() throws IOException {
220
// Development logging: full data for debugging
221
BinaryLogSink sink = new TempFileSink();
222
223
// Log everything with large limits
224
String config = "*{h:8192;m:65536}";
225
226
return BinaryLogs.createBinaryLog(sink, config);
227
}
228
229
public static BinaryLog createAuditLog() throws IOException {
230
// Audit logging: specific methods only
231
BinaryLogSink sink = new AuditLogSink();
232
233
// Log only sensitive operations
234
String config = "com.example.UserService/CreateUser," +
235
"com.example.UserService/DeleteUser," +
236
"com.example.PaymentService/ProcessPayment";
237
238
return BinaryLogs.createBinaryLog(sink, config);
239
}
240
}
241
```
242
243
## Integration with Monitoring Systems
244
245
### Structured Logging Integration
246
247
```java
248
import io.grpc.protobuf.services.BinaryLogSink;
249
import com.google.protobuf.MessageLite;
250
import io.grpc.binarylog.v1.GrpcLogEntry;
251
252
public class MonitoringLogSink implements BinaryLogSink {
253
private final Logger logger = LoggerFactory.getLogger(MonitoringLogSink.class);
254
255
@Override
256
public void write(MessageLite message) {
257
if (message instanceof GrpcLogEntry) {
258
GrpcLogEntry logEntry = (GrpcLogEntry) message;
259
260
// Extract key information for structured logging
261
Map<String, Object> logData = new HashMap<>();
262
logData.put("timestamp", logEntry.getTimestamp());
263
logData.put("type", logEntry.getType().name());
264
logData.put("logger", logEntry.getLogger().name());
265
266
if (logEntry.hasCallId()) {
267
logData.put("callId", logEntry.getCallId());
268
}
269
270
if (logEntry.hasClientHeader()) {
271
logData.put("method", logEntry.getClientHeader().getMethodName());
272
logData.put("authority", logEntry.getClientHeader().getAuthority());
273
}
274
275
// Send to monitoring system
276
logger.info("gRPC call logged: {}", logData);
277
}
278
}
279
280
@Override
281
public void close() throws IOException {
282
// Cleanup monitoring connections if needed
283
}
284
}
285
```
286
287
### Performance Monitoring
288
289
```java
290
public class PerformanceAwareBinaryLog {
291
private final BinaryLog binaryLog;
292
private final AtomicLong logCount = new AtomicLong(0);
293
294
public PerformanceAwareBinaryLog() throws IOException {
295
// Create custom sink that tracks performance
296
BinaryLogSink performanceSink = new BinaryLogSink() {
297
private final BinaryLogSink delegate = new TempFileSink();
298
299
@Override
300
public void write(MessageLite message) {
301
long count = logCount.incrementAndGet();
302
303
// Log performance warning if logging rate is high
304
if (count % 10000 == 0) {
305
System.out.println("Binary log count: " + count);
306
}
307
308
delegate.write(message);
309
}
310
311
@Override
312
public void close() throws IOException {
313
delegate.close();
314
}
315
};
316
317
// Use moderate configuration to balance debugging and performance
318
String config = "*{h:1024;m:4096}";
319
this.binaryLog = BinaryLogs.createBinaryLog(performanceSink, config);
320
}
321
322
public BinaryLog getBinaryLog() {
323
return binaryLog;
324
}
325
326
public long getLogCount() {
327
return logCount.get();
328
}
329
}
330
```
331
332
## Security and Compliance
333
334
Binary logging can capture sensitive data, so proper handling is essential:
335
336
```java
337
public class SecureBinaryLogSink implements BinaryLogSink {
338
private final BinaryLogSink delegate;
339
private final Set<String> sensitiveHeaders;
340
341
public SecureBinaryLogSink(BinaryLogSink delegate) {
342
this.delegate = delegate;
343
this.sensitiveHeaders = Set.of(
344
"authorization",
345
"x-api-key",
346
"cookie"
347
);
348
}
349
350
@Override
351
public void write(MessageLite message) {
352
// In a real implementation, you would need to parse and sanitize
353
// the message to remove sensitive data before delegating
354
355
// For demonstration, we'll just delegate
356
// In practice, implement message filtering here
357
delegate.write(message);
358
}
359
360
@Override
361
public void close() throws IOException {
362
delegate.close();
363
}
364
}
365
366
// Usage
367
BinaryLogSink secureHttp = new SecureBinaryLogSink(new TempFileSink());
368
BinaryLog secureLog = BinaryLogs.createBinaryLog(secureHttp, "*{h:512;m:0}"); // Headers only, no message bodies
369
```