diff --git a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java index 5bb613f7b..87ca324d7 100644 --- a/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java +++ b/redisson/src/main/java/org/redisson/client/handler/CommandDecoder.java @@ -45,6 +45,7 @@ import org.redisson.client.protocol.pubsub.Message; import org.redisson.client.protocol.pubsub.PubSubMessage; import org.redisson.client.protocol.pubsub.PubSubPatternMessage; import org.redisson.client.protocol.pubsub.PubSubStatusMessage; +import org.redisson.misc.LogHelper; import org.redisson.misc.RPromise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -203,11 +204,11 @@ public class CommandDecoder extends ReplayingDecoder { RPromise promise = commandBatch.getPromise(); if (error != null) { if (!promise.tryFailure(error) && promise.cause() instanceof RedisTimeoutException) { - log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), data); + log.warn("response has been skipped due to timeout! channel: {}, command: {}",ctx.channel(), LogHelper.toString(data)); } } else { if (!promise.trySuccess(null) && promise.cause() instanceof RedisTimeoutException) { - log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), data); + log.warn("response has been skipped due to timeout! channel: {}, command: {}", ctx.channel(), LogHelper.toString(data)); } } diff --git a/redisson/src/main/java/org/redisson/client/protocol/CommandData.java b/redisson/src/main/java/org/redisson/client/protocol/CommandData.java index 6d51b4ee6..053160c02 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/CommandData.java +++ b/redisson/src/main/java/org/redisson/client/protocol/CommandData.java @@ -21,6 +21,7 @@ import java.util.List; import org.redisson.client.codec.Codec; import org.redisson.client.protocol.decoder.MultiDecoder; +import org.redisson.misc.LogHelper; import org.redisson.misc.RPromise; /** @@ -85,7 +86,7 @@ public class CommandData implements QueueCommand { @Override public String toString() { return "CommandData [promise=" + promise + ", command=" + command + ", params=" - + Arrays.toString(params) + ", codec=" + codec + "]"; + + LogHelper.toString(params) + ", codec=" + codec + "]"; } @Override diff --git a/redisson/src/main/java/org/redisson/command/CommandAsyncService.java b/redisson/src/main/java/org/redisson/command/CommandAsyncService.java index 35d1cb7a8..1dc08ddb3 100644 --- a/redisson/src/main/java/org/redisson/command/CommandAsyncService.java +++ b/redisson/src/main/java/org/redisson/command/CommandAsyncService.java @@ -49,6 +49,7 @@ import org.redisson.connection.ConnectionManager; import org.redisson.connection.MasterSlaveEntry; import org.redisson.connection.NodeSource; import org.redisson.connection.NodeSource.Redirect; +import org.redisson.misc.LogHelper; import org.redisson.misc.RPromise; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -520,7 +521,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { if (details.getAttempt() == connectionManager.getConfig().getRetryAttempts()) { if (details.getException() == null) { - details.setException(new RedisTimeoutException("Command execution timeout for command: " + command + " with params: " + Arrays.toString(details.getParams()))); + details.setException(new RedisTimeoutException("Command execution timeout for command: " + command + " with params: " + LogHelper.toString(details.getParams()))); } details.getAttemptPromise().tryFailure(details.getException()); return; @@ -605,7 +606,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { if (!future.isSuccess()) { details.setException(new WriteRedisConnectionException( - "Can't write command: " + details.getCommand() + ", params: " + Arrays.toString(details.getParams()) + " to channel: " + future.channel(), future.cause())); + "Can't write command: " + details.getCommand() + ", params: " + LogHelper.toString(details.getParams()) + " to channel: " + future.channel(), future.cause())); return; } @@ -629,7 +630,7 @@ public class CommandAsyncService implements CommandAsyncExecutor { public void run(Timeout timeout) throws Exception { details.getAttemptPromise().tryFailure( new RedisTimeoutException("Redis server response timeout (" + timeoutAmount + " ms) occured for command: " + details.getCommand() - + " with params: " + Arrays.toString(details.getParams()) + " channel: " + connection.getChannel())); + + " with params: " + LogHelper.toString(details.getParams()) + " channel: " + connection.getChannel())); } }; diff --git a/redisson/src/main/java/org/redisson/misc/LogHelper.java b/redisson/src/main/java/org/redisson/misc/LogHelper.java new file mode 100644 index 000000000..dbe458f2f --- /dev/null +++ b/redisson/src/main/java/org/redisson/misc/LogHelper.java @@ -0,0 +1,81 @@ +/** + * Copyright 2016 Nikita Koksharov + * + * 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 org.redisson.misc; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Objects; + +/** + * @author Philipp Marx + */ +public class LogHelper { + + private static final int MAX_COLLECTION_LOG_SIZE = Integer.valueOf(System.getProperty("redisson.maxCollectionLogSize", "10")); + private static final int MAX_STRING_LOG_SIZE = Integer.valueOf(System.getProperty("redisson.maxStringLogSize", "100")); + + private LogHelper() { + } + + public static String toString(T object) { + if (object == null) { + return "null"; + } else if (object instanceof String) { + return toStringString((String) object); + } else if (object.getClass().isArray()) { + return toArrayString(object); + } else if (object instanceof Collection) { + return toCollectionString((Collection) object); + } else { + return Objects.toString(object); + } + } + + private static String toStringString(String string) { + if (string.length() > MAX_STRING_LOG_SIZE) { + return new StringBuilder(string.substring(0, MAX_STRING_LOG_SIZE)).append("...").toString(); + } else { + return string; + } + } + + private static String toCollectionString(Collection collection) { + return toArrayString(collection.toArray(new Object[collection.size()])); + } + + private static String toArrayString(Object array) { + int length = Array.getLength(array) - 1; + if (length == -1) { + return "[]"; + } + + StringBuilder b = new StringBuilder(length * 3); + b.append('['); + for (int i = 0;; ++i) { + b.append(toString(Array.get(array, i))); + + if (i == length) { + return b.append(']').toString(); + } + + b.append(", "); + + if (i == MAX_COLLECTION_LOG_SIZE - 1) { + return b.append("...]").toString(); + } + } + } +} diff --git a/redisson/src/test/java/org/redisson/misc/LogHelperTest.java b/redisson/src/test/java/org/redisson/misc/LogHelperTest.java new file mode 100644 index 000000000..7fb99b12e --- /dev/null +++ b/redisson/src/test/java/org/redisson/misc/LogHelperTest.java @@ -0,0 +1,223 @@ +package org.redisson.misc; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +/** + * @author Philipp Marx + */ +public class LogHelperTest { + + @Test + public void toStringWithNull() { + assertThat(LogHelper.toString(null), is("null")); + } + + @Test + public void toStringWithNestedPrimitives() { + Object[] input = new Object[] { "0", 1, 2L, 3.1D, 4.2F, (byte) 5, '6' }; + + assertThat(LogHelper.toString(input), is("[0, 1, 2, 3.1, 4.2, 5, 6]")); + } + + @Test + public void toStringWithPrimitive() { + assertThat(LogHelper.toString("0"), is("0")); + assertThat(LogHelper.toString(1), is("1")); + assertThat(LogHelper.toString(2L), is("2")); + assertThat(LogHelper.toString(3.1D), is("3.1")); + assertThat(LogHelper.toString(4.2F), is("4.2")); + assertThat(LogHelper.toString((byte) 5), is("5")); + assertThat(LogHelper.toString('6'), is("6")); + } + + @Test + public void toStringWithNestedSmallArrays() { + String[] strings = new String[] { "0" }; + int[] ints = new int[] { 1 }; + long[] longs = new long[] { 2L }; + double[] doubles = new double[] { 3.1D }; + float[] floats = new float[] { 4.2F }; + byte[] bytes = new byte[] { (byte) 5 }; + char[] chars = new char[] { '6' }; + + Object[] input = new Object[] { strings, ints, longs, doubles, floats, bytes, chars }; + + assertThat(LogHelper.toString(input), is("[[0], [1], [2], [3.1], [4.2], [5], [6]]")); + } + + @Test + public void toStringWithNestedSmallCollections() { + List strings = Arrays.asList("0" ); + List ints = Arrays.asList( 1 ); + List longs = Arrays.asList( 2L ); + List doubles = Arrays.asList( 3.1D ); + List floats = Arrays.asList( 4.2F ); + List bytes = Arrays.asList( (byte) 5 ); + List chars = Arrays.asList( '6' ); + + Object[] input = new Object[] { strings, ints, longs, doubles, floats, bytes, chars }; + + assertThat(LogHelper.toString(input), is("[[0], [1], [2], [3.1], [4.2], [5], [6]]")); + } + + @Test + public void toStringWithSmallArrays() { + String[] strings = new String[] { "0" }; + int[] ints = new int[] { 1 }; + long[] longs = new long[] { 2L }; + double[] doubles = new double[] { 3.1D }; + float[] floats = new float[] { 4.2F }; + byte[] bytes = new byte[] { (byte) 5 }; + char[] chars = new char[] { '6' }; + + assertThat(LogHelper.toString(strings), is("[0]")); + assertThat(LogHelper.toString(ints), is("[1]")); + assertThat(LogHelper.toString(longs), is("[2]")); + assertThat(LogHelper.toString(doubles), is("[3.1]")); + assertThat(LogHelper.toString(floats), is("[4.2]")); + assertThat(LogHelper.toString(bytes), is("[5]")); + assertThat(LogHelper.toString(chars), is("[6]")); + } + + @Test + public void toStringWithSmallCollections() { + List strings = Collections.nCopies(1, "0"); + List ints = Collections.nCopies(1, 1); + List longs = Collections.nCopies(1, 2L); + List doubles = Collections.nCopies(1, 3.1D); + List floats = Collections.nCopies(1, 4.2F); + List bytes = Collections.nCopies(1, (byte)5); + List chars = Collections.nCopies(1, '6'); + + assertThat(LogHelper.toString(strings), is("[0]")); + assertThat(LogHelper.toString(ints), is("[1]")); + assertThat(LogHelper.toString(longs), is("[2]")); + assertThat(LogHelper.toString(doubles), is("[3.1]")); + assertThat(LogHelper.toString(floats), is("[4.2]")); + assertThat(LogHelper.toString(bytes), is("[5]")); + assertThat(LogHelper.toString(chars), is("[6]")); + } + + @Test + public void toStringWithNestedBigArrays() { + String[] strings = new String[15]; + Arrays.fill(strings, "0"); + int[] ints = new int[15]; + Arrays.fill(ints, 1); + long[] longs = new long[15]; + Arrays.fill(longs, 2L); + double[] doubles = new double[15]; + Arrays.fill(doubles, 3.1D); + float[] floats = new float[15]; + Arrays.fill(floats, 4.2F); + byte[] bytes = new byte[15]; + Arrays.fill(bytes, (byte) 5); + char[] chars = new char[15]; + Arrays.fill(chars, '6'); + + Object[] input = new Object[] { strings, ints, longs, doubles, floats, bytes, chars }; + StringBuilder sb = new StringBuilder(); + sb.append("[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...], "); + sb.append("[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...], "); + sb.append("[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...], "); + sb.append("[3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, ...], "); + sb.append("[4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, ...], "); + sb.append("[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ...], "); + sb.append("[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...]]"); + + assertThat(LogHelper.toString(input), is(sb.toString())); + } + + @Test + public void toStringWithNestedBigCollections() { + List strings = Collections.nCopies(15, "0"); + List ints = Collections.nCopies(15, 1); + List longs = Collections.nCopies(15, 2L); + List doubles = Collections.nCopies(15, 3.1D); + List floats = Collections.nCopies(15, 4.2F); + List bytes = Collections.nCopies(15, (byte)5); + List chars = Collections.nCopies(15, '6'); + + Object[] input = new Object[] { strings, ints, longs, doubles, floats, bytes, chars }; + StringBuilder sb = new StringBuilder(); + sb.append("[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...], "); + sb.append("[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...], "); + sb.append("[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...], "); + sb.append("[3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, ...], "); + sb.append("[4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, ...], "); + sb.append("[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ...], "); + sb.append("[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...]]"); + + assertThat(LogHelper.toString(input), is(sb.toString())); + } + + @Test + public void toStringWithBigArrays() { + String[] strings = new String[15]; + Arrays.fill(strings, "0"); + int[] ints = new int[15]; + Arrays.fill(ints, 1); + long[] longs = new long[15]; + Arrays.fill(longs, 2L); + double[] doubles = new double[15]; + Arrays.fill(doubles, 3.1D); + float[] floats = new float[15]; + Arrays.fill(floats, 4.2F); + byte[] bytes = new byte[15]; + Arrays.fill(bytes, (byte) 5); + char[] chars = new char[15]; + Arrays.fill(chars, '6'); + + assertThat(LogHelper.toString(strings), is("[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]")); + assertThat(LogHelper.toString(ints), is("[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...]")); + assertThat(LogHelper.toString(longs), is("[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...]")); + assertThat(LogHelper.toString(doubles), is("[3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, ...]")); + assertThat(LogHelper.toString(floats), is("[4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, ...]")); + assertThat(LogHelper.toString(bytes), is("[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ...]")); + assertThat(LogHelper.toString(chars), is("[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...]")); + } + + @Test + public void toStringWithBigCollections() { + List strings = Collections.nCopies(15, "0"); + List ints = Collections.nCopies(15, 1); + List longs = Collections.nCopies(15, 2L); + List doubles = Collections.nCopies(15, 3.1D); + List floats = Collections.nCopies(15, 4.2F); + List bytes = Collections.nCopies(15, (byte)5); + List chars = Collections.nCopies(15, '6'); + + assertThat(LogHelper.toString(strings), is("[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...]")); + assertThat(LogHelper.toString(ints), is("[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...]")); + assertThat(LogHelper.toString(longs), is("[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...]")); + assertThat(LogHelper.toString(doubles), is("[3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, 3.1, ...]")); + assertThat(LogHelper.toString(floats), is("[4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, 4.2, ...]")); + assertThat(LogHelper.toString(bytes), is("[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, ...]")); + assertThat(LogHelper.toString(chars), is("[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ...]")); + } + + @Test + public void toStringWithSmallString() { + char[] charsForStr = new char[100]; + Arrays.fill(charsForStr, '7'); + String string = new String(charsForStr); + + assertThat(LogHelper.toString(string), is(string)); + } + + @Test + public void toStringWithBigString() { + char[] charsForStr = new char[150]; + Arrays.fill(charsForStr, '7'); + String string = new String(charsForStr); + + assertThat(LogHelper.toString(string), is(string.substring(0, 100) + "...")); + } +}