I am trying to log some info during api call in console in my Spring Boot application. I have used Spring Aop and trying to use #Around advise with #Pointcut by using ProceedingJoinPoint. The program is compiling successfully & runs quite well. But logging is not happening in console. I think my Pointcut is not triggering. My code is given bellow. Please help.
package org.mycomp.mat.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.mycomp.mycompmat.user.profile.service.UserProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
#Aspect
#Component
public class LoggingAspect {
Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
#Pointcut("execution(public * org.mycomp.mat.repository..*(..))")
public void requestPointcut() {
}
#Around("requestPointcut()")
public Object requestLogger(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
ObjectMapper objectMapper = new ObjectMapper();
String methodName = proceedingJoinPoint.getSignature().getName();
String className = proceedingJoinPoint.getTarget().getClass().toString();
Object[] inputArguments = proceedingJoinPoint.getArgs();
logger.info(
"Operation " + methodName +
" Of " + className +
" On " + LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME) +
" With Input Parameters :-> " + objectMapper.writeValueAsString(inputArguments)
);
Object object = proceedingJoinPoint.proceed();
logger.info(objectMapper.writeValueAsString(object));
return object;
}
}
the issue is with the pointcut expression missing * for the class name part
like org.mycomp.mat.repository.myClass.myMethod({arguments})
change from #Pointcut("execution(public * org.mycomp.mat.repository..*(..))") to #Pointcut("execution(public * org.mycomp.mat.repository.*.*(..))")
Related
I tried to use #Aspect for logging all requests and responses the code is running fine and successful with a simple spring boot project with a common configuration and not have any init methods or DB call.
The below code is not working that includes init configuration and DB call for required data used #EnableTransactionManagement its getting Error while use #Aspect while running my application
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class MyContextClass implements ApplicationListener<ContextRefreshedEvent>{
#Autowire
private MyServiceRepository myServiceRepo;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Added Db call
CommonUtils.setYears(myServiceRepo.getTotalYears());
}
}
#Aspect logging code
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
#Aspect
#Component
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Autowired
private HttpServletRequest request;
#Autowired
private HttpServletResponse response;
#Pointcut("within(#org.springframework.stereotype.Repository *)"
+ " || within(#org.springframework.stereotype.Service *)"
+ " || within(#org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("springBeanPointcut");
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
#Pointcut("within(com.test.mypackage..*)")
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("applicationPackagePointcut");
}
#Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
String methodParameterNames = methodSignature.getMethod().toString();
if (request ! = null) {
log.info(request.getRequestURL());
log.info(request.getMethod());
}
if (response != null) {
log.info(response.getRequestURL());
log.info(response.getStatus());
}
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
}
this code build success. but when I run the application below error display the error in setYears
2021-05-07 20:14:58 [restartedMain] ERROR o.s.boot.SpringApplication-[SpringApplication.java:856]-Application run failed
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:66)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:292)
at com.sun.proxy.$Proxy120.getRequestURL(Unknown Source)
at com.opl.ans.config.utils.LoggingAspect.logAround(LoggingAspect.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
Is it the right way to do it? Are there any suggestions? Please help.
Based on the error stack the issue is that the request instance is invalid. I would guess that the request instance autowired to the Aspect could be stale or rather not assosicated with the current thread. This means that request instance is not null and the check within logAround() method gives unexepected results.
RequestContextHolder.getRequestAttributes() would return null if no request attributes are currently bound to the thread. Modifying the
if (request ! = null) { ..} check with if (RequestContextHolder.getRequestAttributes() ! = null) {..} should fix the issue.
Also spring boot provides out of the box solutions to what you are currently attempting through AOP . Do check them out as well.
HTTP Tracing
Spring MVC Metrics
I have a spring boot app with an Endpoint Test Configuration class and a unit test to test my http client. I am trying to get my server address and port from my application.properties which is located in my src/test.(All the classes are in my src/test.)
Here is my config class code :
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import javax.xml.bind.JAXBException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import com.nulogix.billing.service.PredictionEngineService;
import com.nulogix.billing.ws.endpoint.AnalyzeEndPoint;
import com.nulogix.billing.ws.endpoint.GetVersionEndPoint;
#Configuration
public class EndPointTestConfiguration {
#Value("${billing.engine.address}")
private String mockAddress;
#Value("${billing.engine.port}")
private String mockPort;
#Bean
public String getAddress() {
String serverAddress = "http://" + mockAddress + ":" + mockPort;
return serverAddress;
}
#Bean
public GetVersionEndPoint getVersionEndPoint() {
return new GetVersionEndPoint();
}
I annotated the values from my .properties with #value and then created a method that I instantiated with a bean to to return my server address string.
I then use that string value here in my HttpClientTest class:
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.Map;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.fluent.Request;
import org.apache.http.entity.ContentType;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ConfigurableApplicationContext;
import com.google.gson.Gson;
import com.nulogix.billing.configuration.EndPointTestConfiguration;
import com.nulogix.billing.mockserver.MockServerApp;
#SpringBootTest(classes = EndPointTestConfiguration.class)
public class HttpClientTest {
#Autowired
EndPointTestConfiguration endpoint;
public static final String request_bad = "ncs|56-2629193|1972-03-28|20190218|77067|6208|3209440|self|";
public static final String request_good = "ncs|56-2629193|1972-03-28|20190218|77067|6208|3209440|self|-123|-123|-123|0.0|0.0|0.0|0.0|0.0|0.0|0.0";
//gets application context
static ConfigurableApplicationContext context;
//call mock server before class
#BeforeClass
static public void setup(){
SpringApplication springApplication = new SpringApplicationBuilder()
.sources(MockServerApp.class)
.build();
context = springApplication.run();
}
//shutdown mock server after class
#AfterClass
static public void tearDown(){
SpringApplication.exit(context);
}
#Test
public void test_bad() throws ClientProtocolException, IOException {
// missing parameter
String result = Request.Post(endpoint.getAddress())
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(request_bad, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
Map<?, ?> resultJsonObj = new Gson().fromJson(result, Map.class);
// ensure the key exists
assertEquals(resultJsonObj.containsKey("status"), true);
assertEquals(resultJsonObj.containsKey("errorMessage"), true);
// validate values
Boolean status = (Boolean) resultJsonObj.get("status");
assertEquals(status, false);
String errorMessage = (String) resultJsonObj.get("errorMessage");
assertEquals(errorMessage.contains("Payload has incorrect amount of parts"), true);
}
#Test
public void test_good() throws ClientProtocolException, IOException {
String result = Request.Post(endpoint.getAddress())
.connectTimeout(2000)
.socketTimeout(2000)
.bodyString(request_good, ContentType.TEXT_PLAIN)
.execute().returnContent().asString();
Map<?, ?> resultJsonObj = new Gson().fromJson(result, Map.class);
// ensure the key exists
assertEquals(resultJsonObj.containsKey("status"), true);
assertEquals(resultJsonObj.containsKey("errorMessage"), false);
assertEquals(resultJsonObj.containsKey("HasCopay"), true);
assertEquals(resultJsonObj.containsKey("CopayAmount"), true);
assertEquals(resultJsonObj.containsKey("HasCoinsurance"), true);
assertEquals(resultJsonObj.containsKey("CoinsuranceAmount"), true);
assertEquals(resultJsonObj.containsKey("version"), true);
// validate values
Boolean status = (Boolean) resultJsonObj.get("status");
assertEquals(status, true);
String version = (String) resultJsonObj.get("version");
assertEquals(version, "0.97");
}
}
I use it in the request.post, I didn't want to hardcode in my IP address and port number.
When I run the test it says
[ERROR] HttpClientTest.test_bad:63 NullPointer
[ERROR] HttpClientTest.test_good:86 NullPointer
But I am not sure why it is null? I am pretty sure I have everything instantiated and the string is clearly populated..
My package structure for my config is com.billing.mockserver and my package structure for my unit test is com.billing.ws.endpoint.
Here is my application.properties
server.port=9119
server.ssl.enabled=false
logging.config=classpath:logback-spring.xml
logging.file=messages
logging.file.max-size=50MB
logging.level.com.nulogix=DEBUG
billing.engine.address=127.0.0.1
billing.engine.port=9119
billing.engine.api.version=0.97
billing.engine.core.name=Patient_Responsibility
You are missing springboot basic understanding. #Configuration class is to initialize other spring beans and other things and are the first classes which get initialized. You should not #Autowire #configuration class.
In your Configuration class you can either create Spring bean for username and password and autowire that in your test class or directly use #Value in your Test class.
Example: in your configuration class you are creating bean of GetVersionEndPoint and you can autowire that in your Test class.
Update 2:
For test classes, you need to add application.properties file in src\test\resource
In our application we have couple of webservices.i need to get the userids(means who has loged in and webservice name) for auditing purpose i need to store.
Could anyone suggest which will be either filter chain or Interceptors and how to get the webservice name.
You can use PreSecurityInterceptor for doing this ,request from client will first goto it and there you can log the data in db or file .
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
/**
* This interceptor verify the method name and log the details
* */
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethod;
//import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
#Provider
#ServerInterceptor
public class PreSecurityInterceptor implements PreProcessInterceptor {
#Context HttpServletRequest req;
#Override
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException {
System.out.println("********************************************* pre process *****************************");
System.out.println(new Date() + " Method : " + method.getMethod().getName() + " being called from "
+ req.getRemoteHost()+":"+req.getLocalPort()+": Remote Port "+req.getRemotePort());
if ( "defaultMethod".equalsIgnoreCase(method.getMethod().getName())) {
return null; // normal flow continues
} else {
System.out.println(new Date() + " Method : " + method.getMethod().getName() + " Precondition failed ");
return (ServerResponse) Response
.status(Response.Status.PRECONDITION_FAILED).entity("invalid method details provided !!").build();
}
}
}
I'm having problems getting my logging aspect to log information when methods from classes of a particular package are accessed. In other words, "no" logging occurs. I even got desperate and added System.out.println statements, with no luck.
All of my classes are located under the org.my.package package, i.e. org.my.package.controller, org.my.package.model, etc.
Here is my Application class:
package org.my.package;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#ComponentScan(basePackages = {"org.my.package.config"})
#EnableAutoConfiguration
#EnableAspectJAutoProxy
public class FirstWebAppApplication {
public static void main(String[] args) {
SpringApplication.run(FirstWebAppApplication.class, args);
}
}
This is my configuration class:
package org.my.package.config;
import org.deloitte.javatraining.daythree.utilities.MyLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"org.my.package.utilities"})
public class AssetConfig {
//-----------------------------------------------------------------------------------------------------------------------
#Bean
public MyLogger myLogger(){
return new MyLogger();
}
}
This is my Aspect class:
package org.my.package.utilities;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class MyLogger {
/** Handle to the log file */
private final Log log = LogFactory.getLog(getClass());
public MyLogger () {}
#AfterReturning("execution(* org.my.package.*.*(..))")
public void logMethodAccessAfter(JoinPoint joinPoint) {
log.info("***** Completed: " + joinPoint.getSignature().getName() + " *****");
System.out.println("***** Completed: " + joinPoint.getSignature().getName() + " *****");
}
#Before("execution(* org.my.package.*.*(..))")
public void logMethodAccessBefore(JoinPoint joinPoint) {
log.info("***** Starting: " + joinPoint.getSignature().getName() + " *****");
System.out.println("***** Starting: " + joinPoint.getSignature().getName() + " *****");
}
}
These are my Gradle build dependencies:
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-web")
compile('com.h2database:h2:1.3.156')
compile('javax.servlet:jstl:1.2')
compile('org.springframework.boot:spring-boot-starter-aop')
providedRuntime("org.apache.tomcat.embed:tomcat-embed-jasper")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
Am I missing something or otherwise mis-configuring my Aspect class? I can't find anything wrong, after verifying with other, similar Stack Overflow questions and online tutorials.
Please advise.
Your point cut, execution( * org.my.package.*.*(..)), is only matching the execution of methods on classes in the org.my.packagepackage not sub packages.
What you probably want is execution( * org.my.package..*.*(..)) notice the .. instead of ..
I am currently working on a Java Web application that runs on Apache Tomcat 7. Due to the fact that I want to log some information into a database, I use the following configuration file information for initiating my Logger:
log4j.rootLogger = DEBUG, DB
log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DB.URL=jdbc:mysql://localhost:3306/cap_recommender_log
log4j.appender.DB.driver=com.mysql.jdbc.Driver
log4j.appender.DB.user=log_user
log4j.appender.DB.password=some_password
log4j.appender.DB.sql=INSERT INTO notify_service_log(date, logger, level, message) VALUES('%d{YYYY-MM-dd}','%C','%p','%m')
log4j.appender.DB.layout=org.apache.log4j.PatternLayout
Also, the notify_service_log table is as follows:
CREATE TABLE `notify_service_log` (
`date` date NOT NULL,
`logger` varchar(256) NOT NULL,
`level` varchar(10) NOT NULL,
`message` text NOT NULL,
KEY `index_level` (`level`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Finally, the web service code is as follows:
package com.imis.cap.service.notify;
import com.imis.cap.module.etl.EtlModuleClient;
import gr.aia.cap.eventbroker.v1.ArrayOfServiceMessage;
import gr.aia.cap.eventbroker.v1.BooleanResponse;
import gr.aia.cap.eventbroker.v1.ServiceMessage;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.jws.WebService;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
#WebService(serviceName = "NotifyService", portName = "HttpBinding_NotifyService", endpointInterface = "gr.aia.cap.eventbroker.v1.NotifyService", targetNamespace = "http://www.aia.gr/cap/eventbroker/v1/", wsdlLocation = "WEB-INF/wsdl/NotifyService/NotifyService.wsdl")
public class NotifyService {
private String username;
private String password;
private String log_properties_file;
private DataSource registration_db;
private static org.apache.log4j.Logger notifyServiceLogger =
Logger.getLogger(NotifyService.class);
public NotifyService() {
Context context;
try {
context = (Context) new InitialContext().lookup("java:comp/env");
this.username = (String) context.lookup("CAP_RECOMMENDER_UNAME");
this.password = (String) context.lookup("CAP_RECOMMENDER_PASS");
this.registration_db = (DataSource) context.lookup("jdbc/cap_registration_db");
this.log_properties_file = (String) context.lookup("NOTIFY_SERVICE_LOG_PROPERTIES_FILE");
}catch(NamingException e) {
System.err.println(e.getMessage());
notifyServiceLogger.error("NotifyService: NamingException occured during construction. Message: " +
e.getMessage());
}
PropertyConfigurator.configure(this.log_properties_file);
notifyServiceLogger.info("NotifyService: Object constructor completed successfully.");
}
public gr.aia.cap.eventbroker.v1.BooleanResponse notify(gr.aia.cap.eventbroker.v1.NotifyRequest request) throws ParseException {
ArrayOfServiceMessage arrayOfServiceMsg = new ArrayOfServiceMessage();
ServiceMessage msg = new ServiceMessage();
BooleanResponse response = new BooleanResponse();
EventTypeReader eventType = new EventTypeReader(request);
String regCode = request.getRegistrationCode();
String dbRegCode = "";
**notifyServiceLogger.info("NotifyService.notify(): Called with reg-code: " + request.getRegistrationCode() + ".");**
/**Some more code**/
return new BooleanResponse(); //Sample response
}
}
The client Code that performs the call of the notify procedure is as follows:
package notifyclient;
import gr.aia.cap.eventbroker.v1.ArrayOfAttribute;
import gr.aia.cap.eventbroker.v1.BooleanResponse;
import gr.aia.cap.eventbroker.v1.EventType;
import gr.aia.cap.eventbroker.v1.NotifyRequest;
public class NotifyClient {
public static void main(String[] args) {
NotifyRequest request = new NotifyRequest();
request.setRegistrationCode("blasdadasd");
request.setAttributes(new ArrayOfAttribute());
request.setEventType(EventType.USER);
BooleanResponse response = notify(request);
System.out.println("Output: " + response.getErrors().getServiceMessage().toString());
}
public static gr.aia.cap.eventbroker.v1.BooleanResponse notify(gr.aia.cap.eventbroker.v1.NotifyRequest request) {
gr.aia.cap.eventbroker.v1.NotifyService_Service service = new gr.aia.cap.eventbroker.v1.NotifyService_Service();
gr.aia.cap.eventbroker.v1.NotifyService port = service.getHttpBindingNotifyService();
return port.notify(request);
}
}
I have to inform you at this point that the required classes that are needed to perform the Web Service call are automatically generated by Netbeans 7.2.
The problem lies on the fact that the Constructor's message is logged into the database, but the information message in the notify function is never logged. Why is this happening? Any ideas?
As comments state, adding PropertyConfigurator.configure(this.log_properties_file); on the line above the logger call in the notify() method resolved the problem.