diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java index aaa21b396f..f4d00985cc 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java @@ -35,8 +35,9 @@ import static ch.qos.logback.core.model.ModelConstants.NULL_STR; /** - * http://ndjson.org/ - * https://datatracker.ietf.org/doc/html/rfc8259 + * + * + * http://ndjson.org/ https://datatracker.ietf.org/doc/html/rfc8259 */ public class JsonEncoder extends EncoderBase { static final boolean DO_NOT_ADD_QUOTE_KEY = false; @@ -51,7 +52,6 @@ public class JsonEncoder extends EncoderBase { public static final String BIRTHDATE_ATTR_NAME = "birthdate"; public static final String CONTEXT_PROPERTIES_ATTR_NAME = "properties"; - public static final String TIMESTAMP_ATTR_NAME = "timestamp"; public static final String NANOSECONDS_ATTR_NAME = "nanoseconds"; @@ -66,6 +66,8 @@ public class JsonEncoder extends EncoderBase { public static final String MESSAGE_ATTR_NAME = "message"; + public static final String FORMATTED_MESSAGE_ATTR_NAME = "formattedMessage"; + public static final String ARGUMENT_ARRAY_ATTR_NAME = "arguments"; public static final String KEY_VALUE_PAIRS_ATTR_NAME = "kvpList"; @@ -77,7 +79,6 @@ public class JsonEncoder extends EncoderBase { public static final String SUPPRESSED_ATTR_NAME = "suppressed"; - public static final String COMMON_FRAMES_COUNT_ATTR_NAME = "commonFramesCount"; public static final String CLASS_NAME_ATTR_NAME = "className"; @@ -100,9 +101,27 @@ public class JsonEncoder extends EncoderBase { private static final String QUOTE_COL = "\":"; - private static final char VALUE_SEPARATOR = COMMA_CHAR; + private boolean withSequenceNumber = true; + + private boolean withTimestamp = true; + private boolean withNanoseconds = true; + + + private boolean withLevel = true; + private boolean withThreadName = true; + private boolean withLoggerName = true; + private boolean withContext = true; + private boolean withMarkers = true; + private boolean withMDC = true; + private boolean withKVPList = true; + private boolean withMessage = true; + private boolean withArguments = true; + private boolean withThrowable = true; + private boolean withFormattedMessage = false; + + @Override public byte[] headerBytes() { return EMPTY_BYTES; @@ -114,41 +133,66 @@ public byte[] encode(ILoggingEvent event) { StringBuilder sb = new StringBuilder(initialCapacity); sb.append(OPEN_OBJ); - appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber()); - sb.append(VALUE_SEPARATOR); + if (withSequenceNumber) { + appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber()); + sb.append(VALUE_SEPARATOR); + } - appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp()); - sb.append(VALUE_SEPARATOR); + if (withTimestamp) { + appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp()); + sb.append(VALUE_SEPARATOR); + } - appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds()); - sb.append(VALUE_SEPARATOR); + if (withNanoseconds) { + appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds()); + sb.append(VALUE_SEPARATOR); + } + if (withLevel) { + String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR; + appenderMember(sb, LEVEL_ATTR_NAME, levelStr); + sb.append(VALUE_SEPARATOR); + } - String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR; - appenderMember(sb, LEVEL_ATTR_NAME, levelStr); - sb.append(VALUE_SEPARATOR); + if (withThreadName) { + appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName())); + sb.append(VALUE_SEPARATOR); + } - appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName())); - sb.append(VALUE_SEPARATOR); + if (withLoggerName) { + appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName()); + sb.append(VALUE_SEPARATOR); + } - appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName()); - sb.append(VALUE_SEPARATOR); + if (withContext) { + appendLoggerContext(sb, event.getLoggerContextVO()); + sb.append(VALUE_SEPARATOR); + } + if (withMarkers) + appendMarkers(sb, event); - appendLoggerContext(sb, event.getLoggerContextVO()); - sb.append(VALUE_SEPARATOR); + if (withMDC) + appendMDC(sb, event); - appendMarkers(sb, event); + if (withKVPList) + appendKeyValuePairs(sb, event); - appendMDC(sb, event); + if (withMessage) { + appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); + sb.append(VALUE_SEPARATOR); + } - appendKeyValuePairs(sb, event); + if (withFormattedMessage) { + appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage())); + sb.append(VALUE_SEPARATOR); + } - appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); - sb.append(VALUE_SEPARATOR); + if (withArguments) + appendArgumentArray(sb, event); - appendArgumentArray(sb, event); + if (withThrowable) + appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy()); - appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy()); sb.append(CLOSE_OBJ); sb.append(CoreConstants.JSON_LINE_SEPARATOR); return sb.toString().getBytes(UTF_8_CHARSET); @@ -156,7 +200,6 @@ public byte[] encode(ILoggingEvent event) { private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) { - sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL); if (loggerContextVO == null) { sb.append(NULL_STR); @@ -176,7 +219,7 @@ private void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContext private void appendMap(StringBuilder sb, String attrName, Map map) { sb.append(QUOTE).append(attrName).append(QUOTE_COL); - if(map == null) { + if (map == null) { sb.append(NULL_STR); return; } @@ -185,8 +228,8 @@ private void appendMap(StringBuilder sb, String attrName, Map ma boolean addComma = false; Set> entries = map.entrySet(); - for(Map.Entry entry: entries) { - if(addComma) { + for (Map.Entry entry : entries) { + if (addComma) { sb.append(VALUE_SEPARATOR); } addComma = true; @@ -196,13 +239,11 @@ private void appendMap(StringBuilder sb, String attrName, Map ma sb.append(CLOSE_OBJ); } - private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) { - // in the nominal case, attributeName != null. However, attributeName will be null for suppressed // IThrowableProxy array, in which case no attribute name is needed - if(attributeName != null) { + if (attributeName != null) { sb.append(QUOTE).append(attributeName).append(QUOTE_COL); if (itp == null) { sb.append(NULL_STR); @@ -217,8 +258,7 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow sb.append(VALUE_SEPARATOR); appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(itp.getMessage())); - - if(itp.isCyclic()) { + if (itp.isCyclic()) { sb.append(VALUE_SEPARATOR); appenderMember(sb, CYCLIC_THROWABLE_ATTR_NAME, jsonEscape("true")); } @@ -226,25 +266,25 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow sb.append(VALUE_SEPARATOR); appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames()); - if(itp.getCommonFrames() != 0) { + if (itp.getCommonFrames() != 0) { sb.append(VALUE_SEPARATOR); appenderMemberWithIntValue(sb, COMMON_FRAMES_COUNT_ATTR_NAME, itp.getCommonFrames()); } IThrowableProxy cause = itp.getCause(); - if(cause != null) { + if (cause != null) { sb.append(VALUE_SEPARATOR); appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause); } IThrowableProxy[] suppressedArray = itp.getSuppressed(); - if(suppressedArray != null && suppressedArray.length != 0) { + if (suppressedArray != null && suppressedArray.length != 0) { sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL); sb.append(OPEN_ARRAY); boolean first = true; - for(IThrowableProxy suppressedITP: suppressedArray) { - if(first) { + for (IThrowableProxy suppressedITP : suppressedArray) { + if (first) { first = false; } else { sb.append(VALUE_SEPARATOR); @@ -254,7 +294,6 @@ private void appendThrowableProxy(StringBuilder sb, String attributeName, IThrow sb.append(CLOSE_ARRAY); } - sb.append(CLOSE_OBJ); } @@ -264,7 +303,7 @@ private void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArra int len = stepArray != null ? stepArray.length : 0; - if(commonFrames >= len) { + if (commonFrames >= len) { commonFrames = 0; } @@ -301,6 +340,7 @@ private void appenderMember(StringBuilder sb, String key, String value) { private void appenderMemberWithIntValue(StringBuilder sb, String key, int value) { sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); } + private void appenderMemberWithLongValue(StringBuilder sb, String key, long value) { sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); } @@ -332,7 +372,7 @@ private void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { sb.append(QUOTE).append(ARGUMENT_ARRAY_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); final int len = argumentArray.length; for (int i = 0; i < len; i++) { - if(i != 0) + if (i != 0) sb.append(VALUE_SEPARATOR); sb.append(QUOTE).append(jsonEscapedToString(argumentArray[i])).append(QUOTE); @@ -407,4 +447,73 @@ boolean isNotEmptyMap(Map map) { public byte[] footerBytes() { return EMPTY_BYTES; } + + /** + * @param withSequenceNumber + * @since 1.5.0 + */ + public void setWithSequenceNumber(boolean withSequenceNumber) { + this.withSequenceNumber = withSequenceNumber; + } + + /** + * @param withTimestamp + * @since 1.5.0 + */ + public void setWithTimestamp(boolean withTimestamp) { + this.withTimestamp = withTimestamp; + } + + /** + * @param withNanoseconds + * @since 1.5.0 + */ + public void setWithNanoseconds(boolean withNanoseconds) { + this.withNanoseconds = withNanoseconds; + } + + public void setWithLevel(boolean withLevel) { + this.withLevel = withLevel; + } + + public void setWithThreadName(boolean withThreadName) { + this.withThreadName = withThreadName; + } + + public void setWithLoggerName(boolean withLoggerName) { + this.withLoggerName = withLoggerName; + } + + public void setWithContext(boolean withContext) { + this.withContext = withContext; + } + + public void setWithMarkers(boolean withMarkers) { + this.withMarkers = withMarkers; + } + + public void setWithMDC(boolean withMDC) { + this.withMDC = withMDC; + } + + public void setWithKVPList(boolean withKVPList) { + this.withKVPList = withKVPList; + } + + public void setWithMessage(boolean withMessage) { + this.withMessage = withMessage; + } + + public void setWithArguments(boolean withArguments) { + this.withArguments = withArguments; + } + + public void setWithThrowable(boolean withThrowable) { + this.withThrowable = withThrowable; + } + + public void setWithFormattedMessage(boolean withFormattedMessage) { + this.withFormattedMessage = withFormattedMessage; + } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java index b6e9072e26..2fa22d0299 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java @@ -24,6 +24,7 @@ import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.StringUtil; import org.slf4j.Marker; import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; @@ -225,7 +226,7 @@ private String extractThreadName(Thread aThread) { return CoreConstants.NA; } String threadName = aThread.getName(); - if (threadName != null && !threadName.isEmpty()) + if (StringUtil.notNullNorEmpty(threadName)) return threadName; Long virtualThreadId = getVirtualThreadId(aThread); if (virtualThreadId != null) { diff --git a/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml b/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml new file mode 100644 index 0000000000..58c88a75aa --- /dev/null +++ b/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml @@ -0,0 +1,36 @@ + + + + + + + + target/test-output/json/test-${diff}.json + + true + false + false + false + false + false + + + + + + + + + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java index a7ed08da6a..c71672f168 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java @@ -27,6 +27,7 @@ import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.util.StatusPrinter; import com.fasterxml.jackson.core.JsonProcessingException; @@ -50,7 +51,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; - // When running from an IDE, add the following on the command line // // --add-opens ch.qos.logback.classic/ch.qos.logback.classic.jsonTest=ALL-UNNAMED @@ -60,6 +60,7 @@ class JsonEncoderTest { int diff = RandomUtil.getPositiveInt(); LoggerContext loggerContext = new LoggerContext(); + StatusChecker statusChecker = new StatusChecker(loggerContext); Logger logger = loggerContext.getLogger(JsonEncoderTest.class); JsonEncoder jsonEncoder = new JsonEncoder(); @@ -113,7 +114,7 @@ void contextWithProperties() throws JsonProcessingException { byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); - // System.out.println(resultString); + // System.out.println(resultString); JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); compareEvents(event, resultEvent); @@ -134,6 +135,8 @@ private static void compareEvents(LoggingEvent event, JsonLoggingEvent resultEve assertTrue(ThrowableProxyComparator.areEqual(event.getThrowableProxy(), resultEvent.getThrowableProxy())); assertEquals(event.getMessage(), resultEvent.getMessage()); + assertEquals(event.getFormattedMessage(), resultEvent.getFormattedMessage()); + assertTrue(Arrays.equals(event.getArgumentArray(), resultEvent.getArgumentArray())); } @@ -208,6 +211,20 @@ void withKeyValuePairs() throws JsonProcessingException { compareEvents(event, resultEvent); } + @Test + void withFormattedMessage() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello {} {}", null, + new Object[] { "arg1", "arg2" }); + jsonEncoder.setWithFormattedMessage(true); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + System.out.println(resultString); + + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + @Test void withMDC() throws JsonProcessingException { Map map = new HashMap<>(); @@ -246,8 +263,8 @@ void withThrowableHavingCause() throws JsonProcessingException { Throwable t = new RuntimeException("test", cause); - - LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, null); + LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, + null); byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); @@ -263,7 +280,8 @@ void withThrowableHavingCyclicCause() throws JsonProcessingException { Throwable t = new RuntimeException("test", cause); cause.initCause(t); - LoggingEvent event = new LoggingEvent("in withThrowableHavingCyclicCause test", logger, Level.WARN, "hello kvp", t, null); + LoggingEvent event = new LoggingEvent("in withThrowableHavingCyclicCause test", logger, Level.WARN, "hello kvp", + t, null); byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); @@ -272,7 +290,6 @@ void withThrowableHavingCyclicCause() throws JsonProcessingException { compareEvents(event, resultEvent); } - @Test void withThrowableHavingSuppressed() throws JsonProcessingException { Throwable suppressed = new IllegalStateException("test suppressed"); @@ -280,7 +297,8 @@ void withThrowableHavingSuppressed() throws JsonProcessingException { Throwable t = new RuntimeException("test"); t.addSuppressed(suppressed); - LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, null); + LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, + null); byte[] resultBytes = jsonEncoder.encode(event); String resultString = new String(resultBytes, StandardCharsets.UTF_8); @@ -294,41 +312,63 @@ void configure(String file) throws JoranException { jc.setContext(loggerContext); loggerContext.putProperty("diff", "" + diff); jc.doConfigure(file); - } @Test void withJoran() throws JoranException, IOException { String configFilePathStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoder.xml"; - - configure(configFilePathStr); Logger logger = loggerContext.getLogger(this.getClass().getName()); logger.addAppender(listAppender); - logger.debug("hello"); - logbackMDCAdapter.put("a1", "v1"+diff); - logger.atInfo().addKeyValue("ik"+diff, "iv"+diff).addKeyValue("a", "b").log("bla bla \"x\" foobar"); - logbackMDCAdapter.put("a2", "v2"+diff); + logbackMDCAdapter.put("a1", "v1" + diff); + logger.atInfo().addKeyValue("ik" + diff, "iv" + diff).addKeyValue("a", "b").log("bla bla \"x\" foobar"); + logbackMDCAdapter.put("a2", "v2" + diff); logger.atWarn().addMarker(markerA).setMessage("some warning message").log(); logbackMDCAdapter.remove("a2"); - logger.atError().addKeyValue("ek"+diff, "v"+diff).setCause(new RuntimeException("an error")).log("some error occurred"); + logger.atError().addKeyValue("ek" + diff, "v" + diff).setCause(new RuntimeException("an error")) + .log("some error occurred"); - StatusPrinter.print(loggerContext); + //StatusPrinter.print(loggerContext); - Path configFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX+"json/test-" + diff + ".json"); - List lines = Files.readAllLines(configFilePath); + Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json"); + List lines = Files.readAllLines(outputFilePath); int count = 4; assertEquals(count, lines.size()); - for(int i = 0; i < count; i++) { - System.out.println("i = "+ i); + for (int i = 0; i < count; i++) { + //System.out.println("i = " + i); LoggingEvent withnessEvent = (LoggingEvent) listAppender.list.get(i); JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(lines.get(i)); compareEvents(withnessEvent, resultEvent); } + } + + @Test + void withJoranAndEnabledFormattedMessage() throws JoranException, IOException { + String configFilePathStr = + ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoderAndEnabledFormattedMessage.xml"; + + configure(configFilePathStr); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + + //StatusPrinter.print(loggerContext); + statusChecker.isWarningOrErrorFree(0); + + logger.atError().addKeyValue("ek1", "v1").addArgument("arg1").log("this is {}"); + + Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json"); + List lines = Files.readAllLines(outputFilePath); + + int count = 1; + assertEquals(count, lines.size()); + + String withness = "{\"sequenceNumber\":0,\"level\":\"ERROR\",\"threadName\":\"main\"," + + "\"loggerName\":\"ch.qos.logback.classic.encoder.JsonEncoderTest\",\"mdc\": {}," + + "\"kvpList\": [{\"ek1\":\"v1\"}],\"formattedMessage\":\"this is arg1\",\"throwable\":null}"; + assertEquals(withness, lines.get(0)); } } \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java index 5feb5e90a5..10a9452604 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java @@ -44,7 +44,7 @@ public class JsonLoggingEvent implements ILoggingEvent { public Level level; public String message; - private transient String formattedMessage; + private String formattedMessage; @JsonAlias({"arguments"}) public Object[] argumentArray; diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java index f53fb3194c..75b7ff80e7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java @@ -1,15 +1,13 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.joran.action; @@ -17,8 +15,8 @@ import ch.qos.logback.core.util.AggregationType; /** - * Lump together several fields for use by {@link NestedComplexPropertyIA}. - * + * Lump together several fields for use by {@link ch.qos.logback.core.model.processor.ImplicitModelHandler ImplicitModelHandler}. + * * @author Ceki */ public class ImplicitModelDataForComplexProperty extends ImplicitModelData { diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java index 1974d70259..6f04f5a84a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java @@ -1,2 +1,42 @@ -package ch.qos.logback.core.util;public class StringUtil { +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2024, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +/** + * @since 1.5.0 + */ +public class StringUtil { + + /** + * Returns true if input str is null or empty. + * + * @param str + * @return + */ + public static boolean isNullOrEmpty(String str) { + return ((str == null) || str.isEmpty()); + } + + + /** + * Returns true if input str is not null nor empty. + * + * @param str + * @return + */ + public static boolean notNullNorEmpty(String str) { + return !isNullOrEmpty(str); + } }