From 0334ce26ce9606a0ba14915da60e3ebbd41b7f04 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 2 Jun 2026 09:01:30 +0200 Subject: [PATCH 1/2] #317: Apply small allocation cleanups in parser, logging, and metadata conversion --- doc/changes/changes_18.0.2.md | 1 + .../SchemaMetadataJsonConverter.java | 5 +- .../request/parser/AbstractRequestParser.java | 7 +- .../converter/ResponseJsonConverter.java | 17 ++-- .../com/exasol/logging/CompactFormatter.java | 9 ++- .../request/parser/RequestParserTest.java | 13 ++- .../parser/TablesMetadataParserTest.java | 81 ++++++++++--------- .../exasol/logging/CompactFormatterTest.java | 55 +++++-------- 8 files changed, 99 insertions(+), 89 deletions(-) diff --git a/doc/changes/changes_18.0.2.md b/doc/changes/changes_18.0.2.md index 00a63aaa..6beddc6c 100644 --- a/doc/changes/changes_18.0.2.md +++ b/doc/changes/changes_18.0.2.md @@ -20,6 +20,7 @@ Code name: Improve code quality * #311: Fixed metadata parsing and conversion diagnostics for missing JSON fields and nullable notes/comments, and made `SqlFunctionScalarCast` tolerate a null argument consistently with related SQL AST nodes. * #310: Fixed `SqlStatementSelect.Builder.build()` so empty and partial builders no longer fail with a raw `NullPointerException`. * #309: Fixed the `SqlNodeVisitor` API documentation and parameter names for `SqlPredicateIsNotNull` and `SqlPredicateIsNull`. +* #317: Applied small allocation cleanups in request parsing, logging timestamp formatting, and schema metadata JSON conversion. * #308: Fixed `SqlLimit` so `limit` and `offset` stay non-negative for the full object lifetime by making the node immutable and aligning the validation message with the accepted zero values. * #307: Fixed `PushdownSqlRenderer` so `HASHTYPE` data types include `bytesize` in rendered pushdown SQL JSON. * #312: Fixed logging utility resource and handler lifecycle issues by closing version metadata streams, falling back when the thread context class loader is missing, defaulting absent version properties to `UNKNOWN`, closing replaced root log handlers, and resetting closed remote socket handlers before reuse. diff --git a/src/main/java/com/exasol/adapter/metadata/converter/SchemaMetadataJsonConverter.java b/src/main/java/com/exasol/adapter/metadata/converter/SchemaMetadataJsonConverter.java index 24e35abe..99aabf61 100644 --- a/src/main/java/com/exasol/adapter/metadata/converter/SchemaMetadataJsonConverter.java +++ b/src/main/java/com/exasol/adapter/metadata/converter/SchemaMetadataJsonConverter.java @@ -53,6 +53,7 @@ public final class SchemaMetadataJsonConverter { private static final String COMMENT_KEY = "comment"; private static final String IDENTITY_KEY = "isIdentity"; private static final String DEFAULT_KEY = "default"; + private static final String COLUMNS_KEY = "columns"; private final JsonBuilderFactory factory = Json.createBuilderFactory(Collections.emptyMap()); /** @@ -94,7 +95,7 @@ private JsonObjectBuilder convertTableMetadata(final TableMetadata table) { final JsonObjectBuilder tableBuilder = this.factory.createObjectBuilder(); tableBuilder.add(TYPE_KEY, "table"); tableBuilder.add(TABLE_NAME_KEY, table.getName()); - final JsonArrayBuilder columnsBuilder = Json.createArrayBuilder(); + final JsonArrayBuilder columnsBuilder = this.factory.createArrayBuilder(); for (final ColumnMetadata column : table.getColumns()) { columnsBuilder.add(convertColumnMetadata(column)); } @@ -104,7 +105,7 @@ private JsonObjectBuilder convertTableMetadata(final TableMetadata table) { if (table.hasComment()) { tableBuilder.add(COMMENT_KEY, table.getComment()); } - tableBuilder.add("columns", columnsBuilder); + tableBuilder.add(COLUMNS_KEY, columnsBuilder); return tableBuilder; } diff --git a/src/main/java/com/exasol/adapter/request/parser/AbstractRequestParser.java b/src/main/java/com/exasol/adapter/request/parser/AbstractRequestParser.java index 57f51962..acef10f6 100644 --- a/src/main/java/com/exasol/adapter/request/parser/AbstractRequestParser.java +++ b/src/main/java/com/exasol/adapter/request/parser/AbstractRequestParser.java @@ -2,8 +2,7 @@ import static com.exasol.adapter.request.parser.RequestParserConstants.PROPERTIES_KEY; -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; +import java.io.StringReader; import java.util.*; import java.util.Map.Entry; @@ -27,9 +26,7 @@ class AbstractRequestParser { * @return JSON reader */ protected JsonReader createJsonReader(final String rawRequest) { - final ByteArrayInputStream rawRequestStream = new ByteArrayInputStream( - rawRequest.getBytes(StandardCharsets.UTF_8)); - return Json.createReader(rawRequestStream); + return Json.createReader(new StringReader(rawRequest)); } /** diff --git a/src/main/java/com/exasol/adapter/response/converter/ResponseJsonConverter.java b/src/main/java/com/exasol/adapter/response/converter/ResponseJsonConverter.java index 5f5a3cb8..2859e333 100644 --- a/src/main/java/com/exasol/adapter/response/converter/ResponseJsonConverter.java +++ b/src/main/java/com/exasol/adapter/response/converter/ResponseJsonConverter.java @@ -1,5 +1,7 @@ package com.exasol.adapter.response.converter; +import static java.util.Collections.emptyMap; + import com.exasol.adapter.capabilities.*; import com.exasol.adapter.metadata.converter.SchemaMetadataJsonConverter; import com.exasol.adapter.response.*; @@ -16,6 +18,7 @@ public final class ResponseJsonConverter { private static final String LITERAL_PREFIX = "LITERAL_"; private static final String SCHEMA_METADATA = "schemaMetadata"; private static final ResponseJsonConverter responseJsonConverter = new ResponseJsonConverter(); + private final JsonBuilderFactory factory = Json.createBuilderFactory(emptyMap()); private ResponseJsonConverter() { } @@ -37,7 +40,7 @@ public static ResponseJsonConverter getInstance() { */ @SuppressWarnings("squid:S1172") public String convertDropVirtualSchemaResponse(final DropVirtualSchemaResponse dropResponse) { - return Json.createObjectBuilder() // + return this.factory.createObjectBuilder() // .add("type", "dropVirtualSchema") // .build() // .toString(); @@ -50,7 +53,7 @@ public String convertDropVirtualSchemaResponse(final DropVirtualSchemaResponse d * @return string representation of a JSON Object */ public String convertCreateVirtualSchemaResponse(final CreateVirtualSchemaResponse createResponse) { - return Json.createObjectBuilder() // + return this.factory.createObjectBuilder() // .add("type", "createVirtualSchema") // .add(SCHEMA_METADATA, SchemaMetadataJsonConverter.getInstance().convert(createResponse.getSchemaMetadata())) // @@ -65,7 +68,7 @@ public String convertCreateVirtualSchemaResponse(final CreateVirtualSchemaRespon * @return string representation of a JSON Object */ public String convertPushDownResponse(final PushDownResponse pushDownResponse) { - return Json.createObjectBuilder() // + return this.factory.createObjectBuilder() // .add("type", "pushdown") // .add("sql", pushDownResponse.getPushDownSql()) // .build() // @@ -79,8 +82,8 @@ public String convertPushDownResponse(final PushDownResponse pushDownResponse) { * @return string representation of a JSON Object */ public String convertGetCapabilitiesResponse(final GetCapabilitiesResponse getCapabilitiesResponse) { - final JsonObjectBuilder builder = Json.createObjectBuilder().add("type", "getCapabilities"); - final JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + final JsonObjectBuilder builder = this.factory.createObjectBuilder().add("type", "getCapabilities"); + final JsonArrayBuilder arrayBuilder = this.factory.createArrayBuilder(); final Capabilities capabilities = getCapabilitiesResponse.getCapabilities(); addMainCapabilitiesToBuilder(capabilities, arrayBuilder); addScalarFunctionCapabilitiesToBuilder(capabilities, arrayBuilder); @@ -136,7 +139,7 @@ private void addMainCapabilitiesToBuilder(final Capabilities capabilities, final * @return string representation of a JSON Object */ public String convertRefreshResponse(final RefreshResponse refreshResponse) { - return Json.createObjectBuilder() // + return this.factory.createObjectBuilder() // .add("type", "refresh") // .add(SCHEMA_METADATA, SchemaMetadataJsonConverter.getInstance().convert(refreshResponse.getSchemaMetadata())) // @@ -151,7 +154,7 @@ public String convertRefreshResponse(final RefreshResponse refreshResponse) { * @return string representation of a JSON Object */ public String convertSetPropertiesResponse(final SetPropertiesResponse setPropertiesResponse) { - final JsonObjectBuilder builder = Json.createObjectBuilder(); + final JsonObjectBuilder builder = this.factory.createObjectBuilder(); builder.add("type", "setProperties"); if (setPropertiesResponse.getSchemaMetadata() != null) { builder.add(SCHEMA_METADATA, diff --git a/src/main/java/com/exasol/logging/CompactFormatter.java b/src/main/java/com/exasol/logging/CompactFormatter.java index d1fa0ea7..48d32da4 100644 --- a/src/main/java/com/exasol/logging/CompactFormatter.java +++ b/src/main/java/com/exasol/logging/CompactFormatter.java @@ -3,7 +3,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.time.Instant; -import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -13,7 +13,8 @@ */ public class CompactFormatter extends Formatter { private static final String LOG_LEVEL_FORMAT = "%-8s"; - private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS") + .withZone(ZoneOffset.UTC); /** * Formats a log record according in a compact manner. @@ -61,7 +62,7 @@ private void appendNonEmptyClassName(final String className, final StringBuilder private String formatTimestamp(final long millis) { final Instant instant = Instant.ofEpochMilli(millis); - return this.dateTimeFormatter.format(instant.atZone(ZoneId.of("Z"))); + return this.dateTimeFormatter.format(instant); } private void formatException(final LogRecord record, final StringBuilder builder) { @@ -73,4 +74,4 @@ private void formatException(final LogRecord record, final StringBuilder builder builder.append(errors); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/exasol/adapter/request/parser/RequestParserTest.java b/src/test/java/com/exasol/adapter/request/parser/RequestParserTest.java index 25d4abab..c11c1c92 100644 --- a/src/test/java/com/exasol/adapter/request/parser/RequestParserTest.java +++ b/src/test/java/com/exasol/adapter/request/parser/RequestParserTest.java @@ -64,6 +64,17 @@ void setPropertiesRequest() { () -> assertThat(properties, hasEntry(equalTo("NULL_value"), equalTo(null)))); } + @Test + void setPropertiesRequestPreservesUtf8Characters() { + final String rawRequest = JsonEntry.object( + entry("type", "setProperties"), + entry("properties", object(entry("CITY", "München"))), + SCHEMA_METADATA_INFO).render(); + final AdapterRequest request = this.parser.parse(rawRequest); + final Map properties = ((SetPropertiesRequest) request).getProperties(); + assertThat(properties, hasEntry(equalTo("CITY"), equalTo("München"))); + } + @Test void unsupportedPropertyType() { final String rawRequest = JsonEntry.object(// @@ -157,4 +168,4 @@ private JsonParent createPushDownRequest(final JsonEntry selectListDataTypes) { )))))))), // SCHEMA_METADATA_INFO); } -} \ No newline at end of file +} diff --git a/src/test/java/com/exasol/adapter/request/parser/TablesMetadataParserTest.java b/src/test/java/com/exasol/adapter/request/parser/TablesMetadataParserTest.java index bd8013bf..a62469e0 100644 --- a/src/test/java/com/exasol/adapter/request/parser/TablesMetadataParserTest.java +++ b/src/test/java/com/exasol/adapter/request/parser/TablesMetadataParserTest.java @@ -10,8 +10,7 @@ import java.io.*; import java.nio.charset.Charset; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.stream.Stream; import org.junit.jupiter.api.Test; @@ -24,6 +23,8 @@ import jakarta.json.*; class TablesMetadataParserTest { + private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap()); + @Test void testParseMetadata() throws IOException { final List tableColumns = new ArrayList<>(); @@ -126,17 +127,25 @@ private List createExpectedTableMetadata() { return expectedInvolvedTablesMetadata; } + private static JsonArrayBuilder arrayBuilder() { + return JSON.createArrayBuilder(); + } + + private static JsonObjectBuilder objectBuilder() { + return JSON.createObjectBuilder(); + } + @Test void testParseColumnMetadataUsesBooleanDefaults() { - final JsonArray tablesAsJson = Json.createArrayBuilder().add(Json.createObjectBuilder() + final JsonArray tablesAsJson = arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("columns", Json.createArrayBuilder() - .add(Json.createObjectBuilder().add("name", "DEFAULTS") - .add("dataType", Json.createObjectBuilder().add("type", "DECIMAL").add("precision", 18) + .add("columns", arrayBuilder() + .add(objectBuilder().add("name", "DEFAULTS") + .add("dataType", objectBuilder().add("type", "DECIMAL").add("precision", 18) .add("scale", 0))) - .add(Json.createObjectBuilder().add("name", "EXPLICIT_VALUES").add("isNullable", false) + .add(objectBuilder().add("name", "EXPLICIT_VALUES").add("isNullable", false) .add("isIdentity", true) - .add("dataType", Json.createObjectBuilder().add("type", "DECIMAL").add("precision", 18) + .add("dataType", objectBuilder().add("type", "DECIMAL").add("precision", 18) .add("scale", 0))))) .build(); @@ -155,18 +164,18 @@ void testParseColumnMetadataUsesBooleanDefaults() { @Test void testParseMetadataReadsAdapterNotes() { - final JsonArray tablesAsJson = Json.createArrayBuilder().add(Json.createObjectBuilder() + final JsonArray tablesAsJson = arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("adapterNotes", Json.createObjectBuilder().add("table", true)) - .add("columns", Json.createArrayBuilder() - .add(Json.createObjectBuilder().add("name", "C1") + .add("adapterNotes", objectBuilder().add("table", true)) + .add("columns", arrayBuilder() + .add(objectBuilder().add("name", "C1") .add("adapterNotes", "column notes") - .add("dataType", Json.createObjectBuilder().add("type", "BOOLEAN"))) - .add(Json.createObjectBuilder().add("name", "C2") - .add("adapterNotes", Json.createObjectBuilder().add("nested", 1)) - .add("dataType", Json.createObjectBuilder().add("type", "DATE"))) - .add(Json.createObjectBuilder().add("name", "C3") - .add("dataType", Json.createObjectBuilder().add("type", "DOUBLE"))))) + .add("dataType", objectBuilder().add("type", "BOOLEAN"))) + .add(objectBuilder().add("name", "C2") + .add("adapterNotes", objectBuilder().add("nested", 1)) + .add("dataType", objectBuilder().add("type", "DATE"))) + .add(objectBuilder().add("name", "C3") + .add("dataType", objectBuilder().add("type", "DOUBLE"))))) .build(); final List tables = TablesMetadataParser.create().parse(tablesAsJson); @@ -193,50 +202,50 @@ void testParseMetadataThrowsException(final JsonArray tablesAsJson, private static Stream invalidTableMetadata() { return Stream.of( - Arguments.of(Json.createArrayBuilder().add(Json.createObjectBuilder().add("name", "T1")).build(), + Arguments.of(arrayBuilder().add(objectBuilder().add("name", "T1")).build(), "E-VSCOMJAVA-44: Failed to parse 'table 'T1'' because mandatory field 'columns' is missing."), - Arguments.of(Json.createArrayBuilder().add(Json.createObjectBuilder() + Arguments.of(arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("columns", Json.createArrayBuilder() - .add(Json.createObjectBuilder() - .add("dataType", Json.createObjectBuilder().add("type", "DECIMAL") + .add("columns", arrayBuilder() + .add(objectBuilder() + .add("dataType", objectBuilder().add("type", "DECIMAL") .add("precision", 18).add("scale", 0))))) .build(), "E-VSCOMJAVA-44: Failed to parse 'column #0 of table 'T1'' because mandatory field 'name' is missing."), - Arguments.of(Json.createArrayBuilder().add(Json.createObjectBuilder() + Arguments.of(arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("columns", Json.createArrayBuilder().add(Json.createObjectBuilder().add("name", "C1")))) + .add("columns", arrayBuilder().add(objectBuilder().add("name", "C1")))) .build(), "E-VSCOMJAVA-44: Failed to parse 'column 'C1' of table 'T1'' because mandatory field 'dataType' is missing."), - Arguments.of(Json.createArrayBuilder().add(Json.createObjectBuilder() + Arguments.of(arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("columns", Json.createArrayBuilder() - .add(Json.createObjectBuilder().add("name", "C1") + .add("columns", arrayBuilder() + .add(objectBuilder().add("name", "C1") .add("dataType", - Json.createObjectBuilder().add("precision", 18).add("scale", 0))))) + objectBuilder().add("precision", 18).add("scale", 0))))) .build(), "E-VSCOMJAVA-44: Failed to parse 'column 'C1' of table 'T1' data type' because mandatory field 'type' is missing."), // Unsupported data types Arguments.of(createSingleColumnTable("VARCHAR", - Json.createObjectBuilder().add("size", 10).add("characterSet", "INVALID").build()), + objectBuilder().add("size", 10).add("characterSet", "INVALID").build()), "E-VSCOMJAVA-19: Unsupported charset encountered: 'INVALID'."), Arguments.of(createSingleColumnTable("INTERVAL", - Json.createObjectBuilder().add("fromTo", "CENTURY TO MINUTE").build()), + objectBuilder().add("fromTo", "CENTURY TO MINUTE").build()), "E-VSCOMJAVA-20: Unsupported interval data type encountered: 'CENTURY TO MINUTE'."), - Arguments.of(createSingleColumnTable("UNSUPPORTED_TYPE", Json.createObjectBuilder().build()), + Arguments.of(createSingleColumnTable("UNSUPPORTED_TYPE", objectBuilder().build()), "E-VSCOMJAVA-18: Unsupported data type encountered: 'UNSUPPORTED_TYPE'.")); } private static JsonArray createSingleColumnTable(final String type, final JsonObject typeAttributes) { - final JsonObjectBuilder dataType = Json.createObjectBuilder().add("type", type); + final JsonObjectBuilder dataType = objectBuilder().add("type", type); for (final String key : typeAttributes.keySet()) { dataType.add(key, typeAttributes.get(key)); } - return Json.createArrayBuilder().add(Json.createObjectBuilder() + return arrayBuilder().add(objectBuilder() .add("name", "T1") - .add("columns", Json.createArrayBuilder() - .add(Json.createObjectBuilder().add("name", "C1").add("dataType", dataType)))) + .add("columns", arrayBuilder() + .add(objectBuilder().add("name", "C1").add("dataType", dataType)))) .build(); } } diff --git a/src/test/java/com/exasol/logging/CompactFormatterTest.java b/src/test/java/com/exasol/logging/CompactFormatterTest.java index 80e4e8e4..e76f8e68 100644 --- a/src/test/java/com/exasol/logging/CompactFormatterTest.java +++ b/src/test/java/com/exasol/logging/CompactFormatterTest.java @@ -3,14 +3,19 @@ import static com.exasol.logging.RemoteLogManagerTest.*; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.text.MatchesPattern.matchesPattern; import static org.junit.jupiter.api.Assertions.assertAll; import java.util.logging.Level; import java.util.logging.LogRecord; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class CompactFormatterTest { private LogRecord record; @@ -28,45 +33,27 @@ void testFormat() { } @Test - void testFormatWithEmptyClass() { - this.record.setSourceClassName(""); + void testFormatUsesUtcTimestamp() { + this.record.setMillis(0); final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +message")); - } - - @Test - void testFormatWithSimpleClass() { - this.record.setSourceClassName("example"); - final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +\\[example\\] +message")); - } - - @Test - void testFormatWithFullClass() { - this.record.setSourceClassName("com.exasol.example"); - final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +\\[c\\.e\\.example\\] +message")); - } - - @Test - void testFormatRobustAgainstDoubleDot() { - this.record.setSourceClassName("com.exasol..example"); - final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +\\[c\\.e\\.\\.example\\] +message")); + assertThat(formattedRecord, startsWith("1970-01-01 00:00:00.000 SEVERE")); } - @Test - void testFormatRobustAgainstEndDot() { - this.record.setSourceClassName("com.exasol."); + @ParameterizedTest + @MethodSource("classNameFormats") + void testFormatWithClassName(final String sourceClassName, final String expectedContent) { + this.record.setSourceClassName(sourceClassName); final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +\\[c\\.e\\.\\] +message")); + assertThat(formattedRecord, matchesTimeStamp(expectedContent)); } - @Test - void testFormatRobustAgainstOnlyDot() { - this.record.setSourceClassName("."); - final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, matchesTimeStamp(" SEVERE +\\[\\.\\] +message")); + private static Stream classNameFormats() { + return Stream.of(Arguments.of("", " SEVERE +message"), + Arguments.of("example", " SEVERE +\\[example\\] +message"), + Arguments.of("com.exasol.example", " SEVERE +\\[c\\.e\\.example\\] +message"), + Arguments.of("com.exasol..example", " SEVERE +\\[c\\.e\\.\\.example\\] +message"), + Arguments.of("com.exasol.", " SEVERE +\\[c\\.e\\.\\] +message"), + Arguments.of(".", " SEVERE +\\[\\.\\] +message")); } @Test @@ -90,4 +77,4 @@ void testFormatException() { () -> assertThat(formattedRecord, containsString("the exception")), () -> assertThat(formattedRecord, containsString("the cause"))); } -} \ No newline at end of file +} From 029e14d1641f2952a71bbbab7402f94739d6bff2 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 2 Jun 2026 09:04:03 +0200 Subject: [PATCH 2/2] Fix deprecation warning --- .../com/exasol/logging/CompactFormatter.java | 16 ++++++------ .../exasol/logging/CompactFormatterTest.java | 25 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/exasol/logging/CompactFormatter.java b/src/main/java/com/exasol/logging/CompactFormatter.java index 48d32da4..5347d2c3 100644 --- a/src/main/java/com/exasol/logging/CompactFormatter.java +++ b/src/main/java/com/exasol/logging/CompactFormatter.java @@ -25,14 +25,14 @@ public class CompactFormatter extends Formatter { * yyyy-MM-dd HH:mm:ss.SSS LEVEL [c.e.ClassName] The message. */ @Override - public String format(final LogRecord record) { + public String format(final LogRecord logRecord) { final StringBuilder builder = new StringBuilder(); - builder.append(formatTimestamp(record.getMillis())); + builder.append(formatTimestamp(logRecord.getMillis())); builder.append(" "); - builder.append(String.format(LOG_LEVEL_FORMAT, record.getLevel())); - appendClassName(record.getSourceClassName(), builder); - builder.append(formatMessage(record)); - formatException(record, builder); + builder.append(String.format(LOG_LEVEL_FORMAT, logRecord.getLevel())); + appendClassName(logRecord.getSourceClassName(), builder); + builder.append(formatMessage(logRecord)); + formatException(logRecord, builder); builder.append(System.lineSeparator()); return builder.toString(); } @@ -65,8 +65,8 @@ private String formatTimestamp(final long millis) { return this.dateTimeFormatter.format(instant); } - private void formatException(final LogRecord record, final StringBuilder builder) { - final Throwable thrown = record.getThrown(); + private void formatException(final LogRecord logRecord, final StringBuilder builder) { + final Throwable thrown = logRecord.getThrown(); if (thrown != null) { builder.append("\n\t"); final StringWriter errors = new StringWriter(); diff --git a/src/test/java/com/exasol/logging/CompactFormatterTest.java b/src/test/java/com/exasol/logging/CompactFormatterTest.java index e76f8e68..57dbe47a 100644 --- a/src/test/java/com/exasol/logging/CompactFormatterTest.java +++ b/src/test/java/com/exasol/logging/CompactFormatterTest.java @@ -1,12 +1,13 @@ package com.exasol.logging; import static com.exasol.logging.RemoteLogManagerTest.*; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.text.MatchesPattern.matchesPattern; import static org.junit.jupiter.api.Assertions.assertAll; +import java.time.Instant; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.stream.Stream; @@ -18,32 +19,32 @@ import org.junit.jupiter.params.provider.MethodSource; class CompactFormatterTest { - private LogRecord record; + private LogRecord logRecord; private final CompactFormatter formatter = new CompactFormatter(); @BeforeEach void beforeEach() { - this.record = new LogRecord(Level.SEVERE, "message"); + this.logRecord = new LogRecord(Level.SEVERE, "message"); } @Test void testFormat() { - final String formattedRecord = this.formatter.format(this.record); + final String formattedRecord = this.formatter.format(this.logRecord); assertThat(formattedRecord, matchesTimeStamp(" SEVERE +message")); } @Test void testFormatUsesUtcTimestamp() { - this.record.setMillis(0); - final String formattedRecord = this.formatter.format(this.record); - assertThat(formattedRecord, startsWith("1970-01-01 00:00:00.000 SEVERE")); + this.logRecord.setInstant(Instant.ofEpochMilli(0)); + final String formattedRecord = this.formatter.format(this.logRecord); + assertThat(formattedRecord, equalTo("1970-01-01 00:00:00.000 SEVERE message\n")); } @ParameterizedTest @MethodSource("classNameFormats") void testFormatWithClassName(final String sourceClassName, final String expectedContent) { - this.record.setSourceClassName(sourceClassName); - final String formattedRecord = this.formatter.format(this.record); + this.logRecord.setSourceClassName(sourceClassName); + final String formattedRecord = this.formatter.format(this.logRecord); assertThat(formattedRecord, matchesTimeStamp(expectedContent)); } @@ -68,9 +69,9 @@ void testFormatWithPlaceholders() { void testFormatException() { final IllegalStateException cause = new IllegalStateException("the cause"); final IllegalArgumentException exception = new IllegalArgumentException("the exception", cause); - this.record.setMessage("the message"); - this.record.setThrown(exception); - final String formattedRecord = this.formatter.format(this.record); + this.logRecord.setMessage("the message"); + this.logRecord.setThrown(exception); + final String formattedRecord = this.formatter.format(this.logRecord); assertAll( () -> assertThat(formattedRecord, matchesPattern(TIMESTAMP_PATTERN + " SEVERE the message\\n(.*" + LINEFEED_PATTERN + ")*")),