I recently created a Java application at work which used
JSch for copying files via SFTP. For the most part, JSch has worked very well for my needs. However, I ran into some problems with the way it handles timeouts, and wanted to debug JSch's behavior. I use
Apache Commons Logging (usually with
Log4j) in all my applications, and was a bit disappointed to discover that JSch doesn't use Commons Logging, but instead uses it's a custom
com.jcraft.jsch.Logger
interface. I ended writing my own simple Logger class which wraps around Apache Commons Log. This works, but has a couple of caveats due to the limitations of JSch's logging design:
- JSch uses a single instance of Logger which is shared by all JSch classes, so all messages logged by JSch classes are logged with a single class/category name.
- JSch's Logger instance is a static property of the JSch class. Therefore, you can't configure configure different Logger instances for individual JSch connections.
import com.jcraft.jsch.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Class to route log messages generated by JSch to Apache Commons Logging.
*
* @author mrabbitt
* @see com.jcraft.jsch.Logger
*/
public class JSchCommonsLogger implements Logger {
private Log log;
/**
* Constructor with custom category name
*
* @param logName the category name used by this logger for Apache Commons.
*/
public JSchCommonsLogger(String logName) {
log = LogFactory.getLog(logName);
}
/**
* Default constructor
*/
public JSchCommonsLogger() {
this(Logger.class.getName());
}
/* (non-Javadoc)
* @see com.jcraft.jsch.Logger#isEnabled(int)
*/
public boolean isEnabled(int level) {
switch (level) {
case DEBUG:
return log.isDebugEnabled();
case INFO:
return log.isInfoEnabled();
case WARN:
return log.isWarnEnabled();
case ERROR:
return log.isErrorEnabled();
case FATAL:
return log.isFatalEnabled();
}
return false;
}
/* (non-Javadoc)
* @see com.jcraft.jsch.Logger#log(int, java.lang.String)
*/
public void log(int level, String message) {
switch (level) {
case DEBUG:
log.debug(message);
break;
case INFO:
log.info(message);
break;
case WARN:
log.warn(message);
break;
case ERROR:
log.error(message);
break;
case FATAL:
log.fatal(message);
break;
}
}
}
To use this, you'll need to do something like this somewhere in your code:
private static JSchCommonsLogger jschLogger = new JSchCommonsLogger();
com.jcraft.jsch.JSch.setLogger(jschLogger);
If you specify no argument to the JSchCommonsLogger() constructor, messages will be logged as
com.jcraft.jsch
. So, for example: If you're using Log4j, you can put something like this into a
log4.properties
to log errrors:
log4j.category.com.jcraft.jsch=ERROR