Attach logged user when printing any error - java

I have a SpringBoot backend with logback configured. Authentication is achieved using a OncePerRequestFilter and setting the authentication in the SecurityContextHolder context.
My goal is to print the loggedUser username whenever logback prints an error. My attempt was to add a variable (${LOGGED_USER}) to the logback pattern and then set this variable in the OncePerRequestFilter using the code below:
final Context context = (Context) LoggerFactory.getILoggerFactory();
final JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
context.putProperty("LOGGED_USER", username);
try {
configurator.doConfigure(Objects.requireNonNull(getClass().getResource("/logback.xml")));
} catch (JoranException e) {
logger.error("Error while configuring logger", e);
}
This works fine in localhost environment. When in production, however, when an error is generated the logger outputs several times, showing every logged user name. It seems to me that my code is creating a new logger context for every request and they are all outputting the error at the same time. I am out of ideas and looking for help!
Thanks in advance

Did you try using Mapped Diagnostic Context to store the username. Access it in the pattern as %X{LOGGED_USER}
final Context context = (Context) LoggerFactory.getILoggerFactory();
final JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
MDC.put("LOGGED_USER", username);
try {
configurator.doConfigure(Objects.requireNonNull(getClass().getResource("/logback.xml")));
} catch (JoranException e) {
logger.error("Error while configuring logger", e);
}
EDIT-
Since you are using OncePerRequestFilter, this answer by another user may help you

Related

Print logs to System.out of a remote EJB

I have created a client that calls an EJB by JNDI. The EJB has logs (slf4j) that normally are printed to a log file. What I want to do when I run my client is to redirect ALL possible logs to the standard output.
I've tried with SimpleLogger. I added slf4-simple dependency and if I add logs on my client, they are printed on console but not the logs of the EJB.
Client:
private static final Logger logger = LoggerFactory.getLogger("the EJB logger");
public static void main(String[] args) {
logger.debug("testing..."); // this log is shown on console
try {
Environment env = new Environment(); // weblogic.jndi.Environment
env.setProviderURL(EJB_HOST);
Context ctx = env.getInitialContext();
MyEjb ejb = (MyEjb) (ctx.lookup(EJB_JNDI));
ejb.foo(); // the logs of this method are not shown on console
} catch (NamingException) {
logger.error(e.getMessage(), e);
System.exit(2);
}
}

H2 db file in custom location

I am starting H2 db in embedded mode. By default h2 db file is getting created in users directory. I have requirement of creating in a custom location. The custom location should be read from environment variable ( Example %MY_HOME%= C:\TEST).
The database file should be created in c:\TEST. What changes should I make in web.xml to do the same?
Thanks in advance
You can add you custom location by setting db.url property of H2.
for example :
If your database name is DBNAME then you can set db.url in web.xml with your custom location in following manner :
jdbc:h2:file:C:\\Test\\DBNAME
If you are using Hibernate in your application then you can build session factory for H2 database in following manner :
private static SessionFactory buildSessionFactory()
{
String methodName = "buildSessionFactory -->";
_logger.debug(methodName + Constants.CALLED);
try
{
// Create the SessionFactory from hibernate.cfg.xml
Configuration configuration = new Configuration();
URL resourceURL = HibernateUtil.class.getClassLoader().getResource("hibernate.cfg.xml");
_logger.debug(resourceURL);
configuration = configuration.configure(resourceURL);
//Here you can set your custom url for H2 database.
String url = "jdbc:h2:file:C:\\Test\\DBNAME;MV_STORE=FALSE;MVCC=TRUE;DB_CLOSE_ON_EXIT=TRUE;FILE_LOCK=NO";
_logger.debug("Database URL " + url);
_logger.debug("Build Session Factory URL: " + url);
configuration = configuration.setProperty("hibernate.connection.url", url);
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
_logger.debug("Session factory built");
_logger.debug(Constants.END);
return configuration.buildSessionFactory(serviceRegistry);
}
catch (Throwable ex)
{
_logger.debug("Failed to create session factory");
_logger.error("Initial SessionFactory creation failed.", ex);
ex.printStackTrace();
throw new ExceptionInInitializerError(ex);
}
}
I got an answer for my query. We can set init parameters in two ways.
1) web.xml, Generally everyone uses.
2) contextInitialized will be method called as call back method while tomcat is getting started. In that method you can set the init parameters by using instance of servletContext clas.

