diff options
author | FrancescoFioraEst <francesco.fiora@est.tech> | 2024-11-28 11:15:38 +0000 |
---|---|---|
committer | Francesco Fiora <francesco.fiora@est.tech> | 2024-12-02 17:02:53 +0000 |
commit | cd58a583081acf3cbfa6caa3b59143a7b6e7669c (patch) | |
tree | 7f586d2d0c390d7825045d3ee669272de3ac7347 /runtime-acm | |
parent | 979045f0272b025b02865acf0372cef72e05c6b1 (diff) |
Convert log in json format in ACM-runtime
Issue-ID: POLICY-5147
Change-Id: If87acc597dfc6cffb6bfc69a70a620a00a991536
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
Diffstat (limited to 'runtime-acm')
3 files changed, 317 insertions, 0 deletions
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayout.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayout.java new file mode 100644 index 000000000..d80ff0295 --- /dev/null +++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayout.java @@ -0,0 +1,124 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.runtime.config; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.ThrowableProxyUtil; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.LayoutBase; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.Map; +import lombok.Setter; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; + +public class LoggingConsoleLayout extends LayoutBase<ILoggingEvent> { + + private static final String DEFAULT_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS+00:00"; + + @Setter + private String timestampFormat = DEFAULT_FORMAT; + + @Setter + private String timestampFormatTimezoneId = ""; + + @Setter + private String staticParameters = ""; + + private final Map<String, String> staticParameterMap = new HashMap<>(); + + private final Coder coder = new StandardCoder(); + + private DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DEFAULT_FORMAT); + + @Override + public void start() { + super.start(); + extractParameters(); + if (timestampFormat != null && !timestampFormat.isBlank()) { + formatter = extractDateTimeFormatter(); + } + } + + private void extractParameters() { + staticParameterMap.clear(); + if (staticParameters == null || staticParameters.isBlank()) { + return; + } + var split = staticParameters.split("\\|"); + for (var str : split) { + var s = str.split("="); + if (s.length == 2) { + staticParameterMap.put(s[0], s[1]); + } + } + } + + private DateTimeFormatter extractDateTimeFormatter() { + try { + var dtf = DateTimeFormatter.ofPattern(timestampFormat); + if (timestampFormatTimezoneId != null && !timestampFormatTimezoneId.isBlank()) { + dtf = dtf.withZone(ZoneId.of(timestampFormatTimezoneId)); + } + return dtf; + } catch (RuntimeException e) { + return DateTimeFormatter.ofPattern(DEFAULT_FORMAT); + } + } + + @Override + public String doLayout(ILoggingEvent event) { + Map<String, Object> map = new HashMap<>(); + map.put("timestamp", getTimestamp(event.getInstant())); + map.put("severity", event.getLevel().toString()); + map.put("message", event.getFormattedMessage()); + Map<String, String> extraDatamap = new HashMap<>(); + extraDatamap.put("logger", event.getLoggerName()); + extraDatamap.put("thread", event.getThreadName()); + var throwableProxy = event.getThrowableProxy(); + if (throwableProxy != null) { + extraDatamap.put("exception", ThrowableProxyUtil.asString(throwableProxy)); + } + map.put("extra_data", extraDatamap); + map.putAll(staticParameterMap); + return getJson(map); + } + + private String getJson(Map<String, Object> map) { + try { + return coder.encode(map) + CoreConstants.LINE_SEPARATOR; + } catch (CoderException e) { + return map.get("message").toString(); + } + } + + private String getTimestamp(Instant instant) { + try { + return formatter.format(instant); + } catch (RuntimeException e) { + return instant.toString(); + } + } +} diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayoutTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayoutTest.java new file mode 100644 index 000000000..d4f4c3ec0 --- /dev/null +++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/config/LoggingConsoleLayoutTest.java @@ -0,0 +1,153 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.acm.runtime.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.LoggerContextVO; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; +import java.util.Map; +import lombok.Data; +import org.junit.jupiter.api.Test; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; + +class LoggingConsoleLayoutTest { + + private static final String FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSX"; + + private static final Coder CODER = new StandardCoder(); + + @Data + private static class DummyEvent implements ILoggingEvent { + + private String threadName = "main"; + private Level level = Level.INFO; + private String message = "{\"key\": \"value\"}"; + private Object[] argumentArray; + private String loggerName = LoggingConsoleLayoutTest.class.getCanonicalName(); + private LoggerContextVO loggerContextVO; + private IThrowableProxy throwableProxy; + private StackTraceElement[] callerData; + private List<Marker> markerList; + private Map<String, String> mdc; + private long timeStamp = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + private int nanoseconds; + private long sequenceNumber; + private List<KeyValuePair> keyValuePairs; + + @Override + public void prepareForDeferredProcessing() { + // dummy implementation + } + + @Override + public boolean hasCallerData() { + return false; + } + + @Override + public Map<String, String> getMDCPropertyMap() { + return Map.of(); + } + + @Override + public String getFormattedMessage() { + return message; + } + } + + @Test + void testLog() throws CoderException { + var layout = new LoggingConsoleLayout(); + var event = new DummyEvent(); + + // start() not called + testingResult(layout, event); + + // start() with default data format + layout.start(); + testingResult(layout, event); + + // TimestampFormat + layout.setTimestampFormat(FORMAT); + layout.start(); + testingResult(layout, event); + + // wrong format + layout.setTimestampFormat("wrong Timestamp"); + layout.setStaticParameters("wrong Parameter"); + layout.start(); + testingResult(layout, event); + + // TimestampFormat and TimestampFormatTimezoneId + layout.setTimestampFormat(FORMAT); + layout.setTimestampFormatTimezoneId("UTC"); + layout.setStaticParameters("service_id=policy-acm|application_id=policy-acm"); + layout.start(); + testingResult(layout, event); + + // null TimestampFormat + layout.setTimestampFormat(null); + layout.setStaticParameters(null); + layout.start(); + testingResult(layout, event); + + // blank TimestampFormat + layout.setTimestampFormat(""); + layout.setStaticParameters(""); + layout.start(); + testingResult(layout, event); + + // null TimestampFormatTimezoneId + layout.setTimestampFormat(FORMAT); + layout.setTimestampFormatTimezoneId(null); + layout.start(); + testingResult(layout, event); + + // blank TimestampFormatTimezoneId + layout.setTimestampFormat(FORMAT); + layout.setTimestampFormatTimezoneId(""); + layout.start(); + testingResult(layout, event); + } + + private void testingResult(LoggingConsoleLayout layout, DummyEvent event) throws CoderException { + var result = layout.doLayout(event); + assertThat(result).isNotNull(); + var map = CODER.decode(result, Map.class); + assertEquals(event.level.toString(), map.get("severity")); + assertEquals(event.message, map.get("message")); + @SuppressWarnings("unchecked") + var extraData = (Map<String, String>) map.get("extra_data"); + assertEquals(event.loggerName, extraData.get("logger")); + assertEquals(event.threadName, extraData.get("thread")); + } +} diff --git a/runtime-acm/src/test/resources/logback.xml b/runtime-acm/src/test/resources/logback.xml new file mode 100644 index 000000000..b8934df2d --- /dev/null +++ b/runtime-acm/src/test/resources/logback.xml @@ -0,0 +1,40 @@ +<!-- + ============LICENSE_START======================================================= + Copyright (C) 2024 Nordix Foundation. + ================================================================================ + 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. + + SPDX-License-Identifier: Apache-2.0 + ============LICENSE_END========================================================= + --> +<configuration scan="true" scanPeriod="30 seconds" debug="false"> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> + <layout class="org.onap.policy.clamp.acm.runtime.config.LoggingConsoleLayout"> + <timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSS+00:00</timestampFormat> + <timestampFormatTimezoneId>UTC</timestampFormatTimezoneId> + <staticParameters>version=1.2.0|service_id=policy-acm|application_id=policy-acm</staticParameters> + </layout> + </encoder> + </appender> + + <logger name="network" level="INFO" additivity="false"> + <appender-ref ref="STDOUT" /> + </logger> + + <root level="INFO"> + <appender-ref ref="STDOUT" /> + </root> + +</configuration> |