Re #9: Fixed bugs and added test.

This commit is contained in:
tomasz.kowalczewski committed 2020-12-26 18:00:44 +01:00
1 parent 29f2dd1313
commit b2d5d1e0d4
2 files changed
+191 -35

No files matched your search

@@ -13,6 +13,19 @@ public class LogBufferSerializer {
this.buffer = buffer;
}
public int calculateRequiredSizeAscii(
Map<String, String> labels,
String logLevelLabel,
String logLevel,
ByteBuffer line
) {
int logLineSize = Integer.BYTES + line.remaining();
int logLevelSize = calculateOptionalLabelSizeAscii(logLevelLabel, logLevel);
int headerAndLabelsSize = calculateRequiredSizeAscii(labels);
return headerAndLabelsSize + logLineSize + logLevelSize;
}
public void writeTo(
int cursor,
long timestamp,
@@ -21,16 +34,54 @@ public class LogBufferSerializer {
String logLevel,
ByteBuffer line
) {
cursor = writeHeader(cursor, timestamp, labels);
cursor = writeHeader(
cursor,
timestamp,
labels,
logLevelLabel != null
);
cursor = writeLabels(cursor, labels);
cursor = writeLabel(
cursor = writeOptionalLabel(
cursor,
logLevelLabel,
logLevel
);
writeLogLine(cursor, line);
}
private int writeHeader(
int cursor,
long timestamp,
Map<String, String> labels,
boolean hasLogLevelLabel
) {
buffer.putLong(cursor, timestamp);
cursor += Long.BYTES;
int labelsCount = labels.size();
if (hasLogLevelLabel) {
labelsCount++;
}
buffer.putInt(cursor, labelsCount);
cursor += Integer.BYTES;
return cursor;
}
private int writeLabels(int cursor, Map<String, String> labels) {
for (Map.Entry<String, String> entry : labels.entrySet()) {
cursor = writeLabel(
cursor,
entry.getKey(),
entry.getValue()
);
}
return cursor;
}
private void writeLogLine(int cursor, ByteBuffer line) {
buffer.putInt(cursor, line.remaining());
cursor += Integer.BYTES;
@@ -42,27 +93,7 @@ public class LogBufferSerializer {
);
}
private int writeLabels(int cursor, Map<String, String> labels) {
for (Map.Entry<String, String> entry : labels.entrySet()) {
cursor = writeLabel(
cursor,
entry.getKey(),
entry.getValue()
);
}
return cursor;
}
private int writeHeader(int cursor, long timestamp, Map<String, String> labels) {
buffer.putLong(cursor, timestamp);
cursor += Long.BYTES;
buffer.putInt(cursor, labels.size() + 1);
cursor += Integer.BYTES;
return cursor;
}
public int writeLabel(
private int writeLabel(
int cursor,
String key,
String value
@@ -73,32 +104,42 @@ public class LogBufferSerializer {
return cursor;
}
private int writeOptionalLabel(
int cursor,
String key,
String value
) {
if (key != null) {
cursor += buffer.putStringAscii(cursor, key);
cursor += buffer.putStringAscii(cursor, value);
}
return cursor;
}
private int calculateRequiredSizeAscii(Map<String, String> labels) {
int timestampSize = Long.BYTES;
int labelsCountSize = Integer.BYTES;
int sum = timestampSize + labelsCountSize;
for (Map.Entry<String, String> entry : labels.entrySet()) {
sum += calculateLabeSizeAscii(entry.getKey(), entry.getValue());
sum += calculateLabelSizeAscii(entry.getKey(), entry.getValue());
}
return sum;
}
private int calculateLabeSizeAscii(String key, String value) {
private int calculateOptionalLabelSizeAscii(String key, String value) {
if (key == null) {
return 0;
}
return Integer.BYTES + key.length()
+ Integer.BYTES + value.length();
}
public int calculateRequiredSizeAscii(
Map<String, String> labels,
String logLevelLabel,
String logLevel,
ByteBuffer line
) {
int lineSize = Integer.BYTES + line.remaining();
int logLevelSize = calculateLabeSizeAscii(logLevelLabel, logLevel);
return calculateRequiredSizeAscii(labels) + lineSize + logLevelSize;
private int calculateLabelSizeAscii(String key, String value) {
return Integer.BYTES + key.length()
+ Integer.BYTES + value.length();
}
}
@@ -0,0 +1,115 @@
package pl.tkowalcz.tjahzi;
import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
class LogBufferSerializerTest {
private static Stream<Arguments> variousMessageConfigurations() {
return Stream.of(
Arguments.of(
"Message with all fields filled",
Map.of("ip", "10.0.0.42",
"hostname", "razor-crest",
"region", "outer-rim"
),
"log_level",
"WARN",
ByteBuffer.wrap("[Mando] You have something I want.".getBytes()),
38 // log line
+ 21 // log level
+ 45 // labels string sizes
+ 6 * 4 // labels string length indicators
+ 8 // timestamp
+ 4 // labels count
),
Arguments.of(
"Message with empty labels",
Map.of(),
"log_level",
"WARN",
ByteBuffer.wrap("[Mando] You have something I want.".getBytes()),
38 // log line
+ 21 // log level
+ 8 // timestamp
+ 4 // labels count
),
Arguments.of(
"Message with null log level",
Map.of("ip", "10.0.0.42",
"hostname", "razor-crest",
"region", "outer-rim"
),
null,
null,
ByteBuffer.wrap("[Mando] You have something I want.".getBytes()),
38 // log line
+ 45 // labels string sizes
+ 6 * 4 // labels string length indicators
+ 8 // timestamp
+ 4 // labels count
)
);
}
@ParameterizedTest(name = "{0}")
@MethodSource("variousMessageConfigurations")
void shouldCalculateSizeOfMessage(
String testName,
Map<String, String> labels,
String logLevelLabel,
String logLevel,
ByteBuffer logLine,
int expectedSize) {
// Given
UnsafeBuffer buffer = new UnsafeBuffer(new byte[expectedSize]);
LogBufferSerializer serializer = new LogBufferSerializer(buffer);
// When
int actual = serializer.calculateRequiredSizeAscii(
labels,
logLevelLabel,
logLevel,
logLine
);
// Then
assertThat(actual).isEqualTo(expectedSize);
}
@ParameterizedTest(name = "{0}")
@MethodSource("variousMessageConfigurations")
void shouldSerializeMessage(
String testName,
Map<String, String> labels,
String logLevelLabel,
String logLevel,
ByteBuffer logLine,
int expectedSize) {
// Given
UnsafeBuffer buffer = new UnsafeBuffer(new byte[expectedSize]);
LogBufferSerializer serializer = new LogBufferSerializer(buffer);
// When
serializer.writeTo(
0,
42L,
labels,
logLevelLabel,
logLevel,
logLine
);
// Then
// There was no buffer overflow so seems legit.
// TODO: verify contents after extracting deserialization code to separate class.
}
}