remote connection dropped after some time in wildfly

I am using remote connection in java applet using the following code.
Hashtable jndiProps = new Hashtable<Object,Object>();
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
jndiProps.put(Context.PROVIDER_URL, "http-remoting://" + myhost + ":" + "8080");
jndiProps.put("jboss.naming.client.ejb.context", true);
jndiProps.put("org.jboss.ejb.client.scoped.context", true);;
//jndiProps.put(Context.URL_PKG_PREFIXES,"org.jboss.ejb.client.naming");
jndiProps.put("endpoint.name", "client-endpoint");
jndiProps.put("remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED", false);
jndiProps.put("remote.connections", "default");
jndiProps.put("remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS", false);
jndiProps.put("remote.connection.default.host", myhost);
jndiProps.put("remote.connection.default.port", "8080");
jndiProps.put(Context.SECURITY_PRINCIPAL, "demouser");
jndiProps.put(Context.SECURITY_CREDENTIALS, "demouser123");
InitialContext ctx = null;
try {
ctx = new InitialContext(jndiProps);
} catch(NamingException nex) {
nex.printStackTrace();
} catch(Exception ex) {
ex.printStackTrace();
}
RemoteInterface remote = (RemoteInterface)ctx.lookup(ejbUrl);
Here the SECURITY_PRINCIPAL is created using the add-user.sh script. The the applet loads, everything works well. But after sometime in java console , a message appears saying
Jul 13, 2016 3:04:21 PM org.jboss.ejb.client.remoting.ChannelAssociation$ResponseReceiver handleEnd
INFO: EJBCLIENT000016: Channel Channel ID 98a848d6 (outbound) of Remoting connection 22d8b2a8 to cms8sf.cdotd.ernet.in/192.168.5.240:8080 can no longer process messages.
And after that , whenever I try to access any method of remote bean Exception in thrown.
java.lang.IllegalStateException: EJBCLIENT000025: No EJB receiver available for handling [appName:app, moduleName:app-ejb, distinctName:] combination for invocation context org.jboss.ejb.client.EJBClientInvocationContext#490f0e4e
at org.jboss.ejb.client.EJBClientContext.requireEJBReceiver(EJBClientContext.java:774)
I am quite unsure why this is happening. Can someone please help me. Thank in advance.
I am using java 1.8 and wildfly 9.0.2.
Perhaps, this option will help you
remote.connection.default.connect.options.org.jboss.remoting3.RemotingOptions.HEARTBEAT_INTERVAL=5000
it's some kind of a ping

Connecting WAS to JRules USING ejb

I am trying to call RES server (v 7.1) from EAR deployed on WAS (8.5) instance. I was able to invoke rule server from standalone program and its working without any problems.
However my main problem is to invoke EJB deployed on RES server remotely from another EAR deployed on some other WAS instance. In this case we are not able to look-up the EJB remotely.
As per below thread we should bypass the EJB3 IlrSessionFactory API and should use Java EJB API to look up rule sessions directly.
http://www-01.ibm.com/support/docview.wss?uid=swg21586621
Recommendation from IBM is to use standard java api for ejb lookup or to upgrade to Rule Server 7.5 (latest 8.x).
Code snippet
// Initialization
Map<String, Object> outputParms = null;
IlrStatelessSession session=null;
IlrSessionResponse response=null;
// IlrSessionFactory factory = getFactory();
try {
sessionFactory = JRulesInvoker.getFactory();
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.ibm.websphere.naming.WsnInitialContextFactory");
env.put(Context.PROVIDER_URL,"corbaloc:iiop:localhost:28004");
Context ctx = new InitialContext(env);
Object lookupResult = ctx.lookup("ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote");
PortableRemoteObject aPortableRemoteObject = new PortableRemoteObject();
session = (IlrStatelessSession) aPortableRemoteObject.narrow(lookupResult, IlrStatelessSession.class);
IlrPath path = new IlrPath(ruleApp, ruleSet);
IlrSessionRequest request = sessionFactory.createRequest();
request.setRulesetPath(path);
request.setInputParameters(inputParms);
request.getTraceFilter().setInfoTotalRulesFired(true);
request.getTraceFilter().setInfoExecutionEvents(true);
request.setTraceEnabled(true);
// session = sessionFactory.createStatelessSession();
System.out.println("created session " + IlrJNDIConstants.STATELESS_SESSION_EJB3_NAME);
response = session.execute(request);
System.out.println(response.getRulesetExecutionTrace().getTotalRulesFired() + " rule(s) fired.");
System.out.println("Execution output=" + response.getRulesetExecutionOutput());
// Return the result(s)
outputParms = response.getOutputParameters();
if (logger.isEnabledFor(Level.DEBUG)) {
if (response.getRulesetExecutionOutput() != null) {
logger.debug("RuleSet execution output: \n" + response.getRulesetExecutionOutput());
}
}
}catch (IlrSessionCreationException cx) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(cx.getMessage(), cx);
}
} catch (IlrSessionException e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getMessage(), e);
}
} catch (NamingException e) {
if (logger.isEnabledFor(Level.ERROR)) {
logger.error(e.getMessage(), e);
}
}
Error
Context: idewas/nodes/ide/servers/server1, name: ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote: First component in name ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote not found.
javax.naming.NameNotFoundException: Context: idewas/nodes/ide/servers/server1, name: ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote: First component in name ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote not found. [Root exception is org.omg.CosNaming.NamingContextPackage.NotFound: IDL:omg.org/CosNaming/NamingContext/NotFound:1.0]
at com.ibm.ws.naming.jndicos.CNContextImpl.mapNotFoundException(CNContextImpl.java:4563)
at com.ibm.ws.naming.jndicos.CNContextImpl.doLookup(CNContextImpl.java:1821)
at com.ibm.ws.naming.jndicos.CNContextImpl.doLookup(CNContextImpl.java:1776)
at com.ibm.ws.naming.jndicos.CNContextImpl.lookupExt(CNContextImpl.java:1433)
at com.ibm.ws.naming.jndicos.CNContextImpl.lookup(CNContextImpl.java:615)
at com.ibm.ws.naming.util.WsnInitCtx.lookup(WsnInitCtx.java:165)
at com.ibm.ws.naming.util.WsnInitCtx.lookup(WsnInitCtx.java:179)
at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:161)
at javax.naming.InitialContext.lookup(InitialContext.java:436)
Check in the SystemOut.log of the RES server what are the binding names for EJBs as it looks like there is no ilog.rules.res.session.impl.ejb3.IlrStatelessSessionRemote there. Also if you have two servers on the same host under the same name e.g. server1 you may have interoberability issues and need to set JVM property com.ibm.websphere.orb.uniqueServerName to true. For more details check the following page Application access problems

Whats the best way to notify admin about new exceptions of the Java application?

