mirror of https://github.com/grpc/grpc-java.git
core: initial binary log class (#3699)
Each MethodDescriptor will have a binary log, though the log may be a noop log. The binary log class is a skeleton class at the moment, but does contain the max header and message length info. The limits are determined by parsing the shell variable GRPC_BINARY_LOG_CONFIG.
This commit is contained in:
parent
e56d98723e
commit
d42110c181
|
|
@ -0,0 +1,246 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017, gRPC Authors All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.grpc.internal;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import io.grpc.MethodDescriptor;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A binary log class that is configured for a specific {@link MethodDescriptor}.
|
||||||
|
*/
|
||||||
|
final class BinaryLog {
|
||||||
|
private static final Logger logger = Logger.getLogger(BinaryLog.class.getName());
|
||||||
|
private static final BinaryLog NOOP_LOG =
|
||||||
|
new BinaryLog(/*maxHeaderBytes=*/ 0, /*maxMessageBytes=*/ 0);
|
||||||
|
private final int maxHeaderBytes;
|
||||||
|
private final int maxMessageBytes;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
BinaryLog(int maxHeaderBytes, int maxMessageBytes) {
|
||||||
|
this.maxHeaderBytes = maxHeaderBytes;
|
||||||
|
this.maxMessageBytes = maxMessageBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof BinaryLog)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BinaryLog that = (BinaryLog) o;
|
||||||
|
return this.maxHeaderBytes == that.maxHeaderBytes
|
||||||
|
&& this.maxMessageBytes == that.maxMessageBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(maxHeaderBytes, maxMessageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + '['
|
||||||
|
+ "maxHeaderBytes=" + maxHeaderBytes + ", "
|
||||||
|
+ "maxMessageBytes=" + maxMessageBytes + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Factory DEFAULT_FACTORY;
|
||||||
|
private static final Factory NOOP_FACTORY = new Factory() {
|
||||||
|
@Override
|
||||||
|
public BinaryLog getLog(String fullMethodName) {
|
||||||
|
return NOOP_LOG;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static {
|
||||||
|
Factory defaultFactory = NOOP_FACTORY;
|
||||||
|
try {
|
||||||
|
String configStr = System.getenv("GRPC_BINARY_LOG_CONFIG");
|
||||||
|
if (configStr != null && configStr.length() > 0) {
|
||||||
|
defaultFactory = new FactoryImpl(configStr);
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
logger.log(Level.SEVERE, "Failed to initialize binary log. Disabling binary log.", t);
|
||||||
|
defaultFactory = NOOP_FACTORY;
|
||||||
|
}
|
||||||
|
DEFAULT_FACTORY = defaultFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts the fullMethodName and returns the binary log that should be used. The log may be
|
||||||
|
* a log that does nothing.
|
||||||
|
*/
|
||||||
|
static BinaryLog getLog(String fullMethodName) {
|
||||||
|
return DEFAULT_FACTORY.getLog(fullMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Factory {
|
||||||
|
BinaryLog getLog(String fullMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class FactoryImpl implements Factory {
|
||||||
|
// '*' for global, 'service/*' for service glob, or 'service/method' for fully qualified.
|
||||||
|
private static final Pattern logPatternRe = Pattern.compile("[^{]+");
|
||||||
|
// A curly brace wrapped expression. Will be further matched with the more specified REs below.
|
||||||
|
private static final Pattern logOptionsRe = Pattern.compile("\\{[^}]+}");
|
||||||
|
private static final Pattern configRe = Pattern.compile(
|
||||||
|
String.format("^(%s)(%s)?$", logPatternRe.pattern(), logOptionsRe.pattern()));
|
||||||
|
// Regexes to extract per-binlog options
|
||||||
|
// The form: {m:256}
|
||||||
|
private static final Pattern msgRe = Pattern.compile("\\{m(?::(\\d+))?}");
|
||||||
|
// The form: {h:256}
|
||||||
|
private static final Pattern headerRe = Pattern.compile("\\{h(?::(\\d+))?}");
|
||||||
|
// The form: {h:256,m:256}
|
||||||
|
private static final Pattern bothRe = Pattern.compile("\\{h(?::(\\d+))?;m(?::(\\d+))?}");
|
||||||
|
|
||||||
|
private final BinaryLog globalLog;
|
||||||
|
private final Map<String, BinaryLog> perServiceLogs;
|
||||||
|
private final Map<String, BinaryLog> perMethodLogs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a string in the format specified by the binary log spec.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
FactoryImpl(String configurationString) {
|
||||||
|
Preconditions.checkState(configurationString != null && configurationString.length() > 0);
|
||||||
|
BinaryLog globalLog = null;
|
||||||
|
Map<String, BinaryLog> perServiceLogs = new HashMap<String, BinaryLog>();
|
||||||
|
Map<String, BinaryLog> perMethodLogs = new HashMap<String, BinaryLog>();
|
||||||
|
|
||||||
|
String[] configurations = configurationString.split(",");
|
||||||
|
for (String configuration : configurations) {
|
||||||
|
Matcher configMatcher = configRe.matcher(configuration);
|
||||||
|
if (!configMatcher.matches()) {
|
||||||
|
throw new IllegalArgumentException("Bad input: " + configuration);
|
||||||
|
}
|
||||||
|
String methodOrSvc = configMatcher.group(1);
|
||||||
|
String binlogOptionStr = configMatcher.group(2);
|
||||||
|
BinaryLog binLog = createBinaryLog(binlogOptionStr);
|
||||||
|
if (binLog == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (methodOrSvc.equals("*")) {
|
||||||
|
if (globalLog != null) {
|
||||||
|
logger.log(Level.SEVERE, "Ignoring duplicate entry: " + configuration);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
globalLog = binLog;
|
||||||
|
logger.info("Global binlog: " + globalLog);
|
||||||
|
} else if (isServiceGlob(methodOrSvc)) {
|
||||||
|
String service = MethodDescriptor.extractFullServiceName(methodOrSvc);
|
||||||
|
if (perServiceLogs.containsKey(service)) {
|
||||||
|
logger.log(Level.SEVERE, "Ignoring duplicate entry: " + configuration);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
perServiceLogs.put(service, binLog);
|
||||||
|
logger.info(String.format("Service binlog: service=%s log=%s", service, binLog));
|
||||||
|
} else {
|
||||||
|
// assume fully qualified method name
|
||||||
|
if (perMethodLogs.containsKey(methodOrSvc)) {
|
||||||
|
logger.log(Level.SEVERE, "Ignoring duplicate entry: " + configuration);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
perMethodLogs.put(methodOrSvc, binLog);
|
||||||
|
logger.info(String.format("Method binlog: method=%s log=%s", methodOrSvc, binLog));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.globalLog = globalLog == null ? NOOP_LOG : globalLog;
|
||||||
|
this.perServiceLogs = Collections.unmodifiableMap(perServiceLogs);
|
||||||
|
this.perMethodLogs = Collections.unmodifiableMap(perMethodLogs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a full method name and returns the log that should be used.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BinaryLog getLog(String fullMethodName) {
|
||||||
|
BinaryLog methodLog = perMethodLogs.get(fullMethodName);
|
||||||
|
if (methodLog != null) {
|
||||||
|
return methodLog;
|
||||||
|
}
|
||||||
|
BinaryLog serviceLog = perServiceLogs.get(
|
||||||
|
MethodDescriptor.extractFullServiceName(fullMethodName));
|
||||||
|
if (serviceLog != null) {
|
||||||
|
return serviceLog;
|
||||||
|
}
|
||||||
|
return globalLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a binlog with the correct header and message limits or {@code null} if the input
|
||||||
|
* is malformed. The input should be a string that is in one of these forms:
|
||||||
|
*
|
||||||
|
* <p>{@code {h(:\d+)?}, {m(:\d+)?}, {h(:\d+)?,m(:\d+)?}}
|
||||||
|
*
|
||||||
|
* <p>If the {@code logConfig} is null, the returned binlog will have a limit of
|
||||||
|
* Integer.MAX_VALUE.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
@Nullable
|
||||||
|
static BinaryLog createBinaryLog(@Nullable String logConfig) {
|
||||||
|
if (logConfig == null) {
|
||||||
|
return new BinaryLog(Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Matcher headerMatcher;
|
||||||
|
Matcher msgMatcher;
|
||||||
|
Matcher bothMatcher;
|
||||||
|
final int maxHeaderBytes;
|
||||||
|
final int maxMsgBytes;
|
||||||
|
if ((headerMatcher = headerRe.matcher(logConfig)).matches()) {
|
||||||
|
String maxHeaderStr = headerMatcher.group(1);
|
||||||
|
maxHeaderBytes =
|
||||||
|
maxHeaderStr != null ? Integer.parseInt(maxHeaderStr) : Integer.MAX_VALUE;
|
||||||
|
maxMsgBytes = 0;
|
||||||
|
} else if ((msgMatcher = msgRe.matcher(logConfig)).matches()) {
|
||||||
|
maxHeaderBytes = 0;
|
||||||
|
String maxMsgStr = msgMatcher.group(1);
|
||||||
|
maxMsgBytes = maxMsgStr != null ? Integer.parseInt(maxMsgStr) : Integer.MAX_VALUE;
|
||||||
|
} else if ((bothMatcher = bothRe.matcher(logConfig)).matches()) {
|
||||||
|
String maxHeaderStr = bothMatcher.group(1);
|
||||||
|
String maxMsgStr = bothMatcher.group(2);
|
||||||
|
maxHeaderBytes =
|
||||||
|
maxHeaderStr != null ? Integer.parseInt(maxHeaderStr) : Integer.MAX_VALUE;
|
||||||
|
maxMsgBytes = maxMsgStr != null ? Integer.parseInt(maxMsgStr) : Integer.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
logger.log(Level.SEVERE, "Illegal log config pattern: " + logConfig);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new BinaryLog(maxHeaderBytes, maxMsgBytes);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.log(Level.SEVERE, "Illegal log config pattern: " + logConfig);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the input string is a glob of the form: {@code <package-service>/*}.
|
||||||
|
*/
|
||||||
|
static boolean isServiceGlob(String input) {
|
||||||
|
return input.endsWith("/*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -82,6 +82,7 @@ final class ClientCallImpl<ReqT, RespT> extends ClientCall<ReqT, RespT> {
|
||||||
private boolean fullStreamDecompression;
|
private boolean fullStreamDecompression;
|
||||||
private DecompressorRegistry decompressorRegistry = DecompressorRegistry.getDefaultInstance();
|
private DecompressorRegistry decompressorRegistry = DecompressorRegistry.getDefaultInstance();
|
||||||
private CompressorRegistry compressorRegistry = CompressorRegistry.getDefaultInstance();
|
private CompressorRegistry compressorRegistry = CompressorRegistry.getDefaultInstance();
|
||||||
|
private final BinaryLog binlog;
|
||||||
|
|
||||||
ClientCallImpl(
|
ClientCallImpl(
|
||||||
MethodDescriptor<ReqT, RespT> method, Executor executor, CallOptions callOptions,
|
MethodDescriptor<ReqT, RespT> method, Executor executor, CallOptions callOptions,
|
||||||
|
|
@ -101,6 +102,7 @@ final class ClientCallImpl<ReqT, RespT> extends ClientCall<ReqT, RespT> {
|
||||||
this.callOptions = callOptions;
|
this.callOptions = callOptions;
|
||||||
this.clientTransportProvider = clientTransportProvider;
|
this.clientTransportProvider = clientTransportProvider;
|
||||||
this.deadlineCancellationExecutor = deadlineCancellationExecutor;
|
this.deadlineCancellationExecutor = deadlineCancellationExecutor;
|
||||||
|
this.binlog = BinaryLog.getLog(method.getFullMethodName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class ContextCancellationListener implements CancellationListener {
|
private final class ContextCancellationListener implements CancellationListener {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ final class ServerCallImpl<ReqT, RespT> extends ServerCall<ReqT, RespT> {
|
||||||
private final byte[] messageAcceptEncoding;
|
private final byte[] messageAcceptEncoding;
|
||||||
private final DecompressorRegistry decompressorRegistry;
|
private final DecompressorRegistry decompressorRegistry;
|
||||||
private final CompressorRegistry compressorRegistry;
|
private final CompressorRegistry compressorRegistry;
|
||||||
|
private final BinaryLog binlog;
|
||||||
|
|
||||||
// state
|
// state
|
||||||
private volatile boolean cancelled;
|
private volatile boolean cancelled;
|
||||||
|
|
@ -71,6 +72,7 @@ final class ServerCallImpl<ReqT, RespT> extends ServerCall<ReqT, RespT> {
|
||||||
this.messageAcceptEncoding = inboundHeaders.get(MESSAGE_ACCEPT_ENCODING_KEY);
|
this.messageAcceptEncoding = inboundHeaders.get(MESSAGE_ACCEPT_ENCODING_KEY);
|
||||||
this.decompressorRegistry = decompressorRegistry;
|
this.decompressorRegistry = decompressorRegistry;
|
||||||
this.compressorRegistry = compressorRegistry;
|
this.compressorRegistry = compressorRegistry;
|
||||||
|
binlog = BinaryLog.getLog(method.getFullMethodName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2017, gRPC Authors All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.grpc.internal;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
import io.grpc.internal.BinaryLog.FactoryImpl;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
/** Tests for {@link BinaryLog}. */
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public final class BinaryLogTest {
|
||||||
|
private static final BinaryLog NONE = new Builder().build();
|
||||||
|
private static final BinaryLog HEADER_FULL = new Builder().header(Integer.MAX_VALUE).build();
|
||||||
|
private static final BinaryLog HEADER_256 = new Builder().header(256).build();
|
||||||
|
private static final BinaryLog MSG_FULL = new Builder().msg(Integer.MAX_VALUE).build();
|
||||||
|
private static final BinaryLog MSG_256 = new Builder().msg(256).build();
|
||||||
|
private static final BinaryLog BOTH_256 = new Builder().header(256).msg(256).build();
|
||||||
|
private static final BinaryLog BOTH_FULL =
|
||||||
|
new Builder().header(Integer.MAX_VALUE).msg(Integer.MAX_VALUE).build();
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_global() throws Exception {
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("*").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("*{h;m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_FULL, new FactoryImpl("*{h}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_FULL, new FactoryImpl("*{m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_256, new FactoryImpl("*{h:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_256, new FactoryImpl("*{m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_256, new FactoryImpl("*{h:256;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(Integer.MAX_VALUE).msg(256).build(),
|
||||||
|
new FactoryImpl("*{h;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(256).msg(Integer.MAX_VALUE).build(),
|
||||||
|
new FactoryImpl("*{h:256;m}").getLog("p.s/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_method() throws Exception {
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("p.s/m").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("p.s/m{h;m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_FULL, new FactoryImpl("p.s/m{h}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_FULL, new FactoryImpl("p.s/m{m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_256, new FactoryImpl("p.s/m{h:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_256, new FactoryImpl("p.s/m{m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_256, new FactoryImpl("p.s/m{h:256;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(Integer.MAX_VALUE).msg(256).build(),
|
||||||
|
new FactoryImpl("p.s/m{h;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(256).msg(Integer.MAX_VALUE).build(),
|
||||||
|
new FactoryImpl("p.s/m{h:256;m}").getLog("p.s/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_method_absent() throws Exception {
|
||||||
|
assertEquals(NONE, new FactoryImpl("p.s/m").getLog("p.s/absent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_service() throws Exception {
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("p.s/*").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_FULL, new FactoryImpl("p.s/*{h;m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_FULL, new FactoryImpl("p.s/*{h}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_FULL, new FactoryImpl("p.s/*{m}").getLog("p.s/m"));
|
||||||
|
assertEquals(HEADER_256, new FactoryImpl("p.s/*{h:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(MSG_256, new FactoryImpl("p.s/*{m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(BOTH_256, new FactoryImpl("p.s/*{h:256;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(Integer.MAX_VALUE).msg(256).build(),
|
||||||
|
new FactoryImpl("p.s/*{h;m:256}").getLog("p.s/m"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(256).msg(Integer.MAX_VALUE).build(),
|
||||||
|
new FactoryImpl("p.s/*{h:256;m}").getLog("p.s/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_service_absent() throws Exception {
|
||||||
|
assertEquals(NONE, new FactoryImpl("p.s/*").getLog("p.other/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createLogFromOptionString() throws Exception {
|
||||||
|
assertEquals(BOTH_FULL, FactoryImpl.createBinaryLog(/*logConfig=*/ null));
|
||||||
|
assertEquals(HEADER_FULL, FactoryImpl.createBinaryLog("{h}"));
|
||||||
|
assertEquals(MSG_FULL, FactoryImpl.createBinaryLog("{m}"));
|
||||||
|
assertEquals(HEADER_256, FactoryImpl.createBinaryLog("{h:256}"));
|
||||||
|
assertEquals(MSG_256, FactoryImpl.createBinaryLog("{m:256}"));
|
||||||
|
assertEquals(BOTH_256, FactoryImpl.createBinaryLog("{h:256;m:256}"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(Integer.MAX_VALUE).msg(256).build(),
|
||||||
|
FactoryImpl.createBinaryLog("{h;m:256}"));
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(256).msg(Integer.MAX_VALUE).build(),
|
||||||
|
FactoryImpl.createBinaryLog("{h:256;m}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createLogFromOptionString_malformed() throws Exception {
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("bad"));
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{bad}"));
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{x;y}"));
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{h:abc}"));
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{2}"));
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{2;2}"));
|
||||||
|
// The grammar specifies that if both h and m are present, h comes before m
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{m:123;h:123}"));
|
||||||
|
// NumberFormatException
|
||||||
|
assertNull(FactoryImpl.createBinaryLog("{h:99999999999999}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_multiConfig_withGlobal() throws Exception {
|
||||||
|
FactoryImpl factory = new FactoryImpl(
|
||||||
|
"*{h},"
|
||||||
|
+ "package.both256/*{h:256;m:256},"
|
||||||
|
+ "package.service1/both128{h:128;m:128},"
|
||||||
|
+ "package.service2/method_messageOnly{m}");
|
||||||
|
assertEquals(HEADER_FULL, factory.getLog("otherpackage.service/method"));
|
||||||
|
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method1"));
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method2"));
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method3"));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(128).msg(128).build(), factory.getLog("package.service1/both128"));
|
||||||
|
// the global config is in effect
|
||||||
|
assertEquals(HEADER_FULL, factory.getLog("package.service1/absent"));
|
||||||
|
|
||||||
|
assertEquals(MSG_FULL, factory.getLog("package.service2/method_messageOnly"));
|
||||||
|
// the global config is in effect
|
||||||
|
assertEquals(HEADER_FULL, factory.getLog("package.service2/absent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_multiConfig_noGlobal() throws Exception {
|
||||||
|
FactoryImpl factory = new FactoryImpl(
|
||||||
|
"package.both256/*{h:256;m:256},"
|
||||||
|
+ "package.service1/both128{h:128;m:128},"
|
||||||
|
+ "package.service2/method_messageOnly{m}");
|
||||||
|
assertEquals(NONE, factory.getLog("otherpackage.service/method"));
|
||||||
|
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method1"));
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method2"));
|
||||||
|
assertEquals(BOTH_256, factory.getLog("package.both256/method3"));
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
new Builder().header(128).msg(128).build(), factory.getLog("package.service1/both128"));
|
||||||
|
// no global config in effect
|
||||||
|
assertEquals(NONE, factory.getLog("package.service1/absent"));
|
||||||
|
|
||||||
|
assertEquals(MSG_FULL, factory.getLog("package.service2/method_messageOnly"));
|
||||||
|
// no global config in effect
|
||||||
|
assertEquals(NONE, factory.getLog("package.service2/absent"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_ignoreDuplicates_global() throws Exception {
|
||||||
|
FactoryImpl factory = new FactoryImpl("*{h},p.s/m,*{h:256}");
|
||||||
|
// The duplicate
|
||||||
|
assertEquals(HEADER_FULL, factory.getLog("p.other1/m"));
|
||||||
|
assertEquals(HEADER_FULL, factory.getLog("p.other2/m"));
|
||||||
|
// Other
|
||||||
|
assertEquals(BOTH_FULL, factory.getLog("p.s/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_ignoreDuplicates_service() throws Exception {
|
||||||
|
FactoryImpl factory = new FactoryImpl("p.s/*,*{h:256},p.s/*{h}");
|
||||||
|
// The duplicate
|
||||||
|
assertEquals(BOTH_FULL, factory.getLog("p.s/m1"));
|
||||||
|
assertEquals(BOTH_FULL, factory.getLog("p.s/m2"));
|
||||||
|
// Other
|
||||||
|
assertEquals(HEADER_256, factory.getLog("p.other1/m"));
|
||||||
|
assertEquals(HEADER_256, factory.getLog("p.other2/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configBinLog_ignoreDuplicates_method() throws Exception {
|
||||||
|
FactoryImpl factory = new FactoryImpl("p.s/m,*{h:256},p.s/m{h}");
|
||||||
|
// The duplicate
|
||||||
|
assertEquals(BOTH_FULL, factory.getLog("p.s/m"));
|
||||||
|
// Other
|
||||||
|
assertEquals(HEADER_256, factory.getLog("p.other1/m"));
|
||||||
|
assertEquals(HEADER_256, factory.getLog("p.other2/m"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A builder class to make unit test code more readable. */
|
||||||
|
private static final class Builder {
|
||||||
|
int maxHeaderBytes = 0;
|
||||||
|
int maxMessageBytes = 0;
|
||||||
|
|
||||||
|
Builder header(int bytes) {
|
||||||
|
maxHeaderBytes = bytes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Builder msg(int bytes) {
|
||||||
|
maxMessageBytes = bytes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BinaryLog build() {
|
||||||
|
return new BinaryLog(maxHeaderBytes, maxMessageBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue