Commit e25efd11 authored by Daniel Scholl's avatar Daniel Scholl
Browse files

Merge branch 'kibattul/ind/7466583' into 'master'

Kibattul/ind/7466583

See merge request !11
parents 2430d823 36464f36
Pipeline #4093 passed with stages
in 4 minutes and 30 seconds
......@@ -24,6 +24,7 @@ The following software have components provided under the terms of this license:
- Apache Log4j API (from )
- Apache Log4j Core (from )
- Apache Log4j JUL Adapter (from )
- Apache Log4j SLF4J Binding (from )
- Apache Log4j to SLF4J Adapter (from )
- Asynchronous Http Client (from )
- Asynchronous Http Client Netty Utils (from )
......
......@@ -21,7 +21,7 @@
<groupId>org.opengroup.osdu</groupId>
<artifactId>core-lib-azure</artifactId>
<packaging>jar</packaging>
<version>0.0.10</version>
<version>0.0.11</version>
<name>core-lib-azure</name>
<properties>
......@@ -79,7 +79,7 @@
<dependency>
<groupId>org.opengroup.osdu</groupId>
<artifactId>os-core-common</artifactId>
<version>0.0.13</version>
<version>0.3.4</version>
</dependency>
<!-- Azure dependencies -->
......@@ -124,12 +124,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.2.4.RELEASE</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
......
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.filters;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* For logging start and end of request.
*/
@Component
@ConditionalOnProperty(value = "logging.transaction.enabled", havingValue = "true", matchIfMissing = false)
public final class TransactionLogFilter implements Filter {
private static final String LOGGER_NAME = "TxnLogger";
private static final List<String> WHITELIST_HEADERS = Arrays.asList(
DpsHeaders.CORRELATION_ID,
DpsHeaders.DATA_PARTITION_ID,
DpsHeaders.CONTENT_TYPE);
private static final String START_LOG_TEMPLATE = "Start Web-API %s %s %s";
private static final String END_LOG_TEMPLATE = "End Web-API %s %s %s status=%d time=%d ms";
@Autowired
private JaxRsDpsLog jaxRsDpsLog;
/**
* Filter logic.
* @param servletRequest Request object.
* @param servletResponse Response object.
* @param filterChain Filter Chain object.
* @throws IOException
* @throws ServletException
*/
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
logTransactionStart(httpRequest);
final long start = System.currentTimeMillis();
filterChain.doFilter(servletRequest, servletResponse);
final long timeTaken = System.currentTimeMillis() - start;
logTransactionEnd(httpRequest, httpResponse, timeTaken);
}
/**
* Logs start of a request.
* @param request Request Object.
*/
private void logTransactionStart(final HttpServletRequest request) {
jaxRsDpsLog.info(LOGGER_NAME, String.format(START_LOG_TEMPLATE, request.getMethod(), request.getServletPath(),
getRequestHeadersString(request)));
}
/**
* Logs end of a request.
* @param request Request object.
* @param response Response object.
* @param timeTaken timeTaken in ms taken for request to complete.
*/
private void logTransactionEnd(final HttpServletRequest request, final HttpServletResponse response,
final long timeTaken) {
jaxRsDpsLog.info(LOGGER_NAME, String.format(END_LOG_TEMPLATE, request.getMethod(), request.getServletPath(),
getResponseHeadersString(response), response.getStatus(), timeTaken));
}
/**
* To construct string representation of request headers.
* @param request Request Object.
* @return String representation of request headers.
*/
private String getRequestHeadersString(final HttpServletRequest request) {
return getHeadersString(request::getHeader);
}
/**
* To construct string representation of response headers.
* @param response Response Object.
* @return String representation of response headers.
*/
private String getResponseHeadersString(final HttpServletResponse response) {
return getHeadersString(response::getHeader);
}
/**
* Construct string representation of headers.
* @param headerGetter Header value supplier
* @return String representation of headers
*/
private String getHeadersString(final Function<String, String> headerGetter) {
final StringBuilder headers = new StringBuilder();
for (String headerName: WHITELIST_HEADERS) {
if (headerGetter.apply(headerName) != null) {
headers.append(headerName);
headers.append(":");
headers.append(headerGetter.apply(headerName));
headers.append(",");
}
}
if (headers.length() != 0) {
headers.deleteCharAt(headers.length() - 1);
}
return String.format("Headers: {%s}", headers.toString());
}
}
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.logging;
import org.opengroup.osdu.core.common.logging.ILogger;
import org.opengroup.osdu.core.common.logging.audit.AuditPayload;
import org.opengroup.osdu.core.common.model.http.HeadersToLog;
import org.opengroup.osdu.core.common.model.http.Request;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* {@link ILogger} implementation with Slf4J Logger.
*/
@Component
@Primary
@ConditionalOnProperty(value = "logging.slf4jlogger.enabled", havingValue = "true", matchIfMissing = false)
public final class Slf4JLogger implements ILogger {
private static final String DEFAULT_LOGGER_NAME = Slf4JLogger.class.getName();
@Autowired
private HeadersToLog headersToLog;
@Autowired
private Slf4jLoggerFactory slf4jLoggerFactory;
@Override
public void audit(final String logPrefix, final AuditPayload auditPayload, final Map<String, String> headers) {
this.audit(DEFAULT_LOGGER_NAME, logPrefix, auditPayload, headers);
}
@Override
public void audit(final String loggerName, final String logPrefix, final AuditPayload payload, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).info("{} {} {}", logPrefix, payload,
this.headersToLog.createStandardLabelsFromMap(headers));
}
@Override
public void request(final String logPrefix, final Request request, final Map<String, String> headers) {
this.request(DEFAULT_LOGGER_NAME, logPrefix, request, headers);
}
@Override
public void request(final String loggerName, final String logPrefix, final Request request, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).info("{} {} {}", logPrefix, request,
this.headersToLog.createStandardLabelsFromMap(headers));
}
@Override
public void info(final String logPrefix, final String message, final Map<String, String> headers) {
this.info(DEFAULT_LOGGER_NAME, logPrefix, message, headers);
}
@Override
public void info(final String loggerName, final String logPrefix, final String message, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).info("{} {} {}", logPrefix, message,
this.headersToLog.createStandardLabelsFromMap(headers));
}
@Override
public void warning(final String logPrefix, final String message, final Map<String, String> headers) {
this.warning(DEFAULT_LOGGER_NAME, logPrefix, message, headers);
}
@Override
public void warning(final String loggerName, final String logPrefix, final String message, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).warn("{} {} {}", logPrefix, message,
this.headersToLog.createStandardLabelsFromMap(headers));
}
@Override
public void warning(final String logPrefix, final String message, final Exception e, final Map<String, String> headers) {
this.warning(DEFAULT_LOGGER_NAME, logPrefix, message, e, headers);
}
@Override
public void warning(final String loggerName, final String logPrefix, final String message, final Exception ex, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).warn("{} {} {}", logPrefix, message,
this.headersToLog.createStandardLabelsFromMap(headers), ex);
}
@Override
public void error(final String logPrefix, final String message, final Map<String, String> headers) {
this.error(DEFAULT_LOGGER_NAME, logPrefix, message, headers);
}
@Override
public void error(final String loggerName, final String logPrefix, final String message, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).error("{} {} {}", logPrefix, message,
this.headersToLog.createStandardLabelsFromMap(headers));
}
@Override
public void error(final String logPrefix, final String message, final Exception e,
final Map<String, String> headers) {
this.error(DEFAULT_LOGGER_NAME, logPrefix, message, e, headers);
}
@Override
public void error(final String loggerName, final String logPrefix, final String message, final Exception ex, final Map<String, String> headers) {
slf4jLoggerFactory.getLogger(loggerName).error("{} {} {}", logPrefix, message,
this.headersToLog.createStandardLabelsFromMap(headers), ex);
}
@Override
public void close() throws Exception {
// do nothing
}
}
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.logging;
import org.opengroup.osdu.core.common.model.http.HeadersToLog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
/**
* Creating beans needed for Slf4JLogger.
*/
@Configuration
public class Slf4JLoggerConfiguration {
/**
* Bean for HeadersToLog used in {@link Slf4JLogger}.
* @return {@link HeadersToLog} instance
*/
@Bean
public HeadersToLog headersToLog() {
return new HeadersToLog(Collections.emptyList());
}
}
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* Factory for slf4j logger instances.
*/
@Component
public final class Slf4jLoggerFactory {
/**
* Returns slf4j logger instance based on name.
* @param loggerName name of the logger
* @return {@link Logger} instance
*/
public Logger getLogger(final String loggerName) {
return LoggerFactory.getLogger(loggerName);
}
}
......@@ -23,15 +23,20 @@
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${hostName} --- [%15.15t] %-40.40c{1.} : %m%n%ex"/>
</Console>
<ApplicationInsightsAppender name="aiAppender">
</ApplicationInsightsAppender>
</Appenders>
<Loggers>
<Root level="trace">
<!-- TODO: In future it will be changed to warn-->
<Root level="info">
<AppenderRef ref="Console" />
<AppenderRef ref="aiAppender" />
</Root>
<logger name="TxnLogger" level="info" additivity="false">
<AppenderRef ref="Console" />
<AppenderRef ref="aiAppender" />
</logger>
</Loggers>
</Configuration>
\ No newline at end of file
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.filters;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.DpsHeaders;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link TransactionLogFilter}
*/
@ExtendWith(MockitoExtension.class)
public class TransactionLogFilterTest {
private static final int STATUS_CODE = 200;
@Mock
private HttpServletRequest servletRequest;
@Mock
private HttpServletResponse servletResponse;
@Mock
private FilterChain filterChain;
@Mock
private JaxRsDpsLog jaxRsDpsLog;
@InjectMocks
private TransactionLogFilter logFilter;
@BeforeEach
public void setup() {
when(servletResponse.getStatus()).thenReturn(STATUS_CODE);
}
@Test
public void testStartAndEndMessagesAreLoggedProperly() throws Exception {
final String startLogMessage = "Start Web-API PUT records Headers: {correlation-id:abc}";
final String endMessage = "End Web-API PUT records Headers: {correlation-id:abc} status=200 time=";
when(servletRequest.getMethod()).thenReturn("PUT");
when(servletRequest.getServletPath()).thenReturn("records");
when(servletRequest.getHeader(eq(DpsHeaders.CORRELATION_ID))).thenReturn("abc");
when(servletResponse.getHeader(eq(DpsHeaders.CORRELATION_ID))).thenReturn("abc");
final ArgumentCaptor<String> logMessageCaptor = ArgumentCaptor.forClass(String.class);
doNothing().when(jaxRsDpsLog).info(eq("TxnLogger"), logMessageCaptor.capture());
this.logFilter.doFilter(servletRequest, servletResponse, filterChain);
verify(servletRequest, times(2)).getMethod();
verify(servletRequest, times(2)).getServletPath();
verify(servletRequest, times(2)).getHeader(eq(DpsHeaders.CORRELATION_ID));
verify(servletResponse, times(2)).getHeader(eq(DpsHeaders.CORRELATION_ID));
verify(servletResponse, times(1)).getStatus();
assertEquals(2, logMessageCaptor.getAllValues().size());
assertEquals(startLogMessage, logMessageCaptor.getAllValues().get(0));
assertEquals(true, logMessageCaptor.getAllValues().get(1).startsWith(endMessage));
}
@Test
public void testStartAndEndMessagesAreLoggedProperlyWithNoHeaders() throws Exception {
final String startLogMessage = "Start Web-API PUT records Headers: {}";
final String endMessage = "End Web-API PUT records Headers: {} status=200 time=";
when(servletRequest.getMethod()).thenReturn("PUT");
when(servletRequest.getServletPath()).thenReturn("records");
final ArgumentCaptor<String> logMessageCaptor = ArgumentCaptor.forClass(String.class);
doNothing().when(jaxRsDpsLog).info(eq("TxnLogger"), logMessageCaptor.capture());
this.logFilter.doFilter(servletRequest, servletResponse, filterChain);
verify(servletRequest, times(2)).getMethod();
verify(servletRequest, times(2)).getServletPath();
verify(servletResponse, times(1)).getStatus();
assertEquals(2, logMessageCaptor.getAllValues().size());
assertEquals(startLogMessage, logMessageCaptor.getAllValues().get(0));
assertEquals(true, logMessageCaptor.getAllValues().get(1).startsWith(endMessage));
}
}
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.logging;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
/**
* Contains tests for {@link Slf4jLoggerFactory}
*/
@ExtendWith(MockitoExtension.class)
public class Slf4JLoggerFactoryTest {
private static final String LOGGER_NAME1 = Slf4JLoggerFactoryTest.class.getName();
private static final String LOGGER_NAME2 = Slf4jLoggerFactory.class.getName();
@InjectMocks
private Slf4jLoggerFactory slf4jLoggerFactory;
@Test
public void testSameLoggerInstanceReturnedWhenCalledWithSameName() {
final Logger logger1 = slf4jLoggerFactory.getLogger(LOGGER_NAME1);
final Logger logger2 = slf4jLoggerFactory.getLogger(LOGGER_NAME1);
assertSame(logger1, logger2, "when called with same name, it should return same instance");
}
@Test
public void testDifferentLoggerInstanceReturnedWhenCalledWithDifferentName() {
final Logger logger1 = slf4jLoggerFactory.getLogger(LOGGER_NAME1);
final Logger logger2 = slf4jLoggerFactory.getLogger(LOGGER_NAME2);
assertNotEquals(logger1, logger2, "when called with different names, it should return different instances");
}
}
// Copyright © Microsoft Corporation
//
// 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.opengroup.osdu.azure.logging;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opengroup.osdu.core.common.logging.audit.AuditPayload;
import org.opengroup.osdu.core.common.model.http.HeadersToLog;
import org.opengroup.osdu.core.common.model.http.Request;
import org.slf4j.Logger;