My question is that whats the best way to keep track of the exceptions for the administrator of the application. (Notify administrator of the thrown exceptions for maintenance purposes).
To users of the system, I believe should catch the exceptions and show an appropriate error message.
To admin of the system, I suppose, best method is to have a messaging system to send details of each exception as a message to the receiver. Once receiver received a new error message persists it in the database or pings the admin an email with details of the exception.
try{
....
}
catch(Exception e){
//what to do here? how to notify admin?
}
I'd suggest using log4j, configured with an SMTPAppender listening to fatal logs. Then, just log a fatal level message (containing any useful information you can get) for any unhandled exception reaching your global try/catch block.
See also : What is the proper way to configure SMTPAppender in log4j?
Enterprise solution:
Use SL4J and save all messages to your logs.
Use MDC to add tags to your log messages. Have these tags describe who should be notified and the nature of the error:
2014-05-24 [SystemCAD][NOTIFY=ADMIN], [ACCOUNTID=123], [SEVERITY=SEVERE], [MESSAGE="Cannot contact Google.com"]
2014-05-24 [SystemCAD][NOTIFY=USER], [ACCOUNTID=123], [SEVERITY=SEVERE], [MESSAGE="Could not save document to Google. Support has been notified."]
Get Splunk or some product similar to index all your logs for easy searching and to create events which can be used to notify your admins. Use PagerDutty to notify your admins and create escalation, avoid duplicates, create triggers, etc.
First, do not try to solve the notification problem in the application itself.
The recommended approach is to catch the exception at an appropriate point in the application and generate a log event that captures the details (including the exception) of the failure. The primary logging should be done using a standard logging system. There are a number of viable options (e.g. java.util.logging, log4j, logback, log4j2, slf4j), each with pro's and con's, but the most important thing is to not attempt to "roll your own".
That's the easy part.
The hard part is figuring out how to get the notification from the logging system to the admin in a way that is appropriate. There are many things that need to be considered:
The admin does not be woken up at 2am by a page reporting an over-temperature in the office water cooler.
The admin does not want 50 SMS message all reporting the same problem. The system needs to be able to filter out duplicates.
The admin needs to be able to tell the system to "shut up" about a certain problem / issue.
The system needs to recognize that certain events are more important than others, and that business hours versus after hours affects prioritization.
What is the most appropriate way to notify the admin? Email? SMS? Pager?
Escalation - what if the primary (on-call) admin does not respond to the notification?
The system also needs to be integrated with other monitoring; e.g. checking service availability, network connectivity, file system levels, CPU / load average measures, checking that important events DO happen.
All of this needs to be configurable, independent of the application that generated the event in the first place.
Ideally, you need integration with operational issue tracking system ... to help the admin relate the event to previous problems, etc.
This is a really big problem space. Fortunately, there are products out there that do this kind of thing. Too many to list here.
(IMO, it doesn't make sense to recommend a solution for you. We don't know your organization's requirements. This is something that needs to be sorted out in conjunction with operational staff & management.)
I have done notification on exception in my application using spring AOP.
For example
#Aspect
public class ExceptionAspect {
#AfterThrowing(
pointcut = "execution(* com.suren.customer.bo.CustomerBo.addCustomerThrowException(..))",
throwing= "error")
public void logAfterThrowing(JoinPoint joinPoint, Throwable error) {
// Notify admin in email
sendEmail(joinPoint,error);
}
}
Common AspectJ annotations :
#Before – Run before the method execution
#After – Run after the method returned a result
#AfterReturning – Run after the method returned a result, intercept the returned result as well.
#AfterThrowing – Run after the method throws an exception
#Around – Run around the method execution, combine all three advices above.
When you design an application you need to consider two types of exceptions
User defined business exception
Unexpected system exception
User defined exceptions
User defined exceptions are used to pass negative conditions from one layer to another (service to web). For example in a banking application, if there is no balance in an account and if you try to withdraw money, WithdrawService might throw NoBalanceException. The web layer would catch this exception and display appropriate message to the user.
These type of exceptions are of no interest to the administrators and no alert is required. You may simply log it as info.
Unexpected system exception
Unexpected system exceptions are exceptions like database connectivity or JMS conncetivity or NullPointException or invalid message received from external system. Basically any unexpected (non-business) exceptions are classified as system exceptions.
According to Joshua Bloch in Effective Java, it is advisable not to catch system exception as you might do more harm than good. Instead allow it to propagate to the highest level (web layer).
In my applications, I provide a global exception handler (supported by Spring / Struts 2) on the web layer and send a detailed email to the ops team including the exception stack trace and redirect the request to a standard error page which says something like "Unexpected internal error occurred. Please try again".
Using this option is more secured as it will not expose the ugly exception stack trace to the user in any situation.
Struts2 reference:
http://struts.apache.org/release/2.3.x/docs/exception-handling.html
Consider using standard logging (like log4j) and using appender suited for you - either SMTP mentioned before, or custom one. There exists solutions called logging servers - they provide high flexibility in terms of notifications, filtering, storing, processing etc. Good place to start reading and investigating are Scribe and Flume. A great discussion on this subject may be found here.
There are also some cloud solutions available, from automated ones like Sentry through LogDigger (your own installation) to more low-level setups like Amazon SQS.
You should use a logging facility to log every exception in a file system so if Admin want they can view it through file-system.
ErrorUtil
public class ErrorLogUtil {
public static File createErrorFile(String fileName, String productName,
String regionName) {
File fileErrorLogs = new File("Error Logs");
if (!fileErrorLogs.isDirectory()) {
fileErrorLogs.mkdir();
}
File fileProductName = new File(fileErrorLogs, productName);
if (!fileProductName.isDirectory()) {
fileProductName.mkdir();
}
File fileDate = null;
if (regionName != null && regionName.trim().length() != 0) {
File fileRegionName = new File(fileProductName, regionName);
if (!fileRegionName.isDirectory()) {
fileRegionName.mkdir();
}
fileDate = new File(fileRegionName, new SimpleDateFormat(
"dd-MM-yyyy").format(new Date()));
if (!fileDate.isDirectory()) {
fileDate.mkdir();
}
} else {
fileDate = new File(fileProductName, new SimpleDateFormat(
"dd-MM-yyyy").format(new Date()));
if (!fileDate.isDirectory()) {
fileDate.mkdir();
}
}
File errorFile = new File(fileDate, fileName + "-errors.txt");
try {
if (!errorFile.exists()) {
errorFile.createNewFile();
System.out.println("New Error File created=>"+errorFile.getAbsolutePath());
}
} catch (IOException e) {
e.printStackTrace();
}
return errorFile;
}
public static void writeError(File errorFile, String error) {
try {
FileOutputStream fileOutputStream = new FileOutputStream(errorFile,
true);
DataOutputStream out = new DataOutputStream(fileOutputStream);
BufferedWriter bufferedWriter = new BufferedWriter(
new OutputStreamWriter(out));
bufferedWriter.append((new Date())+" - "+error);
bufferedWriter.newLine();
bufferedWriter.flush();
bufferedWriter.close();
fileOutputStream.flush();
fileOutputStream.close();
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void printStackTrace(File errorFile, String message, Throwable error) {
try {
FileOutputStream fileOutputStream = new FileOutputStream(errorFile,
true);
DataOutputStream out = new DataOutputStream(fileOutputStream);
PrintWriter bufferedWriter = new PrintWriter(
new BufferedWriter(new OutputStreamWriter(out)));
bufferedWriter.println(new Date() + " : "+ message);
error.printStackTrace(bufferedWriter);
bufferedWriter.println();
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sending mail will not be good because it may fill Admin's mail box but if you really need this you can create a MailUtil and send emails to the user or keep it in a log.
MailUtil
public class MailUtil {
public static void sendEmail(String messageString, String subject, Properties props) {
try {
Session mailSession = null;
final String userName = props.getProperty("mail.from");
final String password = props.getProperty("mail.from.password");
mailSession = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName, password);
}
});
Transport transport = mailSession.getTransport();
MimeMessage message = new MimeMessage(mailSession);
message.setSubject(subject);
message.setFrom(new InternetAddress(props.getProperty("mail.from")));
String[] to = props.getProperty("mail.to").split(",");
for (String email : to) {
message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
}
String body = messageString;
message.setContent(body, "text/html");
transport.connect();
transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
transport.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
public static void sendEmail(String subject, String messageString) {
try {
Session mailSession = null;
Properties props=new Properties();
FileInputStream fileInputStream = new FileInputStream(new File("mail-config.properties"));
props.load(fileInputStream);
fileInputStream.close();
final String fromUsername = props.getProperty("mail.from");
final String fromPassword = props.getProperty("mail.from.password");
mailSession = Session.getInstance(props, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(fromUsername, fromPassword);
}
});
Transport transport = mailSession.getTransport();
MimeMessage message = new MimeMessage(mailSession);
message.setSubject(subject);
message.setFrom(new InternetAddress(fromUsername));
String[] to = props.getProperty("mail.to").split(",");
for (String email : to) {
message.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
}
String body = messageString;
message.setContent(body, "text/html");
transport.connect();
transport.sendMessage(message, message.getRecipients(Message.RecipientType.TO));
transport.close();
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
You should use a property to manage if mail is required or not so in future you can stop mails by just changing the property file.
you can create exception log table. There, Write a code to insert exception with "Pending" status into database whatever exception raised in application.
Create a cron job (linux) or quartz scheduler that will fired in certain period and send mail of "pending" status exception with predefined format to admin user.
Update database entry with "sent" status so it will not send again.
In code, To save exception create super class,i.e.
class UserDao extends CommonDao
{
try
{
}catch(Exception e)
{
saveException(e);
}
}
class CommonDao
{
public void saveException(Exception e)
{
//write code to insert data into database
}
}
For me is not a good idea to put that behavior directly in the code of the application. It is clear that simply call to a function that sends an email in the catch clause it is easy, fast and direct. If you haven't so much time go for it.
But then you will realize that will produce some not expected collateral effects you will need to
Control the performance on exception parsing
Control what exceptions are interesting to notify and what not
Control not send lot of emails because a bug in the application producing exceptions constantly.
For that I prefer to use http://logstash.net/ That allows to put all your logs in a common noSQL database, and then you can use logstash to make dashboards or even create your own applications to send well designed reports about specific events. It require a bit more work at the beginning but after that I'm sure you will have more control about what is important to see in the logs and what not.

Categories