Merge branch '2.2.8' of ssh://git.cloud.com/var/lib/git/cloudstack-oss into 2.2.8

This commit is contained in:
root 2011-06-23 09:58:07 +05:30
commit 8cdba6a7f5
2601 changed files with 424830 additions and 23187 deletions

View File

@ -86,8 +86,30 @@ start() {
}
stop() {
echo -n $"Stopping $PROGNAME" "$SHORTNAME"
start-stop-daemon --stop --quiet --oknodo --pidfile "$PIDFILE"
SHUTDOWN_WAIT="30"
count="0"
echo -n $"Stopping $PROGNAME" "$SHORTNAME"
start-stop-daemon --stop --quiet --oknodo --pidfile "$PIDFILE"
until [ "$count" -gt "$SHUTDOWN_WAIT" ]
do
agentPid=`ps aux|grep [j]ava`
if [ "$?" -gt "0" ];then
break
fi
sleep 1
let count="${count}+1"
done
agentPid=`ps aux|grep [j]ava`
if [ "$?" -eq "0" ]; then
agentPid=`ps aux|grep [j]ava|awk '{print $2}'`
if [ "$agentPid" != "" ]; then
kill -9 $agentPid
fi
fi
log_end_msg $?
rm -f "$PIDFILE"
}

View File

@ -401,6 +401,7 @@ public class Agent implements HandlerFactory, IAgentControl {
_connection.start();
_shell.getBackoffAlgorithm().waitBeforeRetry();
} while (!_connection.isStartup());
s_logger.info("Connected to the server");
}
public void processStartupAnswer(Answer answer, Response response, Link link) {

View File

@ -135,7 +135,7 @@ import com.cloud.agent.api.check.CheckSshCommand;
import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand;
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand;
import com.cloud.agent.api.routing.IPAssocCommand;
import com.cloud.agent.api.routing.IpAssocCommand;
import com.cloud.agent.api.routing.IpAssocAnswer;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.agent.api.storage.CreateAnswer;
@ -882,8 +882,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return execute((FenceCommand) cmd);
} else if (cmd instanceof StartCommand ) {
return execute((StartCommand) cmd);
} else if (cmd instanceof IPAssocCommand) {
return execute((IPAssocCommand)cmd);
} else if (cmd instanceof IpAssocCommand) {
return execute((IpAssocCommand)cmd);
}else if (cmd instanceof NetworkElementCommand) {
return _virtRouterResource.executeRequest(cmd);
} else if (cmd instanceof CheckSshCommand) {
@ -1031,7 +1031,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
vm.attachDevice(nic.toString());
}
public Answer execute(IPAssocCommand cmd) {
public Answer execute(IpAssocCommand cmd) {
String routerName = cmd.getAccessDetail(NetworkElementCommand.ROUTER_NAME);
String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP);
String[] results = new String[cmd.getIpAddresses().length];

View File

@ -108,12 +108,23 @@ public class JettyVmDataServer implements VmDataServer {
protected void handleUserData(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String metadataItem = req.getPathInfo();
String requester = req.getRemoteAddr();
resp.setContentType("text/html");
resp.setStatus(HttpServletResponse.SC_OK);
String userData = _vmDataServer.getVmDataItem(requester, USER_DATA);
if (userData != null){
resp.getWriter().print(userData);
String data = null;
if (metadataItem != null) {
String[] path = metadataItem.split("/");
if (path.length > 1) {
metadataItem = path[1];
}
}
if (metadataItem != null)
data = _vmDataServer.getVmDataItem(requester, metadataItem);
if (data != null){
resp.getWriter().print(data);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_FOUND);
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Request not found");
@ -249,8 +260,8 @@ public class JettyVmDataServer implements VmDataServer {
Context root = new Context(_jetty,"/latest",Context.SESSIONS);
root.setResourceBase(_vmDataDir);
root.addServlet(new ServletHolder(new VmDataServlet(this, USER_DATA)), "/user-data");
root.addServlet(new ServletHolder(new VmDataServlet(this, META_DATA)), "/meta-data");
root.addServlet(new ServletHolder(new VmDataServlet(this, USER_DATA)), "/*");
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setResourceBase("/var/lib/images/");
@ -344,5 +355,4 @@ public class JettyVmDataServer implements VmDataServer {
String vmDataDir = _vmDataDir + File.separator + vmName;
Script.runSimpleBashScript("rm -rf " + vmDataDir);
}
}

View File

@ -33,7 +33,7 @@ public class IpAssocAnswer extends Answer{
super();
}
public IpAssocAnswer(IPAssocCommand cmd, String[] results) {
public IpAssocAnswer(IpAssocCommand cmd, String[] results) {
boolean finalResult = true;
for (String result : results) {

View File

@ -24,14 +24,14 @@ import com.cloud.agent.api.to.IpAddressTO;
* @author alena
*
*/
public class IPAssocCommand extends NetworkElementCommand {
public class IpAssocCommand extends NetworkElementCommand {
IpAddressTO[] ipAddresses;
protected IPAssocCommand() {
protected IpAssocCommand() {
}
public IPAssocCommand(IpAddressTO[] ips) {
public IpAssocCommand(IpAddressTO[] ips) {
this.ipAddresses = ips;
}

View File

@ -72,12 +72,14 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.OperationFailed, State.Error);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.ExpungeOperation, State.Expunging);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.AgentReportShutdowned, State.Stopped);
s_fsm.addTransition(State.Stopped, VirtualMachine.Event.AgentReportMigrated, State.Stopped);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationRetry, State.Starting);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.OperationFailed, State.Stopped);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportRunning, State.Running);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportStopped, State.Stopped);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportShutdowned, State.Stopped);
s_fsm.addTransition(State.Starting, VirtualMachine.Event.AgentReportMigrated, State.Starting);
s_fsm.addTransition(State.Destroyed, VirtualMachine.Event.RecoveryRequested, State.Stopped);
s_fsm.addTransition(State.Destroyed, VirtualMachine.Event.ExpungeOperation, State.Expunging);
s_fsm.addTransition(State.Running, VirtualMachine.Event.MigrationRequested, State.Migrating);
@ -85,6 +87,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportStopped, State.Stopped);
s_fsm.addTransition(State.Running, VirtualMachine.Event.StopRequested, State.Stopping);
s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportShutdowned, State.Stopped);
s_fsm.addTransition(State.Running, VirtualMachine.Event.AgentReportMigrated, State.Running);
s_fsm.addTransition(State.Migrating, VirtualMachine.Event.MigrationRequested, State.Migrating);
s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationSucceeded, State.Running);
s_fsm.addTransition(State.Migrating, VirtualMachine.Event.OperationFailed, State.Running);
@ -97,6 +100,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportStopped, State.Stopped);
s_fsm.addTransition(State.Stopping, VirtualMachine.Event.StopRequested, State.Stopping);
s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportShutdowned, State.Stopped);
s_fsm.addTransition(State.Stopping, VirtualMachine.Event.AgentReportMigrated, State.Stopping);
s_fsm.addTransition(State.Expunging, VirtualMachine.Event.OperationFailed, State.Expunging);
s_fsm.addTransition(State.Expunging, VirtualMachine.Event.ExpungeOperation, State.Expunging);
s_fsm.addTransition(State.Error, VirtualMachine.Event.DestroyRequested, State.Expunging);
@ -138,7 +142,8 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, StateObject
OperationSucceeded,
OperationFailed,
OperationRetry,
AgentReportShutdowned
AgentReportShutdowned,
AgentReportMigrated
};
public enum Type {

View File

@ -3,6 +3,10 @@
#Labels
label.default.use=Default Use
label.host.tags=Host Tags
label.storage.tags=Storage Tags
label.cidr=CIDR
force.delete=Force Delete

View File

@ -3,6 +3,10 @@
#Labels
label.default.use=Usar por defecto
label.host.tags=etiquetas de la máquina
label.storage.tags=Etiquetas de almacenamiento
label.cidr=CIDR
force.delete=Fuerza Borrar

View File

@ -3,6 +3,10 @@
#Labels
label.default.use=デフォルトの使用
label.host.tags=ホストのタグ
label.storage.tags=ストレージタグ
label.cidr=CIDR
force.delete=強制削除

View File

@ -3,6 +3,10 @@
#Labels
label.default.use=默认使用
label.host.tags=主机标签
label.storage.tags=存储标签
label.cidr=CIDR
force.delete=力删除

View File

@ -2,6 +2,7 @@
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
<throwableRenderer class="com.cloud.utils.log.CglibThrowableRenderer"/>
<!-- ================================= -->
<!-- Preserve messages in a local file -->
@ -10,7 +11,7 @@
<!-- A regular appender FIXME implement code that will close/reopen logs on SIGHUP by logrotate FIXME make the paths configurable using the build system -->
<appender name="FILE" class="org.apache.log4j.rolling.RollingFileAppender">
<param name="Append" value="true"/>
<param name="Threshold" value="DEBUG"/>
<param name="Threshold" value="TRACE"/>
<rollingPolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
<param name="FileNamePattern" value="@MSLOG@.%d{yyyy-MM-dd}.gz"/>
<param name="ActiveFileName" value="@MSLOG@"/>

View File

@ -501,7 +501,7 @@ fi
%files deps
%defattr(0644,root,root,0755)
%{_javadir}/%{name}-commons-codec-1.4.jar
%{_javadir}/%{name}-apache-log4j-extras-1.0.jar
%{_javadir}/%{name}-log4j-extras.jar
%{_javadir}/%{name}-backport-util-concurrent-3.0.jar
%{_javadir}/%{name}-ehcache.jar
%{_javadir}/%{name}-email.jar

View File

@ -45,7 +45,7 @@ import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand;
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand;
import com.cloud.agent.api.routing.DhcpEntryCommand;
import com.cloud.agent.api.routing.IPAssocCommand;
import com.cloud.agent.api.routing.IpAssocCommand;
import com.cloud.agent.api.routing.IpAssocAnswer;
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
@ -104,8 +104,8 @@ public class VirtualRoutingResource implements Manager {
return execute((SetStaticNatRulesCommand)cmd);
}else if (cmd instanceof LoadBalancerConfigCommand) {
return execute((LoadBalancerConfigCommand)cmd);
} else if (cmd instanceof IPAssocCommand) {
return execute((IPAssocCommand)cmd);
} else if (cmd instanceof IpAssocCommand) {
return execute((IpAssocCommand)cmd);
} else if (cmd instanceof CheckConsoleProxyLoadCommand) {
return execute((CheckConsoleProxyLoadCommand)cmd);
} else if(cmd instanceof WatchConsoleProxyLoadCommand) {
@ -266,7 +266,7 @@ public class VirtualRoutingResource implements Manager {
return new Answer(cmd);
}
protected Answer execute(final IPAssocCommand cmd) {
protected Answer execute(final IpAssocCommand cmd) {
IpAddressTO[] ips = cmd.getIpAddresses();
String[] results = new String[cmd.getIpAddresses().length];
int i = 0;

View File

@ -131,7 +131,7 @@ import com.cloud.agent.api.proxy.CheckConsoleProxyLoadCommand;
import com.cloud.agent.api.proxy.ConsoleProxyLoadAnswer;
import com.cloud.agent.api.proxy.WatchConsoleProxyLoadCommand;
import com.cloud.agent.api.routing.DhcpEntryCommand;
import com.cloud.agent.api.routing.IPAssocCommand;
import com.cloud.agent.api.routing.IpAssocCommand;
import com.cloud.agent.api.routing.IpAssocAnswer;
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
@ -384,8 +384,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return execute((SetStaticNatRulesCommand) cmd);
} else if (clazz == LoadBalancerConfigCommand.class) {
return execute((LoadBalancerConfigCommand) cmd);
} else if (clazz == IPAssocCommand.class) {
return execute((IPAssocCommand) cmd);
} else if (clazz == IpAssocCommand.class) {
return execute((IpAssocCommand) cmd);
} else if (clazz == CheckConsoleProxyLoadCommand.class) {
return execute((CheckConsoleProxyLoadCommand) cmd);
} else if (clazz == WatchConsoleProxyLoadCommand.class) {
@ -1547,7 +1547,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return null;
}
protected Answer execute(final IPAssocCommand cmd) {
protected Answer execute(final IpAssocCommand cmd) {
Connection conn = getConnection();
String[] results = new String[cmd.getIpAddresses().length];
int i = 0;

View File

@ -1,5 +1,5 @@
/usr/share/java/cloud-commons-codec-1.4.jar
/usr/share/java/cloud-apache-log4j-extras-1.0.jar
/usr/share/java/cloud-log4j-extras.jar
/usr/share/java/cloud-backport-util-concurrent-3.0.jar
/usr/share/java/cloud-ehcache.jar
/usr/share/java/cloud-email.jar

4
deps/.classpath vendored
View File

@ -2,7 +2,6 @@
<classpath>
<classpathentry kind="src" path="XenServerJava"/>
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry exported="true" kind="lib" path="cloud-apache-log4j-extras-1.0.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-axis.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-backport-util-concurrent-3.0.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-bcprov-jdk16-1.45.jar"/>
@ -22,7 +21,7 @@
<classpathentry exported="true" kind="lib" path="cloud-jsch-0.1.42.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-jstl-1.2.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-libvirt-0.4.5.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-log4j.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-log4j.jar" sourcepath="/home/dev/thirdparty/apache-log4j-1.2.16/src/main/java"/>
<classpathentry exported="true" kind="lib" path="cloud-mysql-connector-java-5.1.7-bin.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-servlet-api.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-trilead-ssh2-build213.jar"/>
@ -34,5 +33,6 @@
<classpathentry exported="true" kind="lib" path="cloud-xstream-1.3.1.jar"/>
<classpathentry exported="true" kind="lib" path="jetty-6.1.26.jar"/>
<classpathentry exported="true" kind="lib" path="jetty-util-6.1.26.jar"/>
<classpathentry exported="true" kind="lib" path="cloud-log4j-extras.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

Binary file not shown.

BIN
deps/cloud-log4j-extras.jar vendored Normal file

Binary file not shown.

BIN
deps/cloud-log4j.jar vendored

Binary file not shown.

View File

@ -1423,11 +1423,6 @@ public class AgentManagerImpl implements AgentManager, HandlerFactory, Manager {
return null;
}
if (!answer.getResult()) {
s_logger.warn("Unable to execute command: " + cmd.toString() + " due to " + answer.getDetails());
return null;
}
if (s_logger.isDebugEnabled() && answer.getDetails() != null) {
s_logger.debug("Details from executing " + cmd.getClass().toString() + ": " + answer.getDetails());
}

View File

@ -16,7 +16,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -867,49 +866,55 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
if (s_logger.isTraceEnabled()) {
s_logger.trace("Clustered agent transfer scan check, management server id:" + _nodeId);
}
if (_agentToTransferIds.size() > 0) {
s_logger.debug("Found " + _agentToTransferIds.size() + " agents to transfer");
for (Long hostId : _agentToTransferIds) {
AgentAttache attache = findAttache(hostId);
// if the thread:
// 1) timed out waiting for the host to reconnect
// 2) recipient management server is not active any more
// remove the host from re-balance list and delete from op_host_transfer DB
// no need to do anything with the real attache as we haven't modified it yet
Date cutTime = DateUtil.currentGMTTime();
if (_hostTransferDao.isNotActive(hostId, new Date(cutTime.getTime() - rebalanceTimeOut))) {
s_logger.debug("Timed out waiting for the host id=" + hostId + " to be ready to transfer, skipping rebalance for the host");
failStartRebalance(hostId);
return;
}
HostTransferMapVO transferMap = _hostTransferDao.findByIdAndCurrentOwnerId(hostId, _nodeId);
if (transferMap == null) {
s_logger.debug("Can't transfer host id=" + hostId + "; record for the host no longer exists in op_host_transfer table");
failStartRebalance(hostId);
return;
synchronized (_agentToTransferIds) {
if (_agentToTransferIds.size() > 0) {
s_logger.debug("Found " + _agentToTransferIds.size() + " agents to transfer");
//for (Long hostId : _agentToTransferIds) {
for (Iterator<Long> iterator = _agentToTransferIds.iterator(); iterator.hasNext();) {
Long hostId = iterator.next();
AgentAttache attache = findAttache(hostId);
// if the thread:
// 1) timed out waiting for the host to reconnect
// 2) recipient management server is not active any more
// remove the host from re-balance list and delete from op_host_transfer DB
// no need to do anything with the real attache as we haven't modified it yet
Date cutTime = DateUtil.currentGMTTime();
if (_hostTransferDao.isNotActive(hostId, new Date(cutTime.getTime() - rebalanceTimeOut))) {
s_logger.debug("Timed out waiting for the host id=" + hostId + " to be ready to transfer, skipping rebalance for the host");
iterator.remove();
_hostTransferDao.completeAgentTransfer(hostId);
continue;
}
HostTransferMapVO transferMap = _hostTransferDao.findByIdAndCurrentOwnerId(hostId, _nodeId);
if (transferMap == null) {
s_logger.debug("Can't transfer host id=" + hostId + "; record for the host no longer exists in op_host_transfer table");
iterator.remove();
_hostTransferDao.completeAgentTransfer(hostId);
continue;
}
ManagementServerHostVO ms = _mshostDao.findByMsid(transferMap.getFutureOwner());
if (ms != null && ms.getState() != ManagementServerHost.State.Up) {
s_logger.debug("Can't transfer host " + hostId + " as it's future owner is not in UP state: " + ms + ", skipping rebalance for the host");
iterator.remove();
_hostTransferDao.completeAgentTransfer(hostId);
continue;
}
if (attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) {
iterator.remove();
rebalanceHost(hostId, transferMap.getInitialOwner(), transferMap.getFutureOwner());
} else {
s_logger.debug("Agent " + hostId + " can't be transfered yet as its request queue size is " + attache.getQueueSize() + " and listener queue size is " + attache.getNonRecurringListenersSize());
}
}
ManagementServerHostVO ms = _mshostDao.findByMsid(transferMap.getFutureOwner());
if (ms != null && ms.getState() != ManagementServerHost.State.Up) {
s_logger.debug("Can't transfer host " + hostId + " as it's future owner is not in UP state: " + ms + ", skipping rebalance for the host");
failStartRebalance(hostId);
return;
}
if (attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) {
rebalanceHost(hostId, transferMap.getInitialOwner(), transferMap.getFutureOwner());
} else {
s_logger.debug("Agent " + hostId + " can't be transfered yet as its request queue size is " + attache.getQueueSize() + " and listener queue size is " + attache.getNonRecurringListenersSize());
} else {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Found no agents to be transfered by the management server " + _nodeId);
}
}
} else {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Found no agents to be transfered by the management server " + _nodeId);
}
}
@ -933,7 +938,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
boolean result = true;
if (currentOwnerId == _nodeId) {
_agentToTransferIds.remove(hostId);
if (!startRebalance(hostId)) {
s_logger.debug("Failed to start agent rebalancing");
failRebalance(hostId);
@ -952,9 +956,10 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
}
if (result) {
s_logger.debug("Got host id=" + hostId + " from management server " + futureOwnerId);
s_logger.debug("Successfully transfered host id=" + hostId + " to management server " + futureOwnerId);
finishRebalance(hostId, futureOwnerId, Event.RebalanceCompleted);
} else {
s_logger.debug("Failed to transfer host id=" + hostId + " to management server " + futureOwnerId);
finishRebalance(hostId, futureOwnerId, Event.RebalanceFailed);
}
@ -1033,7 +1038,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
synchronized (_agents) {
ClusteredDirectAgentAttache attache = (ClusteredDirectAgentAttache)_agents.get(hostId);
if (attache != null && attache.getQueueSize() == 0 && attache.getNonRecurringListenersSize() == 0) {
_agentToTransferIds.remove(hostId);
removeAgent(attache, Status.Rebalancing);
ClusteredAgentAttache forwardAttache = (ClusteredAgentAttache)createAttache(hostId);
if (forwardAttache == null) {
@ -1065,11 +1069,6 @@ public class ClusteredAgentManagerImpl extends AgentManagerImpl implements Clust
return true;
}
protected void failStartRebalance(final long hostId) {
_agentToTransferIds.remove(hostId);
_hostTransferDao.completeAgentTransfer(hostId);
}
protected void cleanupTransferMap() {
List<HostTransferMapVO> hostsJoingingCluster = _hostTransferDao.listHostsJoiningCluster(_nodeId);

View File

@ -84,6 +84,7 @@ import com.cloud.async.AsyncJob;
import com.cloud.async.AsyncJobManager;
import com.cloud.async.AsyncJobVO;
import com.cloud.cluster.StackMaid;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.domain.Domain;
@ -114,6 +115,7 @@ public class ApiServer implements HttpRequestHandler {
public static final short DOMAIN_ADMIN_COMMAND = 4;
public static final short RESOURCE_DOMAIN_ADMIN_COMMAND = 2;
public static final short USER_COMMAND = 8;
public static boolean encodeApiResponse = false;
private Properties _apiCommands = null;
private ApiDispatcher _dispatcher;
private ManagementServer _ms = null;
@ -224,6 +226,8 @@ public class ApiServer implements HttpRequestHandler {
ConfigurationVO apiPortConfig = values.get(0);
apiPort = Integer.parseInt(apiPortConfig.getValue());
}
encodeApiResponse = Boolean.valueOf(configDao.getValue(Config.EncodeApiResponse.key()));
ListenerThread listenerThread = new ListenerThread(this, apiPort);
listenerThread.start();
@ -886,4 +890,5 @@ public class ApiServer implements HttpRequestHandler {
}
return responseText;
}
}

View File

@ -22,10 +22,6 @@ import java.lang.reflect.Type;
import org.apache.log4j.Logger;
import com.cloud.configuration.Config;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.server.ManagementServer;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.encoding.URLEncoder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
@ -34,18 +30,6 @@ import com.google.gson.JsonSerializer;
public class EncodedStringTypeAdapter implements JsonSerializer<String>{
public static final Logger s_logger = Logger.getLogger(EncodedStringTypeAdapter.class.getName());
private static final boolean encodeApiResponse = configure();
private static boolean configure() {
ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
ConfigurationDao configDao = locator.getDao(ConfigurationDao.class);
if (configDao != null) {
return Boolean.valueOf(configDao.getValue(Config.EncodeApiResponse.key()));
} else {
return true;
}
}
@Override
public JsonElement serialize(String src, Type typeOfResponseObj, JsonSerializationContext ctx) {
@ -54,7 +38,7 @@ public class EncodedStringTypeAdapter implements JsonSerializer<String>{
}
private static String encodeString(String value) {
if (!encodeApiResponse) {
if (!ApiServer.encodeApiResponse) {
return value;
}
try {

View File

@ -32,6 +32,7 @@ import org.apache.log4j.Logger;
import com.cloud.api.ApiConstants;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.ApiResponseGsonHelper;
import com.cloud.api.ApiServer;
import com.cloud.api.BaseCmd;
import com.cloud.api.ResponseObject;
import com.cloud.configuration.Config;
@ -44,19 +45,6 @@ import com.google.gson.annotations.SerializedName;
public class ApiResponseSerializer {
private static final Logger s_logger = Logger.getLogger(ApiResponseSerializer.class.getName());
private static final boolean encodeApiResponse = configure();
private static boolean configure() {
ComponentLocator locator = ComponentLocator.getCurrentLocator();
ConfigurationDao configDao = locator.getDao(ConfigurationDao.class);
if (configDao != null) {
return Boolean.valueOf(configDao.getValue(Config.EncodeApiResponse.key()));
} else {
return true;
}
}
public static String toSerializedString(ResponseObject result, String responseType) {
if (BaseCmd.RESPONSE_TYPE_JSON.equalsIgnoreCase(responseType)) {
@ -291,7 +279,7 @@ public class ApiResponseSerializer {
}
private static String encodeParam(String value) {
if (!encodeApiResponse) {
if (!ApiServer.encodeApiResponse) {
return value;
}
try {

View File

@ -519,10 +519,14 @@ public class CapacityManagerImpl implements CapacityManager, StateListener<State
releaseVmCapacity(vm, false, false, oldHostId);
} else if (event == Event.AgentReportStopped) {
releaseVmCapacity(vm, false, true, oldHostId);
} else if(event == Event.AgentReportMigrated) {
releaseVmCapacity(vm, false, false, oldHostId);
}
} else if (oldState == State.Running) {
if (event == Event.AgentReportStopped) {
releaseVmCapacity(vm, false, true, oldHostId);
} else if(event == Event.AgentReportMigrated) {
releaseVmCapacity(vm, false, false, oldHostId);
}
} else if (oldState == State.Migrating) {
if (event == Event.AgentReportStopped) {
@ -538,14 +542,18 @@ public class CapacityManagerImpl implements CapacityManager, StateListener<State
} else if (oldState == State.Stopping) {
if (event == Event.AgentReportStopped || event == Event.OperationSucceeded) {
releaseVmCapacity(vm, false, true, oldHostId);
} else if(event == Event.AgentReportMigrated) {
releaseVmCapacity(vm, false, false, oldHostId);
}
} else if (oldState == State.Stopped) {
if (event == Event.DestroyRequested) {
releaseVmCapacity(vm, true, false, vm.getLastHostId());
} else if(event == Event.AgentReportMigrated) {
releaseVmCapacity(vm, false, false, oldHostId);
}
}
if ((newState == State.Starting || newState == State.Migrating) && vm.getHostId() != null) {
if ((newState == State.Starting || newState == State.Migrating || event == Event.AgentReportMigrated) && vm.getHostId() != null) {
boolean fromLastHost = false;
if (vm.getLastHostId() == vm.getHostId()) {
s_logger.debug("VM starting again on the last host it was stopped on");

View File

@ -100,12 +100,10 @@ public class ClusterBasedAgentLoadBalancerPlanner implements AgentLoadBalancerPl
int hostsLeftToGive = hostsToGive;
int hostsLeft = directHosts.size();
List<HostVO> hostsToReturn = new ArrayList<HostVO>();
int count = 0;
for (Long cluster : hostToClusterMap.keySet()) {
List<HostVO> hostsInCluster = hostToClusterMap.get(cluster);
hostsLeft = hostsLeft - hostsInCluster.size();
count++;
if (hostsToReturn.size() < hostsToGive) {
s_logger.debug("Trying cluster id=" + cluster);
@ -114,14 +112,12 @@ public class ClusterBasedAgentLoadBalancerPlanner implements AgentLoadBalancerPl
s_logger.debug("Skipping cluster id=" + cluster + " as it has more hosts that we need: " + hostsInCluster.size() + " vs " + hostsLeftToGive);
continue;
} else {
if (count == hostToClusterMap.size()) {
//get all hosts that are needed from the cluster
for (int i=0; i <= hostsLeftToGive; i++) {
hostsToReturn.add(hostsInCluster.get(i));
hostsLeftToGive = hostsLeftToGive - 1;
s_logger.debug("Taking host " + hostsInCluster.get(i) + " from cluster " + cluster);
}
}
//get all hosts that are needed from the cluster
s_logger.debug("Taking " + hostsLeftToGive + " from cluster " + cluster);
for (int i=0; i < hostsLeftToGive; i++) {
hostsToReturn.add(hostsInCluster.get(i));
}
break;
}
} else {

View File

@ -570,6 +570,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag
// Check that the maximum number of public IPs for the given
// accountId will not be exceeded
if (_accountMgr.resourceLimitExceeded(accountToLock, ResourceType.public_ip)) {
UserContext.current().setEventDetails("Maximum number of public IP addresses for account: " + accountToLock.getAccountName() + " has been exceeded.");
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of public IP addresses for account: " + accountToLock.getAccountName() + " has been exceeded.");
rae.setResourceType("ip");
throw rae;

View File

@ -43,7 +43,7 @@ import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.check.CheckSshAnswer;
import com.cloud.agent.api.check.CheckSshCommand;
import com.cloud.agent.api.routing.DhcpEntryCommand;
import com.cloud.agent.api.routing.IPAssocCommand;
import com.cloud.agent.api.routing.IpAssocCommand;
import com.cloud.agent.api.routing.LoadBalancerConfigCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand;
@ -699,24 +699,14 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
@DB
protected DomainRouterVO findOrCreateVirtualRouter(Network guestNetwork, DataCenterDeployment plan, HypervisorType type, Account owner) throws ConcurrentOperationException, InsufficientCapacityException {
Transaction txn = Transaction.currentTxn();
txn.start();
Network network = _networkDao.lockRow(guestNetwork.getId(), true);
if (network == null) {
throw new ConcurrentOperationException("Unable to acquire lock on " + guestNetwork.getId());
}
DomainRouterVO router = _routerDao.findByNetwork(guestNetwork.getId());
if (router != null) {
return router;
}
long id = _routerDao.getNextInSequence(Long.class, "id");
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating the router " + id);
}
PublicIp sourceNatIp = _networkMgr.assignSourceNatIpAddress(owner, guestNetwork, _accountService.getSystemUser().getId());
/* Before starting router, already know the hypervisor type */
VMTemplateVO template = _templateDao.findRoutingTemplate(type);
List<NetworkOfferingVO> offerings = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemControlNetwork);
NetworkOfferingVO controlOffering = offerings.get(0);
NetworkVO controlConfig = _networkMgr.setupNetwork(_systemAcct, controlOffering, plan, null, null, false, false).get(0);
@ -724,6 +714,25 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
List<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>(3);
NetworkOfferingVO publicOffering = _networkMgr.getSystemAccountNetworkOfferings(NetworkOfferingVO.SystemPublicNetwork).get(0);
List<NetworkVO> publicNetworks = _networkMgr.setupNetwork(_systemAcct, publicOffering, plan, null, null, false, false);
Transaction txn = Transaction.currentTxn();
txn.start();
Network network = _networkDao.lockRow(guestNetwork.getId(), true);
if (network == null) {
throw new ConcurrentOperationException("Unable to acquire lock on " + guestNetwork.getId());
}
router = _routerDao.findByNetwork(guestNetwork.getId());
if (router != null) {
return router;
}
long id = _routerDao.getNextInSequence(Long.class, "id");
if (s_logger.isDebugEnabled()) {
s_logger.debug("Creating the router " + id);
}
PublicIp sourceNatIp = _networkMgr.assignSourceNatIpAddress(owner, guestNetwork, _accountService.getSystemUser().getId());
NicProfile defaultNic = new NicProfile();
defaultNic.setDefaultNic(true);
defaultNic.setIp4Address(sourceNatIp.getAddress().addr());
@ -747,8 +756,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
networks.add(new Pair<NetworkVO, NicProfile>((NetworkVO) guestNetwork, gatewayNic));
networks.add(new Pair<NetworkVO, NicProfile>(controlConfig, null));
/* Before starting router, already know the hypervisor type */
VMTemplateVO template = _templateDao.findRoutingTemplate(type);
router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), template.getHypervisorType(), template.getGuestOSId(),
owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA());
router = _itMgr.allocate(router, template, _offering, networks, plan, null, owner);
@ -792,13 +799,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
@DB
protected DomainRouterVO findOrCreateDhcpServer(Network guestNetwork, DeployDestination dest, Account owner) throws InsufficientCapacityException, ConcurrentOperationException {
Transaction txn = Transaction.currentTxn();
txn.start();
Network network = _networkDao.lockRow(guestNetwork.getId(), true);
if (network == null) {
throw new ConcurrentOperationException("Unable to acquire lock on " + guestNetwork.getId());
}
DataCenterDeployment plan = null;
long dcId = dest.getDataCenter().getId();
@ -838,6 +838,26 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
/* Before starting router, already know the hypervisor type */
VMTemplateVO template = _templateDao.findRoutingTemplate(dest.getCluster().getHypervisorType());
Transaction txn = Transaction.currentTxn();
txn.start();
Network network = _networkDao.lockRow(guestNetwork.getId(), true);
if (network == null) {
throw new ConcurrentOperationException("Unable to acquire lock on " + guestNetwork.getId());
}
// In Basic zone and Guest network we have to start domR per pod, not per network
if ((dc.getNetworkType() == NetworkType.Basic || guestNetwork.isSecurityGroupEnabled()) && guestNetwork.getTrafficType() == TrafficType.Guest) {
router = _routerDao.findByNetworkAndPod(guestNetwork.getId(), podId);
plan = new DataCenterDeployment(dcId, podId, null, null, null);
} else {
router = _routerDao.findByNetwork(guestNetwork.getId());
plan = new DataCenterDeployment(dcId);
}
if (router != null) {
return router;
}
router = new DomainRouterVO(id, _offering.getId(), VirtualMachineName.getRouterName(id, _instance), template.getId(), template.getHypervisorType(), template.getGuestOSId(),
owner.getDomainId(), owner.getId(), guestNetwork.getId(), _offering.getOfferHA());
router.setRole(Role.DHCP_USERDATA);
@ -1505,7 +1525,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian
ipsToSend[i++] = ip;
firstIP = false;
}
IPAssocCommand cmd = new IPAssocCommand(ipsToSend);
IpAssocCommand cmd = new IpAssocCommand(ipsToSend);
cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress());
cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());

View File

@ -1408,12 +1408,16 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
if (answer != null && answer.getResult()) {
return true;
} else {
_storagePoolDao.expunge(pool.getId());
String msg = "";
if (answer != null) {
s_logger.warn(" can not create strorage pool through host " + hostId + " due to " + answer.getDetails());
msg = "Can not create strorage pool through host " + hostId + " due to " + answer.getDetails();
s_logger.warn(msg);
} else {
s_logger.warn(" can not create strorage pool through host " + hostId + " due to CreateStoragePoolCommand returns null");
msg = "Can not create strorage pool through host " + hostId + " due to CreateStoragePoolCommand returns null";
s_logger.warn(msg);
}
return false;
throw new CloudRuntimeException(msg);
}
}
@ -1580,6 +1584,7 @@ public class StorageManagerImpl implements StorageManager, StorageService, Manag
// check if the volume can be created for the user
// Check that the resource limit for volumes won't be exceeded
if (_accountMgr.resourceLimitExceeded(targetAccount, ResourceType.volume)) {
UserContext.current().setEventDetails("Maximum number of volumes for account: " + targetAccount.getAccountName() + " has been exceeded.");
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of volumes for account: " + targetAccount.getAccountName() + " has been exceeded.");
rae.setResourceType("volume");
throw rae;

View File

@ -959,7 +959,7 @@ public class AccountManagerImpl implements AccountManager, AccountService, Manag
if (isResourceDomainAdmin(caller.getType())) {
if (zoneId == null)
return getZoneIdForAccount(caller);
else if (getZoneIdForAccount(caller) != zoneId)
else if (zoneId.compareTo(getZoneIdForAccount(caller)) != 0)
throw new PermissionDeniedException("Caller " + caller + "is not allowed to access the zone " + zoneId);
else
return zoneId;

View File

@ -2316,6 +2316,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
// check if account/domain is with in resource limits to create a new vm
if (_accountMgr.resourceLimitExceeded(owner, ResourceType.user_vm)) {
UserContext.current().setEventDetails("Maximum number of virtual machines for account: " + owner.getAccountName() + " has been exceeded.");
ResourceAllocationException rae = new ResourceAllocationException("Maximum number of virtual machines for account: " + owner.getAccountName() + " has been exceeded.");
rae.setResourceType("vm");
throw rae;

View File

@ -489,6 +489,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
}
@DB
protected <T extends VMInstanceVO> Ternary<T, ReservationContext, ItWorkVO> changeToStartState(VirtualMachineGuru<T> vmGuru, T vm, User caller, Account account)
throws ConcurrentOperationException {
long vmId = vm.getId();
@ -1479,14 +1480,12 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
* something should be cleaned up
*
*/
protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean nativeHA) {
protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean trackExternalChange) {
State agentState = info.state;
final String agentName = info.name;
final State serverState = vm.getState();
final String serverName = vm.getInstanceName();
VirtualMachineGuru<VMInstanceVO> vmGuru = getVmGuru(vm);
Command command = null;
if (s_logger.isDebugEnabled()) {
@ -1511,6 +1510,16 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
_alertMgr.sendAlert(alertType, vm.getDataCenterIdToDeployIn(), vm.getPodIdToDeployIn(), "VM (name: " + vm.getInstanceName() + ", id: " + vm.getId() + ") stopped on host " + hostDesc + " due to storage failure",
"Virtual Machine " + vm.getInstanceName() + " (id: " + vm.getId() + ") running on host [" + vm.getHostId() + "] stopped due to storage failure.");
}
if(trackExternalChange) {
if(hostId != vm.getHostId()) {
try {
stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId);
} catch (NoTransitionException e) {
s_logger.warn(e.getMessage());
}
}
}
// if (serverState == State.Migrating) {
// s_logger.debug("Skipping vm in migrating state: " + vm);
@ -1524,17 +1533,14 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
assert (agentState == State.Stopped || agentState == State.Running) : "If the states we send up is changed, this must be changed.";
if (agentState == State.Running) {
try {
if(nativeHA) {
stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, hostId);
} else {
stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, vm.getHostId());
}
stateTransitTo(vm, VirtualMachine.Event.AgentReportRunning, hostId);
} catch (NoTransitionException e) {
s_logger.warn(e.getMessage());
}
// FIXME: What if someone comes in and sets it to stopping? Then what?
return null;
}
s_logger.debug("State matches but the agent said stopped so let's send a cleanup command anyways.");
return cleanup(agentName);
}
@ -1566,7 +1572,7 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
// Our records showed that it should be running so let's restart it.
_haMgr.scheduleRestart(vm, false);
} else if (serverState == State.Stopping) {
_haMgr.scheduleStop(vm, vm.getHostId(), WorkType.ForceStop);
_haMgr.scheduleStop(vm, hostId, WorkType.ForceStop);
s_logger.debug("Scheduling a check stop for VM in stopping mode: " + vm);
} else if (serverState == State.Starting) {
s_logger.debug("Ignoring VM in starting mode: " + vm.getInstanceName());
@ -1576,59 +1582,21 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
} else if (agentState == State.Running) {
if (serverState == State.Starting) {
if (fullSync) {
s_logger.debug("VM state is starting on full sync so updating it to running");
vm = findById(vm.getType(), vm.getId());
try {
stateTransitTo(vm, Event.AgentReportRunning, vm.getHostId());
} catch (NoTransitionException e1) {
s_logger.warn(e1.getMessage());
}
s_logger.debug("VM's " + vm + " state is starting on full sync so updating it to Running");
vm = vmGuru.findById(vm.getId());
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
List<NicVO> nics = _nicsDao.listByVmId(profile.getId());
for (NicVO nic : nics) {
Network network = _networkMgr.getNetwork(nic.getNetworkId());
NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null);
profile.addNic(nicProfile);
}
Commands cmds = new Commands(OnError.Stop);
s_logger.debug("Finalizing commands that need to be send to complete Start process for the vm " + vm);
if (vmGuru.finalizeCommandsOnStart(cmds, profile)) {
if (cmds.size() != 0) {
try {
_agentMgr.send(vm.getHostId(), cmds);
} catch (OperationTimedoutException e) {
s_logger.error("Exception during update for running vm: " + vm, e);
return null;
} catch (ResourceUnavailableException e) {
s_logger.error("Exception during update for running vm: " + vm, e);
return null;
}
}
if (vmGuru.finalizeStart(profile, vm.getHostId(), cmds, null)) {
try {
stateTransitTo(vm, Event.AgentReportRunning, vm.getHostId());
} catch (NoTransitionException e) {
s_logger.warn(e.getMessage());
}
} else {
s_logger.error("Exception during update for running vm: " + vm);
return null;
}
} else {
s_logger.error("Unable to finalize commands on start for vm: " + vm);
try {
ensureVmRunningContext(hostId, vm, Event.AgentReportRunning);
} catch (OperationTimedoutException e) {
s_logger.error("Exception during update for running vm: " + vm, e);
return null;
} catch (ResourceUnavailableException e) {
s_logger.error("Exception during update for running vm: " + vm, e);
return null;
} catch (NoTransitionException e) {
s_logger.warn(e.getMessage());
}
}
} else if (serverState == State.Stopping) {
s_logger.debug("Scheduling a stop command for " + vm);
_haMgr.scheduleStop(vm, vm.getHostId(), WorkType.Stop);
_haMgr.scheduleStop(vm, hostId, WorkType.Stop);
} else {
s_logger.debug("VM state is in stopped so stopping it on the agent");
command = cleanup(agentName);
@ -1636,6 +1604,46 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene
}
return command;
}
private void ensureVmRunningContext(long hostId, VMInstanceVO vm, Event cause) throws OperationTimedoutException, ResourceUnavailableException, NoTransitionException {
VirtualMachineGuru<VMInstanceVO> vmGuru = getVmGuru(vm);
s_logger.debug("VM state is starting on full sync so updating it to running");
vm = findById(vm.getType(), vm.getId());
try {
stateTransitTo(vm, cause, hostId);
} catch (NoTransitionException e1) {
s_logger.warn(e1.getMessage());
}
s_logger.debug("VM's " + vm + " state is starting on full sync so updating it to Running");
vm = vmGuru.findById(vm.getId()); // this should ensure vm has the most up to date info
VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
List<NicVO> nics = _nicsDao.listByVmId(profile.getId());
for (NicVO nic : nics) {
Network network = _networkMgr.getNetwork(nic.getNetworkId());
NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null);
profile.addNic(nicProfile);
}
Commands cmds = new Commands(OnError.Stop);
s_logger.debug("Finalizing commands that need to be send to complete Start process for the vm " + vm);
if (vmGuru.finalizeCommandsOnStart(cmds, profile)) {
if (cmds.size() != 0) {
_agentMgr.send(vm.getHostId(), cmds);
}
if (vmGuru.finalizeStart(profile, vm.getHostId(), cmds, null)) {
stateTransitTo(vm, cause, vm.getHostId());
} else {
s_logger.error("Unable to finish finialization for running vm: " + vm);
}
} else {
s_logger.error("Unable to finalize commands on start for vm: " + vm);
}
}
public Commands fullSync(final long hostId, final Map<String, State> newStates) {
Commands commands = new Commands(OnError.Continue);

View File

@ -118,4 +118,15 @@ UPDATE vm_instance set vm_type=type;
ALTER TABLE `cloud`.`networks` ADD COLUMN `is_domain_specific` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if network is domain specific, 0 false otherwise';
INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'NetworkManager', 'allow.subdomain.network.access', 'true', 'Allow subdomains to use networks dedicated to their parent domain(s)');
INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'management-server', 'encode.api.response', 'true', 'Do UTF-8 encoding for the api response, true by default');
INSERT INTO configuration (`category`, `instance`, `component`, `name`, `value`, `description`) VALUES ('Advanced', 'DEFAULT', 'management-server', 'encode.api.response', 'false', 'Do UTF-8 encoding for the api response, false by default');
DELETE FROM load_balancer_vm_map WHERE instance_id IN (SELECT id FROM vm_instance WHERE removed IS NOT NULL);
INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'management-server', 'hypervisor.list', 'KVM,XenServer,VMware,BareMetal', 'The list of hypervisors that this deployment will use.');
UPDATE IGNORE configuration set name='guest.domain.suffix' where name='domain.suffix';
INSERT IGNORE INTO configuration VALUES ('Advanced', 'DEFAULT', 'AgentManager', 'guest.domain.suffix', 'cloud.internal', 'Default domain name for vms inside virtualized networks fronted by router');
DELETE FROM configuration WHERE name='domain.suffix';

View File

@ -0,0 +1,118 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
=============
Preparing log4j releases.
=============
This is a detailed instruction to reproduce the log4j distribution
either to verify that the release is reproducable or to prepare
a hot-fix.
Apache log4j 1.2.16 was prepared using Ubuntu 9.10.
Preparation of environment:
1. Install Sun Java 6:
$> sudo sed 's/restricted/restricted universe multiverse/' -i /etc/apt/sources.list
$> sudo apt-get update && sudo apt-get -y update
$> sudo apt-get install sun-java6-jdk
$> sudo update-java-alternatives -s java-6-sun
$> export JAVA_HOME=/usr/lib/jvm/java-6-sun
2. Install Maven 2, Subversion, mingw and xemacs21, openssh-server:
$> sudo apt-get install maven2 subversion mingw32 xemacs21 openssh-server
3. Copy Win32 version of jni_md.h for NTEventLogAppender.dll
c:\>cd "\Program Files\Java\jdk_1.6.0_16\include\win32
c:\>scp jni_md.h username@hostname:
$> export JNI_WIN32_INCLUDE_DIR=/home/username
4. Create a local ssh key with no passphrase to enable
"deployment" of site back to the local machine using scp.
$> ssh-keygen
$> cd ~/.ssh
$> cat id_rsa.pub >> authorized_keys
$> ssh localhost
$> exit
From a command prompt:
$ export SVN_EDITOR=xemacs
$ svn co https://svn.apache.org/repos/asf/logging/log4j/tags/v1_2_16
$ cd v1_2_16
$ mvn site assembly:assembly
If you intended to deploy jars to the repo or update the site,
you need to set up ssh to use private keys to access people.apache.org
and create or modify ~/.m2/settings.xml to specify user name and key location.
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>logging.repo</id>
<username>USERNAME for people.apache.org</username>
<privateKey>/home/username/.ssh/id_rsa</privateKey>
<passphrase></passphrase>
</server>
</servers>
</settings>
You should test your ssh connection to people.apache.org
and localhost before attempting a deployment like:
$ ssh -l USERNAME people.apache.org
$ ssh localhost
The release artifacts were originally built by:
$> svn co http://svn.apache.org/repos/asf/logging/log4j/trunk log4j
$> cd log4j
$> mvn package release:prepare
$> mvn release:perform
The release artifacts can be rebuilt by:
$ mvn release:perform -DconnectionUrl=scm:svn:https://svn.apache.org/repos/asf/logging/log4j/tags/v1_2_16
The website content will automatically be staged to the ASF SVN repo by "mvn site-deploy".
This phase checks out https://svn.apache.org/repos/asf/logging/site/trunk/docs/log4j/1.2
into target/site-deploy, copys the generated documentation to that directory using
scp to localhost and then commits the changed content. You will be prompted for an
SVN commit message using the configured SVN_EDITOR. A commit message must be entered or the
site commit will be aborted.
The staged content can be tested by opening
http://svn.apache.org/repos/asf/logging/site/trunk/docs/1.2/index.html,
however some links may be broken due to the staged location.
The staged version can be published to the main public site by executing
"svn update" in /www/logging.apache.org/log4j/1.2 on people.apache.org.

104
thirdparty/apache-log4j-1.2.16/INSTALL vendored Normal file
View File

@ -0,0 +1,104 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
===========
Using log4j
===========
1) First untar or unzip the distribution file.
2) Assuming you chose to extract the distribution in to the
PATH_OF_YOUR_CHOICE, untarring the distribution file should create
a logging-log4j-VERSION directory, where VERSION is the log4j
version number, under PATH_OF_YOUR_CHOICE. We will refer to the
directory PATH_OF_YOUR_CHOICE/apache-log4j-VERSION/ as $LOG4J_HOME/.
3) Add $LOG4J_HOME/log4j-VERSION.jar to your CLASSPATH,
4) You can now test your installation by first compiling the following
simple program.
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
public class Hello {
private static final Logger logger = Logger.getLogger(Hello.class);
public
static
void main(String argv[]) {
BasicConfigurator.configure();
logger.debug("Hello world.");
logger.info("What a beatiful day.");
}
}
After compilation, try it out by issuing the command
java Hello
You should see log statements appearing on the console.
5) Refer to the javadoc documentation and the user manual on how to
include log statements in your own code.
=========
JAR files
=========
The log4j distribution comes with one jar file: log4j-VERSION.jar
under the LOG4J_HOME directory.
This jar file contains all the class files of the log4j project,
except test cases and classes from the "examples" and
"org.apache.log4j.performance" packages.
==============
Building log4j
==============
log4j (as of 1.2.15) is built with Maven 2. To rebuild log4j,
place Maven 2 on the PATH and execute "mvn package". The resulting
jar will be placed in the target subdirectory.
If building with JDK 1.4, one dependency will need to be manually
installed since its license does not allow it to be placed in the
online maven repositories. If not already installed, a build attempt will
describe where to download and how to install the dependency. To
install the dependency:
Download JMX 1.2.1 from http://java.sun.com/products/JavaManagement/download.html.
$ jar xf jmx-1_2_1-ri.zip
$ mvn install:install-file -DgroupId=com.sun.jmx -DartifactId=jmxri \
-Dversion=1.2.1 -Dpackaging=jar -Dfile=jmx-1_2_1-bin/lib/jmxri.jar
The build script will attempt to build NTEventLogAppender.dll if
MinGW is available on the path. If the unit tests are run on Windows
without NTEventLogAppender.dll, many warnings of the missing DLL
will be generated. An installer for MinGW on Windows is
available for download at http://sourceforge.net/project/showfiles.php?group_id=2435.
MinGW is also available through the package managers of many Linux distributions.
In case of problems send an e-mail note to
log4j-user@logging.apache.org. Please do not directly e-mail any
log4j developers. The answer to your question might be useful to other
users. Moreover, there are many knowledgeable users on the log4j-user
mailing lists who can quickly answer your questions.

148
thirdparty/apache-log4j-1.2.16/KEYS vendored Normal file
View File

@ -0,0 +1,148 @@
This file contains the PGP&GPG keys of various Apache developers.
Please don't use them for email unless you have to. Their main
purpose is code signing.
Apache users: pgp < KEYS
Apache developers:
(pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
or
(gpg --fingerprint --list-sigs <your name>
&& gpg --armor --export <your name>) >> this file.
Apache developers: please ensure that your key is also available via the
PGP keyservers (such as pgpkeys.mit.edu).
Type bits /keyID Date User ID
pub 1024D/0C7C4F05 2005/06/10 Mark Dwayne Womack <mwomack@apache.org>
Mark Dwayne Womack <markwomack@womacknet.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: PGP Key Server 0.9.6
mQGiBEKqEj8RBADR8e9Xl0kFJqv8SspvDP8kUsivBxWVZz+HVKf0pL2wOie0LfsF
E0Y3dI7k0k8i8KXtWYmHY3dpJGLUaruqIRxPFen/No56Q7udlK5hj7vKEUb46krx
sLgik1s+WX8+61Yu5cLuGdqnfwRGuNV7uf3JF1Q78VXIyUlS4BFMXGtqjwCg//s6
1m7N3p8AtIgma+U13rEkq9cEAJ6l9eEPgOdRx53nKkCgkVpDxxhpbg90STQ1s94f
rZIc+y5LN7FEERiQSiXvuzCwxiritiv+03sqdjYicxYZux+aladi0mHDIdgZkrAP
MrRJ+8AKs7jv+WXcyaJcja8h3IZLShszgUM6uCW4Wr8mzZ+ns/65ihe4A/jS/Gu+
cD44A/9doNrvOnNXPD+N/R9ME3zS6FY8d5F6f8oxZOEsMll/AG1HhOR4yxOmZMOl
+ZTg9AacHWMtTkjbs6JkT6uA8+tU+txoYjofkaGcJgq/SQYen89ifXZXVkUMWjqL
ioUUKCsu4OQPnH5k3jCQp7DrkpDAgIBGZ5F3QGaYsrkVjxNoE7QnTWFyayBEd2F5
bmUgV29tYWNrIDxtd29tYWNrQGFwYWNoZS5vcmc+iEYEEBECAAYFAkKwy1kACgkQ
vhbJXS4RQyKOYQCfddSBIMCd3kaFPvL90/piH/QIdIgAnRwG83V+KMRltnPRclDH
IcTI5unMiQBOBBARAgAOBQJCqhI/BAsDAgECGQEACgkQErKHWAx8TwWGeACgnN9d
jKDT2mjBKnApaDkMzmAaMvMAoKEcdBBCx62RPNIEBnxZ6zAxH244tC1NYXJrIER3
YXluZSBXb21hY2sgPG1hcmt3b21hY2tAd29tYWNrbmV0LmNvbT6IRgQQEQIABgUC
QrDLXgAKCRC+FsldLhFDIgvLAJ9VsCFN6uBcObkb+UCN70ukHH5VWQCdEjPNvKCs
c4+FGbTTsEoabhoNhsuJAEsEEBECAAsFAkKqFqUECwMCAQAKCRASsodYDHxPBeU1
AKDUIGqVzFZDXgK8PcLW8OlL2dXRmgCgwwxRkgpzuKNMnkphSZNend3VdJa5Ag0E
QqoSPxAIAPZCV7cIfwgXcqK61qlC8wXo+VMROU+28W65Szgg2gGnVqMU6Y9AVfPQ
B8bLQ6mUrfdMZIZJ+AyDvWXpF9Sh01D49Vlf3HZSTz09jdvOmeFXklnN/biudE/F
/Ha8g8VHMGHOfMlm/xX5u/2RXscBqtNbno2gpXI61Brwv0YAWCvl9Ij9WE5J280g
tJ3kkQc2azNsOA1FHQ98iLMcfFstjvbzySPAQ/ClWxiNjrtVjLhdONM0/XwXV0Oj
HRhs3jMhLLUq/zzhsSlAGBGNfISnCnLWhsQDGcgHKXrKlQzZlp+r0ApQmwJG0wg9
ZqRdQZ+cfL2JSyIZJrqrol7DVekyCzsAAgIH/2A8l4YFI5dYs5ZV3OgspEh0Qa/N
qd/JiVWBdygI77zp9TEgUtFBPDItEjmJq8sgnao0Cd5d68l9c+PQJ1xr4fOpdug0
YmYUgIaKutha3SSNRbD+T0WYmtTO5A4wxbsF3hYU7fvBJrt8gnO4tx6KAn/O2rRk
wPfNTZ1EdnKttZDM2Mz4OiK0SsQ9mS7zP+HPx3kzdl1Oj0Vk2tUElD1R1hVjLa/o
v5YgJCrwSD7RfCZAOEoPxXXN6StCqW1zT6HgcSR0clM5BC+ZyBJzczzD9I5+TrlH
d3ISaCOuP5NeYQdTsQ446bjiNk7LT25gNHF6U6WjmLQ3lXCz4kNoBG6fiOKIRgQY
EQIABgUCQqoSPwAKCRASsodYDHxPBQ7wAKD4DO0Se4+SvSqUKDfxhI0lJTgK0gCg
0lICv0KXT6PKyTndK+lr6K2AL4o=
=GKga
-----END PGP PUBLIC KEY BLOCK-----
pub 1024D/2E114322 2005-06-11 [expires: 2010-06-10]
Key fingerprint = A1A2 B554 6D43 31B2 A41E 1C07 BE16 C95D 2E11 4322
uid Curt Arnold <carnold@apache.org>
sig 3 2E114322 2010-02-17 Curt Arnold <carnold@apache.org>
sub 2048g/209ECE57 2005-06-11 [expires: 2010-06-10]
sig 2E114322 2005-06-11 Curt Arnold <carnold@apache.org>
pub 4096R/70C9C3D0 2010-02-17 [expires: 2012-02-17]
Key fingerprint = 28F5 F554 39C7 1A8F 2B1E 58C4 D3EC 4990 70C9 C3D0
uid carnold@apache.org (CODE SIGNING KEY) <carnold@apache.org>
sig 3 70C9C3D0 2010-02-17 carnold@apache.org (CODE SIGNING KEY) <carnold@apache.org>
sig 2E114322 2010-02-17 Curt Arnold <carnold@apache.org>
sub 4096R/0E434FF3 2010-02-17 [expires: 2012-02-17]
sig 70C9C3D0 2010-02-17 carnold@apache.org (CODE SIGNING KEY) <carnold@apache.org>
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG/MacGPG2 v2.0.14 (Darwin)
mQGiBEKrSNQRBAC4J7udOBoC5+gVxBaPAbjXfnq12l5Pau1WD+UothePNGjI2hOp
+Rnzikk3ISgyrjiX3A8ScZYbu3iXvMpF4zknkGLdmerpf4Gz9xGeushwun+UFaFL
MX5u7LWJo9wDKzbcJJit1j/qGEg/HRp5fnVYCh0/l4dLansL60NhxtYdxwCguu2e
wZMZFroaiIXqnce7+cGDRq8D/2HgKGtEJHY3z8OtUqncWbW+RAQqdcT0Z+bMB8o6
0UCHxUoJrFS1lA62qU3kcZ8ACPoh9xDW4X47EgNPELX81alymTI5FdqiDK7RIwzE
JlOH/8JJgC6eSwiUXJ0cOJwpMonitcpMLouxuURuPSpfE5b1mQ1gFzN5MBL8xlZQ
8IO6A/9qWwyWyQBoJud0RDIsVRosdoSBZtw9PHsURgsqfNsS2NXTWK4HjxExw1KO
AXmRlALfrH8yAShy/AyiUrwlKHG2WPTe6Etygjlr4dIxqTiCOoi+qv+H8SXW4Qy3
SnyozJ2RlKoYG0oDTbVMsPhOFdytHjConDLL9vS14j4kN9zWB7QgQ3VydCBBcm5v
bGQgPGNhcm5vbGRAYXBhY2hlLm9yZz6IZwQTEQIAJwIbAwUJCWYBgAIeAQIXgAUC
S3t0VgULCQgHAwUVCgkICwUWAgMBAAAKCRC+FsldLhFDIrS6AJ92lpskwBHNNWLw
XsHLaFSGh9n9tACfT0dOv3wec7oM+lt2x81S0uqcbZq5Ag0EQqtJBRAIAN8maiGI
O44Sdc9Ep3CAm0aXDeR8IQ/F253WcMQtkFBjeHEDd6/+EFT52vswMI6ZJDVV/A7p
e4VMXAdNutFmUG2gy9OJOu8gMuO3jTCLxUXyQYNF/RasOAQJgc7q1N5QgKtXVH2I
nQ21vHvlHM1fVe4rYDPr4JL2lZHe0P8kTzeQ7jI5pQnfYRJmS8I5AMQYFOiM48Pd
7SbsWu/rym7ikcmKUe6ZE59hSioneVP31CDMNRxCAQJVS1mZxTozsAEoh+cvmRjO
D1Es0iXvu6Sfe8+sLRL+7CNUZgixE1UFbdnNxuZGlG9qs0LGP7hDWijT1/Y4SHz4
ovXxk0oocmFtiLcAAwcIAMbY7K99hLAFVaU2ukxCSp1TNPcD+IB3gtpRieKaZvBn
/LGeCO+fNAogkw537lmpLk4nI+JiP/xWohyJ9lyEpW7yD4c9AHKNjqvEWD5Bhpnw
4qKJohQSVQwEeJRsftY4D0jCP9xbgPiq5woBzHWNok3BVaHqLK0fd0/+KygnT+k6
cR22Mus9RsEisXk9Oj5lvC0miDOWof4vk2Ll8/H3xt4CXAr13n5Yj2632HolOHrF
UQXTgwc9v5CNIihOQMEiXFxHh743qbsUZktjxeYH7r8wSCV93/QQ4qELiWoUzndp
kCRTlEKenucAv6f5qqZqG7pVW8S48T99HwzwqgFX5VOITwQYEQIADwUCQqtJBQIb
DAUJCWYBgAAKCRC+FsldLhFDIjnoAJ9ECOIrTH3adnVLOkHZnewyp2ssxwCgtLjl
wZ7/4QtL3W5Id3nKxPFiI+eZAg0ES3tyxgEQAMiGGFkj5EOkQv6EYFtWvd65Y6yn
dpNIbOb/o9SszVu4kO1OHdemWg4OwXERb1+Ozl9NamOvW0ypH8qzFu4rhpoiUev0
DV93afBkhW6Mvd8D459okgnlfb/K7c3H72gHmuJKZkyNmLgyzX63BjgIbpjR83/N
RC578BjoJbYCe0bKR9bCE+MxxCQzUO+tcUrk8o00zEhqycmw1B7gFLWL5MAf50h8
37/gbCcv9Q902kYfqsd5I7wPj4SRPnowN8vPlNtiwqMDqCl5rUmCrulfvSoNaJ/1
bEu7L9+/ptontfnr3kMJksGsoPo+Cf8xkGZL1D+80TxrBRf4CILQRF8dm+K8+4ms
VMsPccCINw/AZh+GkIjJ+SfQpIE4Rx6OMd6JSmCuMnom4O8ZbJ9QYcuS1uf4r32M
C6MQZrENvEFj1MaSZoRPXNdI/vwCKE5Hc1enB7bEHD6yayE3SgIGYVC+YPJkh0Ch
fAANRSlw7arrL13UB6xwzAnzhIK7BzADsbGDMAMmIh+rvb6Xxb6iQCh/q7iGPpRz
AXkmusLsbk6hMP4d8W5puJQTdy7a4t2DIX//9LYpl+DeYCww9DbXTwIays/WLnpo
Jxj8R1G75gfUIC+kvFl+OiIOR7579yw5Ar35DmqG8RIwxW9cv+at3u63WhYwdhqB
4Dm5hujWYluYoAZvABEBAAG0OmNhcm5vbGRAYXBhY2hlLm9yZyAoQ09ERSBTSUdO
SU5HIEtFWSkgPGNhcm5vbGRAYXBhY2hlLm9yZz6JAjwEEwEKACYFAkt7csYCGwMF
CQPCZwAFCwkIBwMFFQoJCAsEFgIBAAIeAQIXgAAKCRDT7EmQcMnD0FNcD/oDWhmZ
sikg42VV4SnaaIGUgjHoNT5NKWHhgeEXWAu44Y1r9X0+a6/xvtr69MfbExn9CJWJ
6vuyY9bpLcNNB49NLxhKD/IEpAUdGYcGGn1+AkkX/Sa7Y2SE5F9snJ2aSn+BctdP
JNHgEQxWGXGHAFPEdw73RudqsDUvcIat+uU8H/ZdrXX1F2NiUp7rjHqYvizXgy5p
Es99n00gCiz3eKg2l816v1ClDRAufFDj/JHghL4Yx+wUJN0cFBOAZHzDZgtcSUuv
Jb2B2f31vjqJ5cGzSQ1KhRIqoIwUm+LYOtuCAbboW9P9GPbib6oavRXRbz/SAoIX
BbYGWnA4aYQGT1r/FrLHZOmY6kw2izOPryXXxu1qAblFDc1C6XNHtNWx3uuTE/TL
haePHai9N0rJLuJCwHSveY5Lc8LF6cE2sV+kFsyMUwzaxLHg4G2XpnVIx5mEM+Fr
Hb89t8rThdE2n+qwUgCzlYgNEjAb1wf/xqUwXL7r/3rwKYkamq7Y7ljY9BTRX6VY
XsDEiCPs2XSNQtZfTajkei/WN0/SJDQZfIbK9wPqQXTA4DxZ5qJaTa5hS/ueksYH
tCK6pH3L8ov92ZbDcfxwq3u9wGl3GT1abFIfyfX53FXaoCXSi+g7F+a3hv6nmy0E
1zT26YXh3xRIPJ2x8gGCATHsGHabfnrRespQeYhGBBARCgAGBQJLe3TDAAoJEL4W
yV0uEUMianUAn1+KjhbSeXIcMrxWK6WaCHojo0StAJ9GiDT+6t4AnRXCIJtob6uZ
tN17tLkCDQRLe3LGARAAq9WmSZRtHLuOG8gmYesNeyfPqTdOgak+dgT4nnMIMnlN
Y23k3oMIgSBeiYyFMDoTXPYsxVZW24mrMhi9D1XhsDygn534iD94ptrLpnDG+56z
0aHdGjS8eUhEK636uNLT47ZEm8997fWCiqQQzX7UP9ttP9DKLkseiZ97jtN5To+7
n5Kmog082k0208p2jJqVw9aqoeXh4520nRaqiD2QQg2onQLCAz7UH7RllVzhqH9Z
G3HcI0RXw3q6FP33XlGNQuxhV9IPna9/43TXHaOsstwtJjvOaPfzGDS7BAmIKZx6
rbL7VYcIheZ+qXuVoRrwb0facxJfkTcLUy8aRQappDxk9FpWByEK2hEsWiw+CkI6
maFoyCvC/io5L3ZwWL3C+7wMZ1Qi8DvT93NbLboxhW5XRZk9gb1vlJSalTMbGNUV
tOiV+SbwfkMQX1xOMFbDx2u5orcJ0h9/Ox1LJEuoTHx68nXLB1e9ioLfQPATM+70
JhXGAHmjp8rkY6Jdua0MbamQtUXfJAZv/pJrukWNWmT5UFYY0DNNcypfaqm0l4g9
ZFwwUaraWrMkcsrvmmZ+6JASSwA1a6RwXYLtsMLX0boaUKEvDPqnIbeF3jJnztLN
MDWsGeW2NXYfuIaHMTUM7oVGgpDP+TG1IlOQU0lrSbw4Sm3AlA25tOF3jgGZtaMA
EQEAAYkCJQQYAQoADwUCS3tyxgIbDAUJA8JnAAAKCRDT7EmQcMnD0MzLEACRQ6Em
nkRbNupza9airUj2vrXj4I5ZN9jvgnvZcBVSmbKDEZR0akHFXaEes4d9AVh95y91
HuOHiiJ1hUZ0TGlk52ML5FEE68dDxvORUven9DiUdTAgLBJ5rUvfzj3tf6R4BNmM
rzyEA0UCAthqTd1nCdjhwIT6z4BiEVGsBHVAN7mYl+blHqssvuRgNAWrdPFRGYJP
cXI7wQc051WCHWXzi6b4h7oiIwlfsx7818S7ITHGM1AxTFl5rIqs9K4oHFTbcg1g
sLbJFM0NJ4l1yQZG/9Vldo0RmDdMSc5KgwPa48nf8LHsL3qtcQuvy/b1CIhf8UqZ
OWabhVa25xQFTRPmN5ND6wAsvx/zYr0/NF7RfJ3gTn8jCif8LZLVtYYP8zj4TgIL
ZhP46YwxaLkyibh+7/R+obszGVt06yqefNYuzfAXpX+2/pDUVQ532AAVD5g4enX7
eAxMda//UAC+WxfCqE6c3CWhXlGaborCsP5auhBg0rbi3/dgPySwMugBfVksa16X
X6y36kjw1MRZuhJi4WQGRLX4yjd6sGnWDoY5c/4G89JZKzjWsuNrSJIZRWrzgn84
NLAXpK81RMPkWukePN4ZQ1WEi0IH6AbBBbmQhIRR1bnGR48ZXH+/piJTNi9yKOPi
wlp15dJcNo0puq0ZbuZjPboQjg8PQaGtlMVN6A==
=EKia
-----END PGP PUBLIC KEY BLOCK-----

5
thirdparty/apache-log4j-1.2.16/NOTICE vendored Normal file
View File

@ -0,0 +1,5 @@
Apache log4j
Copyright 2007 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,71 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
#
#
#
# Providing a build.properties file is no longer
# necessary for an Ant build as long as one Maven build
# has previously been performed.
#
# base location of support directories
#
lib.home.dir=/java
# The jaxp interface and a jaxp parser are required
# to build the DOMConfigurator.
#
# modern equivalent is xml-commons-apis.jar
#
jaxp.home=${lib.home.dir}/crimson-1.1.3
jaxp.jaxp.jar=${jaxp.home}/crimson.jar
# JavaMail API Required to build the SMTPAppender
javamail.jar=${lib.home.dir}/javamail-1.3.2/mail.jar
# and JavaBeans Activation Framework
# http://java.sun.com/products/javabeans/jaf/index.jsp
activation.jar=${lib.home.dir}/jaf-1.0.2/activation.jar
# JMS interfaces are required to be on the classpath
# in order to build the JMSAppender.
jms.jar=${lib.home.dir}/jms1.1/lib/jms.jar
# Required to build the org.apache.log4j.jmx package.
jmx.home.dir=${lib.home.dir}/jmx-1_2_1-bin
jmx.jar=${jmx.home.dir}/lib/jmxri.jar
jndi.jar=${lib.home.dir}/jndi-1_2_1/lib/jndi.jar
# Required to run Checkstyle. Available from http://checkstyle.sf.net
checkstyle.jar=${lib.home.dir}/checkstyle-2.2/checkstyle-all-2.2.jar
# Velocity's Anakia task is used in the generation of the documentation
# download from http://jakarta.apache.org
velocity.jar=${lib.home.dir}/velocity-1.4/velocity-dep-1.4.jar
# Velocity's Anakia task requires JDOM,
# but Velocity 1.4 is not compatible with JDOM 1.0, but beta 8 works okay
# download for http://www.jdom.org/dist/binary/archive
jdom.jar=${lib.home.dir}/jdom-b8/build/jdom.jar
#
# CLIRR binary compatibility checker
# http://clirr.sourceforge.net
clirr-core.jar=${lib.home.dir}/clirr-0.6/clirr-core-0.6.jar
# bcel 5.1 will throw NullPointerExceptions
bcel.jar=${lib.home.dir}/bcel-5.2/bcel-5.2.jar

804
thirdparty/apache-log4j-1.2.16/build.xml vendored Normal file
View File

@ -0,0 +1,804 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<!-- This file is an ANT build script. ANT is a Java based build tool. -->
<!-- It is available from http://ant.apache.org -->
<project name="log4j" default="usage" basedir="." >
<!-- The build.properties file defines the path to local jar files -->
<property file="build.properties"/>
<!-- if a Maven build has been attempted,
the Maven repository can provide all the dependencies. -->
<property name="m2_repo" location="${user.home}/.m2/repository"/>
<property name="javamail.jar" location="${m2_repo}/javax/mail/mail/1.4/mail-1.4.jar"/>
<property name="geronimo-jms.jar"
value="${m2_repo}/org/apache/geronimo/specs/geronimo-jms_1.1_spec/1.0/geronimo-jms_1.1_spec-1.0.jar"/>
<available property="jms.jar" value="${geronimo-jms.jar}" file="${geronimo-jms.jar}"/>
<property name="jms.jar" location="${m2_repo}/javax/jms/jms/1.1/jms-1.1.jar"/>
<property name="jmx.jar" location="${m2_repo}/com/sun/jmx/jmxri/1.2.1/jmxri-1.2.1.jar"/>
<!-- Read the system environment variables and stores them in properties, -->
<!-- prefixed with "env". -->
<property environment="env"/>
<property name="version" value="1.2.15"/>
<!-- The base directory relative to which most targets are built -->
<property name="base" value="."/>
<property name="deprecation" value="on"/>
<!-- The directory where java source files are stored. -->
<property name="java.source.dir" value="src/main/java/"/>
<!-- The directory where resource files are stored. -->
<property name="resource.source.dir" value="src/main/resources/"/>
<!-- The directory where javadoc files are stored. -->
<property name="javadoc.source.dir" value="src/main/javadoc/"/>
<!-- The directory where the package-list file is found, ./ or -->
<!-- build/ -->
<property name="packaging.dir" value="build"/>
<!-- distribution directory -->
<property name="dist.dir" value="dist"/>
<!-- Destination for compiled files -->
<property name="javac.dest" value="${dist.dir}/classes"/>
<!-- Destination for generated jar files -->
<property name="jar.dest" value="${dist.dir}/lib"/>
<!-- The jar file that the jar task will generate -->
<property name="jar.filename" value="log4j-${version}.jar"/>
<property name="sources-jar.filename" value="log4j-${version}-sources.jar"/>
<!-- Destination for documentation files -->
<property name="docs.dest" value="./docs"/>
<!-- Source directory for xml docs -->
<property name="xdocs.src" value="./src/xdocs"/>
<!-- Destination for javadoc generated files -->
<property name="javadoc.dest" value="docs/api"/>
<!-- Icons source directory. -->
<property name="icons.source" value="icons"/>
<!-- The stem where most log4j source code is located. -->
<property name="stem" value="org/apache/log4j"/>
<!-- Some targets needs a more precise stem. -->
<property name="BSTEM" value="${java.source.dir}/${stem}"/>
<!-- Directory where release images go. -->
<property name="dist.images" value="${dist.dir}/images"/>
<!-- Directory for temporary files. -->
<property name="dist.tmp" value="${dist.dir}/tmp"/>
<property name="javac.source" value="1.2"/>
<property name="javac.target" value="1.1"/>
<!-- destination for generated documentation on Apache web host -->
<property name="apache.javadoc_dest" value="/www/logging.apache.org/log4j/docs"/>
<!-- Apache web host -->
<property name="apache.host" value="people.apache.org"/>
<!-- destination for Maven generated documentation -->
<property name="svnrepo.url" value="https://svn.apache.org/repos/asf"/>
<property name="svnsite.url" value="${svnrepo.url}/logging/site/trunk/docs/log4j/1.2"/>
<available property="svn-available" file="target/site-deploy/.svn"/>
<!-- Construct compile classpath -->
<path id="compile.classpath">
<pathelement location="${build.home}/classes"/>
<pathelement location="${javamail.jar}"/>
<pathelement location="${activation.jar}"/>
<pathelement location="${jaxp.jaxp.jar}"/>
<pathelement location="${jms.jar}"/>
<pathelement location="${jmx.jar}"/>
<pathelement location="${jndi.jar}"/>
<pathelement location="${javaee-api.jar}"/>
</path>
<!-- ================================================================= -->
<!-- Default target -->
<!-- ================================================================= -->
<target name="usage">
<echo>
These are the targets supported by this ANT build scpript:
build - compile all project files, if a certain library is missing,
then the compilation of its dependents are skipped.
javadoc - build project javadoc files
jar - build log4j-core and log4j jar files
dist - will create a complete distribution in dist/
Setting the env variable NO_JAVADOC will build the distribution
without running the javadoc target.
release - will create a complete distribution in dist/
using stricter settings for public distribution.
</echo>
</target>
<target name="jaxpCheck">
<available classname="javax.xml.parsers.DocumentBuilderFactory"
property="jaxp-present"/>
</target>
<target name="jaxp" depends="jaxpCheck" if="jaxp-present">
<echo message="JAXP present."/>
</target>
<target name="jmxCheck">
<condition property="jmx-present" value="true">
<and>
<available classname="javax.management.MBeanInfo"
classpath="${jmx.jar}"/>
<available classname="com.sun.jdmk.comm.HtmlAdaptorServer">
<classpath>
<pathelement location="${jmx.jar}"/>
<pathelement location="${javaee-api.jar}"/>
</classpath>
</available>
</and>
</condition>
</target>
<target name="jmx" depends="jmxCheck" if="jmx-present">
<echo message="JMX is present."/>
</target>
<target name="jmsCheck">
<available classname="javax.jms.Message" property="jms-present">
<classpath>
<pathelement location="${jms.jar}"/>
<pathelement location="${javaee-api.jar}"/>
</classpath>
</available>
</target>
<target name="jms" depends="jmsCheck" if="jms-present">
<echo message="JMS is present."/>
</target>
<target name="jndiCheck">
<available classname="javax.naming.Context" property="jndi-present"/>
</target>
<target name="jndi" depends="jndiCheck" if="jndi-present">
<echo message="JNDI is present."/>
</target>
<target name="javamailCheck">
<available classname="javax.mail.MessageAware" property="javamail-present">
<classpath>
<pathelement location="${javamail.jar}"/>
<pathelement location="${javaee-api.jar}"/>
</classpath>
</available>
</target>
<target name="javamail" depends="javamailCheck" if="javamail-present">
<echo message="JAVAMAIL is present."/>
</target>
<!-- ================================================================= -->
<!-- Initialize variables -->
<!-- NOTE: all directories are relative to jakarta-log4j/ -->
<!-- ================================================================= -->
<target name="init">
<tstamp />
<property name="javac.includeAntRuntime" value="true"/>
<property name="javac.includeJavaRuntime" value="false"/>
<property name="javac.fork" value="false"/>
</target>
<target name="build" depends="init, build.core, build.examples, build.xml,
build.javamail, build.jms, build.jmx"/>
<target name="build.core" depends="init">
<mkdir dir="${javac.dest}" />
<javac srcdir="${java.source.dir}"
destdir="${javac.dest}"
includes="${stem}/**/*.java,
${stem}/xml/XMLLayout.java"
excludes="misc/*, **/UnitTest*.java,
**/StressCategory.java,
**/doc-files/*,
${stem}/xml/**,
${stem}/test/serialization/**,
${stem}/net/SMTPAppender.java,
${stem}/net/JMS*.java,
${stem}/jmx/*.java,
${stem}/or/jms/*.java"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}"
debug="on">
<classpath refid="compile.classpath"/>
</javac>
<copy todir="${javac.dest}">
<fileset dir="${resource.source.dir}" includes="${stem}/lf5/**/*.properties"/>
<fileset dir="${resource.source.dir}" includes="${stem}/lf5/viewer/images/*"/>
</copy>
</target>
<target name="build.examples" depends="build.core">
<mkdir dir="${javac.dest}" />
<javac srcdir="${basedir}"
destdir="${javac.dest}"
includes="examples/**/*.java"
excludes="misc/*"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}"
debug="on">
<classpath>
<pathelement path="${classpath}"/>
<fileset file="${jaxp.jaxp.jar}"/>
</classpath>
</javac>
<rmic base="${javac.dest}" classname="examples.NumberCruncherServer"/>
<copy todir="${javac.dest}">
<fileset dir="." includes="examples/lf5/**/*.properties"/>
<fileset dir="." includes="examples/lf5/**/*.xml"/>
</copy>
</target>
<target name="build.xml" depends="init, jaxp" if="jaxp-present">
<javac srcdir="${java.source.dir}"
destdir="${javac.dest}"
includes="${stem}/xml/**/*.java"
excludes="${stem}/xml/examples/doc-files/**.java,
${stem}/xml/Transform.java"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}"
classpath="${classpath}">
<classpath refid="compile.classpath"/>
</javac>
<copy file="${resource.source.dir}/${stem}/xml/log4j.dtd" tofile="${javac.dest}/${stem}/xml/log4j.dtd" />
</target>
<target name="build.javamail" depends="init, javamail"
if="javamail-present">
<javac srcdir="${java.source.dir}"
destdir="${javac.dest}"
includes="${stem}/net/SMTPAppender.java"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}">
<classpath refid="compile.classpath"/>
</javac>
</target>
<target name="build.jms" depends="init, jms, jndi" if="jms-present">
<javac srcdir="${java.source.dir}"
destdir="${javac.dest}"
includes="${stem}/net/JMS*.java, ${stem}/or/jms/*.java"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}">
<classpath refid="compile.classpath"/>
</javac>
</target>
<target name="build.jmx" depends="init, jmx, jndi" if="jmx-present">
<javac srcdir="${java.source.dir}"
destdir="${javac.dest}"
includes="${stem}/jmx/*.java"
excludes="${stem}/jmx/T.java"
target="${javac.target}"
source="${javac.source}"
includeAntRuntime="${javac.includeAntRuntime}"
includeJavaRuntime="${javac.includeJavaRuntime}"
fork="${javac.fork}"
deprecation="${deprecation}">
<classpath refid="compile.classpath"/>
</javac>
</target>
<target name="build.nt" depends="log4j.jar" description="Build NTEventLogAppender.dll">
<mkdir dir="${dist.dir}/object"/>
<ant antfile="src/ntdll/build.xml">
<property name="target.dir" location="${dist.dir}/lib"/>
<property name="object.dir" location="${dist.dir}/object"/>
<property name="classes.dir" location="${javac.dest}" />
<property name="src.dir" location="src/ntdll" />
<property name="jni.include.dir" location="${java.home}/../include" />
</ant>
</target>
<!-- ================================================================= -->
<!-- Remove all generated (compiled) class files. -->
<!-- ================================================================= -->
<target name="clean" depends="init">
<delete dir="${dist.dir}/" />
</target>
<!-- ================================================================= -->
<!-- Runs checkstyle. Available from http://checkstyle.sf.net -->
<!-- ================================================================= -->
<target name="checkstyle" depends="init">
<taskdef resource="checkstyletask.properties"
classpath="${checkstyle.jar}"/>
<!-- by default checkstyle supports the Sun coding standard. -->
<checkstyle lcurlyMethod="nlow"
lcurlyOther="nlow"
lcurlyType="nlow"
maxMethodLen="500"
maxConstructorLen="500">
<fileset dir="src/java/org/apache/log4j/chainsaw" includes="**/*.java"/>
</checkstyle>
</target>
<!-- ================================================================= -->
<!-- Runs Chainsaw -->
<!-- ================================================================= -->
<target name="chainsaw" depends="build">
<!-- Need to fork to avoid problems -->
<java classname="org.apache.log4j.chainsaw.Main" fork="yes"
classpath="${javac.dest};${ant.home}/lib/crimson.jar">
</java>
</target>
<!-- ================================================================= -->
<!-- Remove the temporary manifest file, actual work is done in the -->
<!-- dependencies. -->
<!-- ================================================================= -->
<target name="jar" depends="log4j.jar, log4j-sources.jar">
</target>
<!-- ================================================================= -->
<!-- Create log4j.jar, excluding tests and other odds and ends. -->
<!-- ================================================================= -->
<target name="log4j.jar" depends="build">
<mkdir dir="${jar.dest}"/>
<delete>
<fileset dir="${jar.dest}">
<include name="${jar.filename}"/>
</fileset>
</delete>
<jar jarfile="${jar.dest}/${jar.filename}" basedir="${javac.dest}"
includes="${stem}/*.class,
${stem}/xml/log4j.dtd,
${stem}/config/*.class,
${stem}/helpers/*.class,
${stem}/spi/*.class,
${stem}/net/*.class,
${stem}/jdbc/*.class,
${stem}/varia/*.class,
${stem}/chainsaw/*.class,
${stem}/lf5/**/*.class,
${stem}/lf5/**/*.properties,
${stem}/lf5/**/*.gif,
${stem}/nt/*.class,
${stem}/xml/*.class,
${stem}/jmx/*.class,
${stem}/or/*.class,
${stem}/or/sax/*.class,
${stem}/or/jms/*.class,
${stem}/config/*.class"
excludes="**/UnitTest**">
<manifest>
<section name="org/apache/log4j">
<attribute name="Implementation-Title" value="log4j"/>
<attribute name="Implementation-Version" value="${version}"/>
<attribute name="Implementation-Vendor" value='"Apache Software Foundation"'/>
</section>
</manifest>
<metainf dir="." includes="NOTICE,LICENSE"/>
</jar>
</target>
<!-- ================================================================= -->
<!-- Create log4j-sources.jar -->
<!-- ================================================================= -->
<target name="log4j-sources.jar">
<mkdir dir="${jar.dest}"/>
<delete>
<fileset dir="${jar.dest}" includes="${sources-jar.filename}"/>
</delete>
<jar jarfile="${jar.dest}/${sources-jar.filename}" basedir="${java.source.dir}"
includes="**/*.java">
<manifest>
<section name="org/apache/log4j">
<attribute name="Implementation-Title" value="log4j"/>
<attribute name="Implementation-Version" value="${version}"/>
<attribute name="Implementation-Vendor" value='"Apache Software Foundation"'/>
</section>
</manifest>
<metainf dir="." includes="NOTICE,LICENSE"/>
</jar>
</target>
<!-- ================================================================= -->
<!-- This target builds the javadoc files. -->
<!-- ================================================================= -->
<target name="javadoc" depends="init" unless="env.NO_JAVADOC">
<mkdir dir="${javadoc.dest}" />
<javadoc sourcepath="${java.source.dir}"
destdir="${javadoc.dest}"
packagenames="org.apache.log4j,
org.apache.log4j.config,
org.apache.log4j.helpers,
org.apache.log4j.jmx,
org.apache.log4j.lf5,
org.apache.log4j.net,
org.apache.log4j.nt,
org.apache.log4j.or,
org.apache.log4j.jdbc,
org.apache.log4j.or.sax,
org.apache.log4j.or.jms,
org.apache.log4j.performance,
org.apache.log4j.spi,
org.apache.log4j.varia,
org.apache.log4j.chainsaw,
org.apache.log4j.xml,
org.apache.log4j.xml.examples"
version="true"
protected="true"
author="true"
use="true"
overview="${docs.dest}/overview.html"
doctitle="log4j version ${version}&lt;br&gt;API Specification"
windowtitle="Log4j Version ${version}"
header='&lt;b&gt;Log4j ${version}&lt;/b&gt;&lt;!-- Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the &apos;License&apos;); 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 &apos;AS IS&apos; 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.
--&gt;'
bottom="Copyright 2000-2007 Apache Software Foundation.">
<link href="http://java.sun.com/j2se/1.3/docs/api/"/>
<link href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/"/>
<classpath refid="compile.classpath"/>
</javadoc>
<!-- Extra files referenced by JavaDocs -->
<copy todir="${javadoc.dest}">
<fileset dir="${javadoc.source.dir}"/>
</copy>
</target>
<!-- ================================================================= -->
<!-- Compare the current API with a previous release -->
<!-- ================================================================= -->
<target name="jdiff" description="Generate comparison to reference API">
<property name="reference-api.version" value="1.2.9"/>
<property name="reference-api.dir" location="${user.home}/logging-log4j-${reference-api.version}"/>
<property name="reference-api.source.dir" location="${user.home}/logging-log4j-${reference-api.version}/src/java"/>
<!-- Must be set to the root of where JDiff is installed. -->
<property name="JDIFF_HOME" value="${user.home}/jdiff"/>
<!-- Just one simple way to tell Ant about the JDiff task -->
<taskdef name="jdiff" classname="jdiff.JDiffAntTask"
classpath="${JDIFF_HOME}/lib/antjdiff.jar"/>
<jdiff verbose="on" destdir="build/jdiff">
<old name="Version ${reference-api.version}">
<dirset dir="${reference-api.source.dir}" includes="org/**"/>
</old>
<new name="Version ${version}">
<dirset dir="${java.source.dir}" includes="org/**" excludes="org/apache/log4j/test/**"/>
</new>
</jdiff>
</target>
<!-- ================================================================= -->
<!-- Compare the current API with a previous release -->
<!-- ================================================================= -->
<target name="clirr" depends="jar" description="Check binary compatibility with arbitrary release">
<property name="clirr.version" value="0.6"/>
<property name="clirr-core.jar" value="${m2_repo}/net/sf/clirr/clirr-core/${clirr.version}/clirr-core-${clirr.version}.jar"/>
<available property="clirr-core.jar-available" file="${clirr-core.jar}"/>
<fail unless="clirr-core.jar-available">clirr-core-${clirr.version}.jar not in maven repo. Run mvn clirr:check to download.</fail>
<property name="bcel.version" value="5.1"/>
<property name="bcel.jar" value="${m2_repo}/bcel/bcel/${bcel.version}/bcel-${bcel.version}.jar"/>
<available property="bcel.jar-available" file="${bcel.jar}"/>
<fail unless="bcel.jar-available">bcel-${bcel.version}.jar not in maven repo. Run mvn clirr:check to download.</fail>
<property name="reference.version" value="1.2.14"/>
<property name="reference.jar" value="${m2_repo}/log4j/log4j/${reference.version}/log4j-${reference.version}.jar"/>
<available property="reference.jar-available" file="${reference.jar}"/>
<fail unless="reference.jar-available">log4j-${reference.version}.jar not in maven repo. Run mvn clirr:check to download.</fail>
<taskdef resource="clirrtask.properties"
classpath="${clirr-core.jar};${bcel.jar}"/>
<delete dir="target/clirr"/>
<mkdir dir="target/clirr/orig"/>
<mkdir dir="target/clirr/new"/>
<copy todir="target/clirr/orig">
<fileset file="${jms.jar}"/>
<fileset file="${jmx.jar}"/>
<fileset file="${reference.jar}"/>
</copy>
<copy todir="target/clirr/new">
<fileset file="${jms.jar}"/>
<fileset file="${jmx.jar}"/>
<fileset dir="${jar.dest}" includes="${jar.filename}"/>
</copy>
<clirr failOnBinError="no" failOnSrcError="no">
<origfiles dir="target/clirr/orig" includes="*.jar"/>
<newfiles dir="target/clirr/new" includes="*.jar"/>
<formatter type="xml" outfile="target/clirr/compatibility.xml"/>
</clirr>
</target>
<!-- ============================================== -->
<!-- Build the site files using Maven -->
<!-- ============================================== -->
<target name="site">
<exec executable="mvn">
<arg value="site"/>
</exec>
</target>
<!-- ================================================================= -->
<!-- Build a complete distribution. Results go to ${dist.images} -->
<!-- ================================================================= -->
<target name="dist" depends="init, clean, javadoc, jar, site, build.nt">
<delete verbose="true">
<fileset dir=".">
<patternset>
<include name="**/*.bak"/>
<include name="${dist.dir}/velocity.log"/>
<include name="${BSTEM}/**/temp*"/>
<include name="${BSTEM}/performance/test"/>
<include name="${BSTEM}/test/current.*"/>
<include name="${BSTEM}/test/current.*"/>
<include name="${BSTEM}/examples/test"/>
<include name="${BSTEM}/test/logging.*"/>
<include name="${BSTEM}/test/log4j.properties"/>
<include name="{$BSTEM}/test/socket.lcf"/>
<include name="${BSTEM}/test/file"/>
<include name="${BSTEM}/test/output.*"/>
<include name="${BSTEM}/net/test/loop.log"/>
<include name="${BSTEM}/net/test/loop.log.1"/>
<include name="${jar.dest}/manifest.mf"/>
<include name="${javac.dest}/*.class"/>
</patternset>
</fileset>
</delete>
<mkdir dir="${dist.images}" />
<mkdir dir="${dist.tmp}/apache-log4j-${version}" />
<copy todir="${dist.tmp}/apache-log4j-${version}">
<fileset dir="${base}"
includes="src/main/java/**,
docs/**,
examples/**,
build/*,
build.xml,
build.properties.sample,
manifest.mf,
INSTALL,
LICENSE,
NOTICE,
${dist.dir}/lib/log4j-${version}.jar,
${dist.dir}/lib/NTEventLogAppender.dll,
${dist.dir}/classes/**,
contribs/**"
excludes="make/make.loc,
**/*.bak, **/goEnv.bat,
**/Makefile, **/goEnv.bat,
docs/pub-support/*,
${dist.dir}/classes/org/**,
src/main/java/org/apache/log4j/test/**/*,
**/.#*,
**/*.o, **/*.res, **/*.h, **/EventLogCategories.rc"/>
</copy>
<fixcrlf srcdir="${dist.tmp}/apache-log4j-${version}"
includes="build.sh" cr="remove"/>
<fixcrlf srcdir="${dist.tmp}/apache-log4j-${version}"
includes="build.bat" cr="add"/>
<chmod dir="${dist.tmp}/apache-log4j-${version}"
includes="build.sh" perm="+x"/>
<tar tarfile="${dist.images}/apache-log4j-${version}.tar.gz"
basedir="${dist.tmp}"
includes="apache-log4j-${version}/**"
compression="gzip" />
<zip zipfile="${dist.images}/apache-log4j-${version}.zip"
basedir="${dist.tmp}"
includes="apache-log4j-${version}/**" />
<delete dir="${dist.tmp}" />
</target>
<!-- ================================================================= -->
<!-- Build a complete distribution for release. -->
<!-- ================================================================= -->
<target name="release">
<property name="javac.includeAntRuntime" value="false"/>
<property name="javac.includeJavaRuntime" value="false"/>
<property name="javac.fork" value="true"/>
<property name="build.compiler" value="classic"/>
<antcall target="clean-site"/>
<antcall target="dist"/>
<!-- check that jar file is not missing
classes due to missing dependencies -->
<available property="jaxp-found"
classname="org.apache.log4j.xml.DOMConfigurator"
classpath="${jar.dest}/${jar.filename}"/>
<fail unless="jaxp-found" message="JAXP was not found."/>
<available property="jmx-found"
classname="org.apache.log4j.jmx.Agent"
classpath="${jar.dest}/${jar.filename}"/>
<fail unless="jmx-found" message="JMX was not found."/>
<available property="jms-found"
classname="org.apache.log4j.net.JMSAppender"
classpath="${jar.dest}/${jar.filename}"/>
<fail unless="jms-found" message="JMS or JNDI was not found."/>
<available property="javamail-found"
classname="org.apache.log4j.net.SMTPAppender"
classpath="${jar.dest}/${jar.filename}"/>
<fail unless="javamail-found" message="JAVAMAIL was not found."/>
<checksum forceOverwrite="yes" fileext=".md5">
<fileset dir="${dist.images}" includes="apache-log4j-${version}.tar.gz apache-log4j-${version}.zip"/>
</checksum>
<checksum forceOverwrite="yes" fileext=".sha1" algorithm="SHA">
<fileset dir="${dist.images}" includes="apache-log4j-${version}.tar.gz apache-log4j-${version}.zip"/>
</checksum>
<exec executable="gpg" dir="${dist.images}">
<arg value="--armor"/>
<arg value="--output"/>
<arg value="apache-log4j-${version}.tar.gz.asc"/>
<arg value="--detach-sig"/>
<arg value="apache-log4j-${version}.tar.gz"/>
</exec>
<exec executable="gpg" dir="${dist.images}">
<arg value="--armor"/>
<arg value="--output"/>
<arg value="apache-log4j-${version}.zip.asc"/>
<arg value="--detach-sig"/>
<arg value="apache-log4j-${version}.zip"/>
</exec>
</target>
<target name="fixcrlf" depends="init"
description="Fixes CR-LF and tabs in source files">
<fixcrlf srcDir="."
includes="**/*.java **/build.xml"
tab="remove"
tablength="3"/>
</target>
<target name="checkout-site" unless="svn-available">
<delete dir="target/site-deploy"/>
<exec executable="svn">
<arg value="co"/>
<arg value="${svnsite.url}"/>
<arg value="target/site-deploy"/>
</exec>
</target>
<target name="update-site" if="svn-available">
<exec executable="svn" dir="target/site-deploy" failonerror="true">
<arg value="update"/>
</exec>
</target>
<target name="post-site" depends="checkout-site, update-site"/>
<target name="mime=html">
<exec executable="svn">
<arg value="propset"/>
<arg value="svn:mime-type"/>
<arg value="text/html"/>
<arg value="${src.html}"/>
</exec>
</target>
<target name="mime=css">
<exec executable="svn">
<arg value="propset"/>
<arg value="svn:mime-type"/>
<arg value="text/css"/>
<arg value="${src.css}"/>
</exec>
</target>
<target name="site-deploy">
<!-- Add any new files (and generate innocuous warnings for the existing content) -->
<delete file="target/site-deploy/svn-commit.tmp~"/>
<exec executable="bash" dir="target/site-deploy" failonerror="true">
<arg line='-c "svn add --force *"'/>
</exec>
<taskdef name="foreach" classname="net.sf.antcontrib.logic.ForEach" />
<foreach target="mime=html" param="src.html">
<path>
<fileset dir="target/site-deploy" includes="**/*.html"/>
</path>
</foreach>
<foreach target="mime=css" param="src.css">
<path>
<fileset dir="target/site-deploy" includes="**/*.css"/>
</path>
</foreach>
<!-- requires that SVN_EDITOR, VISUAL or EDITOR being set to edit commit description -->
<exec executable="svn" dir="target/site-deploy" failonerror="true">
<arg value="commit"/>
</exec>
</target>
</project>

View File

@ -0,0 +1,116 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
----------------------------------------------------------------------
WARNING: The contents of the contribs/ directory is not guaranteed to
work properly. Some files might not even compile.
----------------------------------------------------------------------
Each directory corresponds to the name of an author containing his/her
contributions.
EirikLygre/
==========
DailyFileAppender1.java
DailyFileAppender extends FileAppender to use filenames formatted with
date/time information. The filename is recomputed every day at midnight.
Note that the filename doesn't have to change every day, making it possible
to have logfiles which are per-week or per-month.
JamesHouse/
==========
LogTextPanel.java
LogTextPanelExample.java
TextPanelAppender.java
JimMoore/
========
LoggingOutputStream.java
Allows the user to divert System.out and System.err to log4j.
WARNING: Be sure to read the included e-mails to understand the dangers of
WARNING: redirecting the console to LoggingOutputStream.
LeosLiterak/
===========
TempFileAppender.java
TempFileAppender creates new unique file for each logging statement.
MarkDouglas/
===========
SocketNode2.java
SocketServer2.java
Small changes to SocketServer and SockerNode to allow the client machine name
to be displayed in the logging output. This is important for us as we are
using a single logging server with several clients.
KevinSteppe/
===========
JDBCAppender.java
JDBCTest.java
KitchingSimon/
=============
DatagramStringAppender.java
DatagramStringWriter.java
logconfig.xml
SingleLineTracerPrintWriter.java
udpserver.pl
A set of files that implement an Appender which sends messages to a
remote host/port via UDP (datagram). Message formatting is
performed at the at the client end, mainly so that:
(a) the UDP server application does not have to be in java
(b) non-java clients can send messages to the same UDP server.
ThomasFenner/
============
Yet one more JDBCAppender.
JDBCAppender.java
JDBCConnectionHandler.java
JDBCIDHandler.java
JDBCLogger.java
Log4JTest.java
code_example1.java
code_example2.java
configfile_example.txt
Volker Mentzner/
===============
HTTPRequestHandler.java
Log4jRequestHandler.java
PluggableHTTPServer.java
RootRequestHandler.java
UserDialogRequestHandler.java
Allows users to configure log4j at runtime using a web-browser.

View File

@ -0,0 +1,245 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.JScrollPane;
import javax.swing.BoxLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Container;
/**
The AppenderTable illustrates one possible implementation of
an Table possibly containing a great many number of rows.
<p>In this particular example we use a fixed size buffer
(CyclicBuffer) although this data structure could be easily
replaced by dynamically growing one, such as a Vector. The required
properties of the data structure is 1) support for indexed element
access 2) support for the insertion of new elements at the end.
<p>Experimentation on my 1400Mhz AMD machine show that it takes
about 45 micro-seconds to insert an element in the table. This
number does not depend on the size of the buffer. It takes as much
(or as little) time to insert one million elements to a buffer of
size 10 as to a buffer of size 10'000. It takes about 4 seconds to
insert a total of 100'000 elements into the table.
<p>On windows NT the test will run about twice as fast if you give
the focus to the window that runs "java AppenderTable" and not the
window that contains the Swing JFrame. */
public class AppenderTable extends JTable {
static Logger logger = Logger.getLogger(AppenderTable.class);
static public void main(String[] args) {
if(args.length != 2) {
System.err.println(
"Usage: java AppenderTable bufferSize runLength\n"
+" where bufferSize is the size of the cyclic buffer in the TableModel\n"
+" and runLength is the total number of elements to add to the table in\n"
+" this test run.");
return;
}
JFrame frame = new JFrame("JTableAppennder test");
Container container = frame.getContentPane();
AppenderTable tableAppender = new AppenderTable();
int bufferSize = Integer.parseInt(args[0]);
AppenderTableModel model = new AppenderTableModel(bufferSize);
tableAppender.setModel(model);
int runLength = Integer.parseInt(args[1]);
JScrollPane sp = new JScrollPane(tableAppender);
sp.setPreferredSize(new Dimension(250, 80));
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
container.add(sp);
// The "ADD" button is intended for manual testing. It will
// add one new logging event to the table.
JButton button = new JButton("ADD");
container.add(button);
button.addActionListener(new JTableAddAction(tableAppender));
frame.setSize(new Dimension(500,300));
frame.setVisible(true);
long before = System.currentTimeMillis();
int i = 0;
while(i++ < runLength) {
LoggingEvent event = new LoggingEvent("x", logger, Level.ERROR,
"Message "+i, null);
tableAppender.doAppend(event);
}
long after = System.currentTimeMillis();
long totalTime = (after-before);
System.out.println("Total time :"+totalTime+ " milliseconds for "+
"runLength insertions.");
System.out.println("Average time per insertion :"
+(totalTime*1000/runLength)+ " micro-seconds.");
}
public
AppenderTable() {
this.setDefaultRenderer(Object.class, new Renderer());
}
/**
When asked to append we just insert directly into the model. In a
real appender we would use two buffers one for accumulating
events and another to accumalte events but after filtering. Only
the second buffer would be displayed in the table and made
visible to the user.*/
public
void doAppend(LoggingEvent event) {
((AppenderTableModel)getModel()).insert(event);
}
/**
The Renderer is required to display object in a friendlier from.
This particular renderer is just a JTextArea.
<p>The important point to note is that we only need *one*
renderer. */
class Renderer extends JTextArea implements TableCellRenderer {
PatternLayout layout;
public
Renderer() {
layout = new PatternLayout("%r %p %c [%t] - %m");
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
// If its a LoggingEvent than format it using our layout.
if(value instanceof LoggingEvent) {
LoggingEvent event = (LoggingEvent) value;
String str = layout.format(event);
setText(str);
} else {
setText(value.toString());
}
return this;
}
}
}
class AppenderTableModel extends AbstractTableModel {
CyclicBuffer cb;
AppenderTableModel(int size) {
cb = new CyclicBuffer(size);
}
/**
Insertion to the model always results in a fireTableDataChanged
method call. Suprisingly enough this has no crippling impact on
performance. */
public
void insert(LoggingEvent event) {
cb.add(event);
fireTableDataChanged();
}
/**
We assume only one column.
*/
public
int getColumnCount() {
return 1;
}
/**
The row count is given by the number of elements in the
buffer. This number is guaranteed to be between 0 and the buffer
size (inclusive). */
public int getRowCount() {
return cb.length();
}
/**
Get the value in a given row and column. We suppose that there is
only one colemn so we are only concerned with the row.
<p>Interesting enough this method returns an object. This leaves
the door open for a TableCellRenderer to render the object in
a variety of ways.
*/
public
Object getValueAt(int row, int col) {
return cb.get(row);
}
}
/**
The JTableAddAction is called when the user clicks on the "ADD"
button.
*/
class JTableAddAction implements ActionListener {
AppenderTable appenderTable;
Logger dummy = Logger.getLogger("x");
int counter = 0;
public
JTableAddAction(AppenderTable appenderTable) {
this.appenderTable = appenderTable;
}
public
void actionPerformed(ActionEvent e) {
counter++;
LoggingEvent event = new LoggingEvent("x", dummy, Level.DEBUG,
"Message "+counter, null);
appenderTable.doAppend(event);
}
}

View File

@ -0,0 +1,219 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.xml;
import org.apache.log4j.Category;
import org.apache.log4j.Layout;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.DateLayout;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.Attributes;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.XMLReaderFactory;
import org.xml.sax.SAXException;
import org.apache.xerces.parsers.SAXParser;
import org.apache.trax.Processor;
import org.apache.trax.TemplatesBuilder;
import org.apache.trax.Templates;
import org.apache.trax.Transformer;
import org.apache.trax.Result;
import org.apache.trax.ProcessorException;
import org.apache.trax.ProcessorFactoryException;
import org.apache.trax.TransformException;
import org.apache.serialize.SerializerFactory;
import org.apache.serialize.Serializer;
import org.apache.serialize.OutputFormat;
import org.xml.sax.helpers.AttributesImpl;
import java.io.FileOutputStream;
import java.io.IOException;
public class Transform {
public static void main(String[] args) throws Exception {
PropertyConfigurator.disableAll();
PropertyConfigurator.configure("x.lcf");
// I. Instantiate a stylesheet processor.
Processor processor = Processor.newInstance("xslt");
// II. Process the stylesheet. producing a Templates object.
// Get the XMLReader.
XMLReader reader = XMLReaderFactory.createXMLReader();
// Set the ContentHandler.
TemplatesBuilder templatesBuilder = processor.getTemplatesBuilder();
reader.setContentHandler(templatesBuilder);
// Set the ContentHandler to also function as a LexicalHandler, which
// includes "lexical" (e.g., comments and CDATA) events. The Xalan
// TemplatesBuilder -- org.apache.xalan.processor.StylesheetHandler -- is
// also a LexicalHandler).
if(templatesBuilder instanceof LexicalHandler) {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
templatesBuilder);
}
// Parse the stylesheet.
reader.parse(args[0]);
//Get the Templates object from the ContentHandler.
Templates templates = templatesBuilder.getTemplates();
// III. Use the Templates object to instantiate a Transformer.
Transformer transformer = templates.newTransformer();
// IV. Perform the transformation.
// Set up the ContentHandler for the output.
FileOutputStream fos = new FileOutputStream(args[2]);
Result result = new Result(fos);
Serializer serializer = SerializerFactory.getSerializer("xml");
serializer.setOutputStream(fos);
transformer.setContentHandler(serializer.asContentHandler());
// Set up the ContentHandler for the input.
org.xml.sax.ContentHandler chandler = transformer.getInputContentHandler();
DC dc = new DC(chandler);
reader.setContentHandler(dc);
if(chandler instanceof LexicalHandler) {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
chandler);
} else {
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
null);
}
// Parse the XML input document. The input ContentHandler and
// output ContentHandler work in separate threads to optimize
// performance.
reader.parse(args[1]);
}
}
class DC implements ContentHandler {
static Category cat = Category.getInstance("DC");
ContentHandler chandler;
DC(ContentHandler chandler) {
this.chandler = chandler;
}
public
void characters(char[] ch, int start, int length)
throws org.xml.sax.SAXException {
cat.debug("characters: ["+new String(ch, start, length)+ "] called");
chandler.characters(ch, start, length);
}
public
void endDocument() throws org.xml.sax.SAXException {
cat.debug("endDocument called.");
chandler.endDocument();
}
public
void endElement(String namespaceURI, String localName, String qName)
throws org.xml.sax.SAXException {
cat.debug("endElement("+namespaceURI+", "+localName+", "+qName+") called");
chandler.endElement(namespaceURI, localName, qName);
}
public
void endPrefixMapping(String prefix) throws org.xml.sax.SAXException {
cat.debug("endPrefixMapping("+prefix+") called");
chandler.endPrefixMapping(prefix);
}
public
void ignorableWhitespace(char[] ch, int start, int length)
throws org.xml.sax.SAXException {
cat.debug("ignorableWhitespace called");
chandler.ignorableWhitespace(ch, start, length);
}
public
void processingInstruction(java.lang.String target, java.lang.String data)
throws org.xml.sax.SAXException {
cat.debug("processingInstruction called");
chandler.processingInstruction(target, data);
}
public
void setDocumentLocator(Locator locator) {
cat.debug("setDocumentLocator called");
chandler.setDocumentLocator(locator);
}
public
void skippedEntity(String name) throws org.xml.sax.SAXException {
cat.debug("skippedEntity("+name+") called");
chandler.skippedEntity(name);
}
public
void startDocument() throws org.xml.sax.SAXException {
cat.debug("startDocument called");
chandler.startDocument();
}
public
void startElement(String namespaceURI, String localName, String qName,
Attributes atts) throws org.xml.sax.SAXException {
cat.debug("startElement("+namespaceURI+", "+localName+", "+qName+")called");
if("log4j:event".equals(qName)) {
cat.debug("-------------");
if(atts instanceof org.xml.sax.helpers.AttributesImpl) {
AttributesImpl ai = (AttributesImpl) atts;
int i = atts.getIndex("timestamp");
ai.setValue(i, "hello");
}
String ts = atts.getValue("timestamp");
cat.debug("New timestamp is " + ts);
}
chandler.startElement(namespaceURI, localName, qName, atts);
}
public
void startPrefixMapping(String prefix, String uri)
throws org.xml.sax.SAXException {
cat.debug("startPrefixMapping("+prefix+", "+uri+") called");
chandler.startPrefixMapping(prefix, uri);
}
}

View File

@ -0,0 +1,242 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j;
import java.io.IOException;
import java.io.Writer;
import java.io.FileWriter;
import java.io.File;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ErrorCode;
/**
DailyFileAppender extends FileAppender to use filenames formatted with
date/time information. The filename is recomputed every day at midnight.
Note that the filename doesn't have to change every day, making it possible
to have logfiles which are per-week or per-month.
The appender computes the proper filename using the formats specified in
<a href="http://java.sun.com/j2se/1.3/docs/api/java/text/SimpleDateFormat.html">
java.text.SimpleDateFormat</a>. The format requires that most static text is
enclosed in single quotes, which are removed. The examples below show how
quotes are used to embed static information in the format.
Sample filenames:
<code>
Filename pattern Filename
"'/logs/trace-'yyyy-MM-dd'.log'" /logs/trace-2000-12-31.log
"'/logs/trace-'yyyy-ww'.log'" /logs/trace-2000-52.log
</code>
@author <a HREF="mailto:eirik.lygre@evita.no">Eirik Lygre</a>
*/
public class DailyFileAppender extends FileAppender {
/**
A string constant used in naming the option for setting the
filename pattern. Current value of this string constant is
<strong>FileNamePattern</strong>.
*/
static final public String FILE_NAME_PATTERN_OPTION = "FilePattern";
/**
The filename pattern
*/
private String fileNamePattern = null;
/**
The actual formatted filename that is currently being written to
*/
private String currentFileName = null;
/**
The timestamp when we shall next recompute the filename
*/
private long nextFilenameComputingMillis = System.currentTimeMillis () - 1;
/**
The default constructor does no longer set a default layout nor a
default output target. */
public
DailyFileAppender() {
}
/**
Instantiate a RollingFileAppender and open the file designated by
<code>filename</code>. The opened filename will become the ouput
destination for this appender.
<p>If the <code>append</code> parameter is true, the file will be
appended to. Otherwise, the file desginated by
<code>filename</code> will be truncated before being opened.
*/
public DailyFileAppender (Layout layout,String filename,boolean append) throws IOException {
super(layout, filename, append);
}
/**
Instantiate a FileAppender and open the file designated by
<code>filename</code>. The opened filename will become the output
destination for this appender.
<p>The file will be appended to. */
public DailyFileAppender (Layout layout,String filename) throws IOException {
super(layout, filename);
}
/**
Set the current output file.
The function will compute a new filename, and open a new file only
when the name has changed.
The function is automatically called once a day, to allow for
daily files -- the purpose of this class.
*/
public
synchronized
void setFile(String fileName, boolean append) throws IOException {
/* Compute filename, but only if fileNamePattern is specified */
if (fileNamePattern == null) {
errorHandler.error("Missing file pattern (" + FILE_NAME_PATTERN_OPTION + ") in setFile().");
return;
}
Date now = new Date();
fileName = new SimpleDateFormat(fileNamePattern).format (now);
if (fileName.equals(currentFileName))
return;
/* Set up next filename checkpoint */
DailyFileAppenderCalendar c = new DailyFileAppenderCalendar();
c.rollToNextDay ();
nextFilenameComputingMillis = c.getTimeInMillis ();
currentFileName = fileName;
super.setFile(fileName, append);
}
/**
This method differentiates RollingFileAppender from its super
class.
*/
protected
void subAppend(LoggingEvent event) {
if (System.currentTimeMillis () >= nextFilenameComputingMillis) {
try {
setFile (super.fileName, super.fileAppend);
}
catch(IOException e) {
System.err.println("setFile(null, false) call failed.");
e.printStackTrace();
}
}
super.subAppend(event);
}
/**
Retuns the option names for this component, namely {@link
#FILE_NAME_PATTERN_OPTION} in
addition to the options of {@link FileAppender#getOptionStrings
FileAppender}.
*/
public
String[] getOptionStrings() {
return OptionConverter.concatanateArrays(super.getOptionStrings(),
new String[] {FILE_NAME_PATTERN_OPTION});
}
/**
Set the options for the appender
*/
public
void setOption(String key, String value) {
super.setOption(key, value);
if(key.equalsIgnoreCase(FILE_NAME_PATTERN_OPTION)) {
fileNamePattern = value;
}
}
/**
If the a value for {@link #FILE_OPTION} is non-null, then {@link
#setFile} is called with the values of {@link #FILE_OPTION} and
{@link #APPEND_OPTION}.
@since 0.8.1 */
public
void activateOptions() {
try {
setFile(null, super.fileAppend);
}
catch(java.io.IOException e) {
errorHandler.error("setFile(null,"+fileAppend+") call failed.",
e, ErrorCode.FILE_OPEN_FAILURE);
}
}
}
/**
DailyFileAppenderCalendar is a helper class to DailyFileAppender. Using
this class, it is easy to compute and access the next Millis()
It subclasses the standard
<a href="http://java.sun.com/j2se/1.3/docs/api/java/text/GregorianCalendar.html">
java.util.GregorianCalendar</a>-object, to allow access to the protected
function getTimeInMillis(), which it then exports.
@author <a HREF="mailto:eirik.lygre@evita.no">Eirik Lygre</a>
*/
class DailyFileAppenderCalendar extends java.util.GregorianCalendar
{
/**
Returns the current time in Millis
*/
public long getTimeInMillis() {
return super.getTimeInMillis();
}
/**
Roll the date to the next hour, with minute, second and millisecond
set to zero.
*/
public void rollToNextDay () {
this.add(java.util.Calendar.DATE, 0);
this.add(java.util.Calendar.HOUR, 0);
this.set(java.util.Calendar.MINUTE, 0);
this.set(java.util.Calendar.SECOND, 0);
this.set(java.util.Calendar.MILLISECOND, 0);
}
}

View File

@ -0,0 +1,44 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Delivered-To: urba-cgu@urbanet.ch
To: Ceki Gulcu <cgu@urbanet.ch>
From: Eirik_Lygre/evita/no%EVITA@evita.no
Subject: Re: Suggestion for new appender "DailyFileAppender"
Date: Thu, 18 Jan 2001 20:18:27 +0100
X-MIMETrack: Serialize by Router on domino1/evita/no(Release 5.0.5 |September 22, 2000) at
18.01.2001 20:18:30
This version subclasses GregorianCalendar, to get access to the
millis-variable used in the Calendar-classes (it is protected).
What do you think?
Eirik
++++++++++
Eirik Lygre
eirik.lygre@evita.no
e-vita as, Stortorvet 3, Oslo
Mobil: (+47) 905 66476
Fax: (+47) 23 35 70 51
DailyFileAppender1.jav

View File

@ -0,0 +1,188 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.BorderLayout;
import javax.swing.*;
import javax.swing.text.StyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.StyleConstants;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.util.ArrayList;
import org.apache.log4j.*;
public class LogTextPanel extends JPanel {
private JScrollBar scrollBar;
private JTextPane textPane;
private JCheckBox cbxTail;
private StyledDocument doc;
private Hashtable fontAttributes;
private int eventBufferMaxSize = 10000;
private ArrayList eventBuffer = new ArrayList(eventBufferMaxSize);
private int eventViewIndex = 0;
public LogTextPanel() {
constructComponents();
createDefaultFontAttributes();
}
private void constructComponents() {
// setup the panel's additional components...
this.setLayout(new BorderLayout());
cbxTail = new JCheckBox();
cbxTail.setSelected(true);
cbxTail.setText("Tail log events");
JPanel bottomPanel = new JPanel();
bottomPanel.add(cbxTail, null);
textPane = new JTextPane();
textPane.setEditable(false);
textPane.setText("");
doc = textPane.getStyledDocument();
scrollBar = new JScrollBar(JScrollBar.VERTICAL);
this.add(bottomPanel, BorderLayout.SOUTH);
this.add(scrollBar, BorderLayout.EAST);
this.add(textPane, BorderLayout.CENTER);
}
public
void setTextBackground(Color color) {
textPane.setBackground(color);
}
public
void setTextBackground(String v) {
textPane.setBackground(parseColor(v));
}
private void createDefaultFontAttributes() {
Priority[] prio = Priority.getAllPossiblePriorities();
fontAttributes = new Hashtable();
for (int i=0; i<prio.length;i++) {
MutableAttributeSet att = new SimpleAttributeSet();
fontAttributes.put(prio[i], att);
//StyleConstants.setFontSize(att,11);
}
setTextColor(Priority.FATAL, Color.red);
setTextColor(Priority.ERROR, Color.magenta);
setTextColor(Priority.WARN, Color.orange);
setTextColor(Priority.INFO, Color.blue);
setTextColor(Priority.DEBUG, Color.black);
}
private
Color parseColor (String v) {
StringTokenizer st = new StringTokenizer(v,",");
int val[] = {255,255,255,255};
int i=0;
while (st.hasMoreTokens()) {
val[i]=Integer.parseInt(st.nextToken());
i++;
}
return new Color(val[0],val[1],val[2],val[3]);
}
void setTextColor(Priority p, String v) {
StyleConstants.setForeground(
(MutableAttributeSet)fontAttributes.get(p),parseColor(v));
}
void setTextColor(Priority p, Color c) {
StyleConstants.setForeground(
(MutableAttributeSet)fontAttributes.get(p),c);
}
void setTextFontSize(int size) {
Enumeration e = fontAttributes.elements();
while (e.hasMoreElements()) {
StyleConstants.setFontSize((MutableAttributeSet)e.nextElement(),size);
}
return;
}
void setTextFontName(String name) {
Enumeration e = fontAttributes.elements();
while (e.hasMoreElements()) {
StyleConstants.setFontFamily((MutableAttributeSet)e.nextElement(),name);
}
return;
}
void setEventBufferSize(int bufferSize) {
eventBufferMaxSize = bufferSize;
}
void newEvents(EventBufferElement[] evts) {
if((eventBuffer.size() + evts.length) >= eventBufferMaxSize) {
for(int i=0; i < evts.length; i++) {
eventBuffer.remove(0);
}
eventViewIndex -= evts.length;
if(eventViewIndex < 0)
eventViewIndex = 0;
}
for(int i=0; i < evts.length; i++)
eventBuffer.add(evts[i]);
if((eventBuffer.size() > maxR) && cbxTail.isSelected()) {
eventViewIndex = (eventBuffer.size() - maxR);
}
// only redraw if new line is visible...
if((maxR < 0) || (eventBuffer.size() >= eventViewIndex && eventBuffer.size() <= (eventViewIndex + maxR)))
drawText();
}
int maxR = -1;
void drawText() {
if(maxR < 0)
maxR = textPane.getHeight() / textPane.getFontMetrics(textPane.getFont()).getHeight();
try {
doc.remove(0, doc.getLength());
} catch(Exception e) { e.printStackTrace(); }
for(int i=eventViewIndex; (i < eventBuffer.size()) && (i < (eventViewIndex + maxR)); i++) {
EventBufferElement evt = (EventBufferElement)eventBuffer.get(i);
try {
doc.insertString(doc.getLength(), evt.text, (MutableAttributeSet)fontAttributes.get(evt.prio));
} catch(Exception e) { e.printStackTrace(); }
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui.examples;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import org.apache.log4j.*;
import org.apache.log4j.gui.TextPanelAppender;
public class LogTextPanelExample {
boolean packFrame = false;
String catName = "dum.cat.name";
public LogTextPanelExample() {
// setup the logging
TextPanelAppender tpa = new TextPanelAppender(new PatternLayout("%-5p %d [%t]: %m%n"), "logTextPanel");
tpa.setThreshold(Priority.DEBUG);
Category cat = Category.getInstance(catName);
cat.addAppender(tpa);
LogFrame frame = new LogFrame(tpa);
frame.validate();
//Center the frame (window), and show it
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = frame.getSize();
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
frame.setVisible(true);
}
/**Main method*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
catch(Exception e) {
e.printStackTrace();
}
LogTextPanelExample foo = new LogTextPanelExample();
new LogTextPanelExampleGenThread(foo.catName);
}
}
class LogFrame extends JFrame {
public LogFrame(TextPanelAppender tpa) {
enableEvents(AWTEvent.WINDOW_EVENT_MASK);
JPanel contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(new BorderLayout());
this.setSize(new Dimension(600, 400));
this.setTitle("LogTextPanel Example");
contentPane.add(tpa.getLogTextPanel(), BorderLayout.CENTER);
}
// exit when window is closed
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
}
class LogTextPanelExampleGenThread extends Thread {
String catName;
public LogTextPanelExampleGenThread(String catName) {
this.catName = catName;
this.setPriority(Thread.NORM_PRIORITY - 1);
this.start();
}
public void run() {
Category cat = Category.getInstance(catName);
int cnt = 0;
while(true) {
cnt++;
int randEvt = (int)(Math.random() * 125);
if(randEvt < 3)
cat.fatal("{" + cnt + "} Something screwed up bad.");
else if(randEvt < 10)
cat.error("{" + cnt + "} An error occured while trying to delete all of your files.");
else if(randEvt < 25)
cat.warn("{" + cnt + "} It seems as if your hard disk is getting full.");
else if(randEvt < 55)
cat.info("{" + cnt + "} It is now time for tea.");
else if(randEvt < 65)
cat.debug("{" + cnt + "} Something bad is happening on line 565 of com.foo.Crap");
else if(randEvt < 75)
cat.debug("{" + cnt + "} Input value for xe343dd is not equal to xe39dfd!");
else if(randEvt < 85)
cat.debug("{" + cnt + "} Successfully reached line 2312 of com.foo.Goo");
else if(randEvt < 105)
cat.debug("{" + cnt + "} Here is some extra handy debugging information for you.");
else if(randEvt < 115)
cat.debug("{" + cnt + "} The file you are about to write to is not open.");
else if(randEvt < 125)
cat.debug("{" + cnt + "} The input value to the method was <null>.");
try {
Thread.sleep(10);
}
catch(Exception e) {}
}
}
}

View File

@ -0,0 +1,217 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.ArrayList;
import javax.swing.JPanel;
import org.apache.log4j.*;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.Loader;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.helpers.TracerPrintWriter;
import org.apache.log4j.helpers.OptionConverter;
/**
*
* @author James House
*/
public class TextPanelAppender extends AppenderSkeleton {
TracerPrintWriter tp;
StringWriter sw;
QuietWriter qw;
LogTextPanel logTextPanel;
LogPublishingThread logPublisher;
final String COLOR_OPTION_FATAL = "Color.Fatal";
final String COLOR_OPTION_ERROR = "Color.Error";
final String COLOR_OPTION_WARN = "Color.Warn";
final String COLOR_OPTION_INFO = "Color.Info";
final String COLOR_OPTION_DEBUG = "Color.Debug";
final String COLOR_OPTION_BACKGROUND = "Color.Background";
final String FONT_NAME_OPTION = "Font.Name";
final String FONT_SIZE_OPTION = "Font.Size";
final String EVENT_BUFFER_SIZE_OPTION = "EventBuffer.Size";
public TextPanelAppender(Layout layout, String name) {
this.layout = layout;
this.name = name;
this.sw = new StringWriter();
this.qw = new QuietWriter(sw, errorHandler);
this.tp = new TracerPrintWriter(qw);
setLogTextPanel(new LogTextPanel());
logPublisher = new LogPublishingThread(logTextPanel, Priority.ERROR, 500);
//logPublisher = new LogPublishingThread(logTextPanel, null, 500);
}
public
void close() {
}
public void append(LoggingEvent event) {
String text = this.layout.format(event);
// Print Stacktrace
// Quick Hack maybe there is a better/faster way?
if (event.throwable!=null) {
event.throwable.printStackTrace(tp);
for (int i=0; i< sw.getBuffer().length(); i++) {
if (sw.getBuffer().charAt(i)=='\t')
sw.getBuffer().replace(i,i+1," ");
}
text += sw.toString();
sw.getBuffer().delete(0,sw.getBuffer().length());
}
else
if(!text.endsWith("\n"))
text += "\n";
logPublisher.publishEvent(event.priority, text);
}
public
JPanel getLogTextPanel() {
return logTextPanel;
}
public
String[] getOptionStrings() {
return new String[] { COLOR_OPTION_FATAL, COLOR_OPTION_ERROR,
COLOR_OPTION_WARN, COLOR_OPTION_INFO, COLOR_OPTION_DEBUG,
COLOR_OPTION_BACKGROUND, FONT_NAME_OPTION, FONT_SIZE_OPTION};
}
public
void setName(String name) {
this.name = name;
}
protected
void setLogTextPanel(LogTextPanel logTextPanel) {
this.logTextPanel = logTextPanel;
logTextPanel.setTextBackground(Color.white);
}
public
void setOption(String option, String value) {
if (option.equalsIgnoreCase(COLOR_OPTION_FATAL))
logTextPanel.setTextColor(Priority.FATAL,value);
if (option.equalsIgnoreCase(COLOR_OPTION_ERROR))
logTextPanel.setTextColor(Priority.ERROR,value);
if (option.equalsIgnoreCase(COLOR_OPTION_WARN))
logTextPanel.setTextColor(Priority.WARN,value);
if (option.equalsIgnoreCase(COLOR_OPTION_INFO))
logTextPanel.setTextColor(Priority.INFO,value);
if (option.equalsIgnoreCase(COLOR_OPTION_DEBUG))
logTextPanel.setTextColor(Priority.DEBUG,value);
if (option.equalsIgnoreCase(COLOR_OPTION_BACKGROUND))
logTextPanel.setTextBackground(value);
if (option.equalsIgnoreCase(FONT_SIZE_OPTION))
logTextPanel.setTextFontSize(Integer.parseInt(value));
if (option.equalsIgnoreCase(FONT_NAME_OPTION))
logTextPanel.setTextFontName(value);
if (option.equalsIgnoreCase(EVENT_BUFFER_SIZE_OPTION))
logTextPanel.setEventBufferSize(Integer.parseInt(value));
return;
}
public
boolean requiresLayout() {
return true;
}
class LogPublishingThread extends Thread {
LogTextPanel logTextPanel;
ArrayList evts;
Priority triggerPrio;
long pubInterval;
public LogPublishingThread(LogTextPanel logTextPanel, Priority triggerPrio, long pubInterval) {
this.logTextPanel = logTextPanel;
this.evts = new ArrayList(1000);
this.triggerPrio = triggerPrio;
this.pubInterval = pubInterval;
//this.setPriority(Thread.NORM_PRIORITY - 1);
this.start();
}
public void run() {
while(true) {
synchronized(evts) {
try {
evts.wait(pubInterval);
}
catch(InterruptedException e) {}
logTextPanel.newEvents((EventBufferElement[])evts.toArray(new EventBufferElement[evts.size()]));
evts.clear();
}
}
}
public void publishEvent(Priority prio, String text) {
synchronized(evts) {
evts.add(new EventBufferElement(prio, text));
if(triggerPrio != null && prio.isGreaterOrEqual(triggerPrio))
evts.notify();
}
}
}
} // TextPaneAppender
class EventBufferElement {
public String text;
public Priority prio;
public int numLines;
EventBufferElement(Priority prio, String text) {
this.prio = prio;
this.text = text;
numLines = 1;
int pos = pos = text.indexOf('\n', 0);
int len = text.length() - 1;
while( (pos > 0) && (pos < len) )
numLines++;
pos = text.indexOf('\n', pos + 1);
}
}

View File

@ -0,0 +1,87 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Delivered-To: urba-cgu@urbanet.ch
From: James House <james.house@medibuy.com>
To: Ceki Gulcu <cgu@urbanet.ch>
Subject: RE: Buffering issues
Date: Tue, 23 Jan 2001 11:38:30 -0800
X-Mailer: Internet Mail Service (5.5.2650.21)
Ceki,
Most of the "speed" issues can be easily solved, as demonstrated in
the new versions of the files that are attached, and as described
here:
The "drawing" of the panel is time consuming. And let's face it: we
will never build a swing component that can re-draw itself thousands
of times per second - nor could the human eye keep up with that anyway
(60-ish redraws per second is the limit of what most humans can
perceive).
I think you'll agree that regardless of which choice of swing
components, the issue of not being able to draw more than 100 times
per second (if that) will be a constant.
Possible solutions to wasting a lot of time redrawing are:
* Redraw every N messages
* Redraw every N milliseconds
* Redraw when a message of a high priority is received
* Only redraw if the changes (new messages) are visible (depending on
scroll-bar position)
I made some very small changes (which are some of what I outlined in
the previous e-mail as things on the to-do list), and you should be
able to see a great difference in performance.
In general the changes are: Use a "queue" to asynchronously deliver
the messages to the panel for updating, rather than having the calling
thread (the one generating the log message) be responsible for
re-drawing the panel - this frees up the thread to continue on without
waiting for the logging mechanism. The "queue" has a thread that
wakes up every N milliseconds, or when a message of a specified
priority arrives. It then delivers all queued messages to the panel,
which then redraws ONCE for the entire batch of queued messages IF the
new messages are visible in the display.
With the "tail" mechanism turned on (so that any new set of messages
forces a redraw, because the last message is always visible), the
panel can now log about a thousand messages in 1 second. With tail
turned off (so that redraw is only required if the panel is showing
the space the new messages will be printed), it can receive over a
100,000 messages in just a few seconds. Also, most of the flicker is
already eliminated.
Again, everything in the code is really "roughed in" - just there to
give the general idea, not necessarily to do things in the best way -
but I really feel that this kind of solution is what you'll have to
end up with in the long term.
James

View File

@ -0,0 +1,241 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.ErrorCode;
import org.apache.log4j.helpers.LogLog;
import java.util.Hashtable;
import java.util.Properties;
import javax.jms.*;
import javax.naming.InitialContext;
import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
/**
* A Simple JMS (P2P) Queue Appender.
*
* @author Ceki G&uuml;lc&uuml;
* @author Jamie Tsao
*/
public class JMSQueueAppender extends AppenderSkeleton {
protected QueueConnection queueConnection;
protected QueueSession queueSession;
protected QueueSender queueSender;
protected Queue queue;
String initialContextFactory;
String providerUrl;
String queueBindingName;
String queueConnectionFactoryBindingName;
public
JMSQueueAppender() {
}
/**
* The <b>InitialContextFactory</b> option takes a string value.
* Its value, along with the <b>ProviderUrl</b> option will be used
* to get the InitialContext.
*/
public void setInitialContextFactory(String initialContextFactory) {
this.initialContextFactory = initialContextFactory;
}
/**
* Returns the value of the <b>InitialContextFactory</b> option.
*/
public String getInitialContextFactory() {
return initialContextFactory;
}
/**
* The <b>ProviderUrl</b> option takes a string value.
* Its value, along with the <b>InitialContextFactory</b> option will be used
* to get the InitialContext.
*/
public void setProviderUrl(String providerUrl) {
this.providerUrl = providerUrl;
}
/**
* Returns the value of the <b>ProviderUrl</b> option.
*/
public String getProviderUrl() {
return providerUrl;
}
/**
* The <b>QueueConnectionFactoryBindingName</b> option takes a
* string value. Its value will be used to lookup the appropriate
* <code>QueueConnectionFactory</code> from the JNDI context.
*/
public void setQueueConnectionFactoryBindingName(String queueConnectionFactoryBindingName) {
this.queueConnectionFactoryBindingName = queueConnectionFactoryBindingName;
}
/**
* Returns the value of the <b>QueueConnectionFactoryBindingName</b> option.
*/
public String getQueueConnectionFactoryBindingName() {
return queueConnectionFactoryBindingName;
}
/**
* The <b>QueueBindingName</b> option takes a
* string value. Its value will be used to lookup the appropriate
* destination <code>Queue</code> from the JNDI context.
*/
public void setQueueBindingName(String queueBindingName) {
this.queueBindingName = queueBindingName;
}
/**
Returns the value of the <b>QueueBindingName</b> option.
*/
public String getQueueBindingName() {
return queueBindingName;
}
/**
* Overriding this method to activate the options for this class
* i.e. Looking up the Connection factory ...
*/
public void activateOptions() {
QueueConnectionFactory queueConnectionFactory;
try {
Context ctx = getInitialContext();
queueConnectionFactory = (QueueConnectionFactory) ctx.lookup(queueConnectionFactoryBindingName);
queueConnection = queueConnectionFactory.createQueueConnection();
queueSession = queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup(queueBindingName);
queueSender = queueSession.createSender(queue);
queueConnection.start();
ctx.close();
} catch(Exception e) {
errorHandler.error("Error while activating options for appender named ["+name+
"].", e, ErrorCode.GENERIC_FAILURE);
}
}
protected InitialContext getInitialContext() throws NamingException {
try {
Hashtable ht = new Hashtable();
//Populate property hashtable with data to retrieve the context.
ht.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
ht.put(Context.PROVIDER_URL, providerUrl);
return (new InitialContext(ht));
} catch (NamingException ne) {
LogLog.error("Could not get initial context with ["+initialContextFactory + "] and [" + providerUrl + "].");
throw ne;
}
}
protected boolean checkEntryConditions() {
String fail = null;
if(this.queueConnection == null) {
fail = "No QueueConnection";
} else if(this.queueSession == null) {
fail = "No QueueSession";
} else if(this.queueSender == null) {
fail = "No QueueSender";
}
if(fail != null) {
errorHandler.error(fail +" for JMSQueueAppender named ["+name+"].");
return false;
} else {
return true;
}
}
/**
* Close this JMSQueueAppender. Closing releases all resources used by the
* appender. A closed appender cannot be re-opened.
*/
public synchronized // avoid concurrent append and close operations
void close() {
if(this.closed)
return;
LogLog.debug("Closing appender ["+name+"].");
this.closed = true;
try {
if(queueSession != null)
queueSession.close();
if(queueConnection != null)
queueConnection.close();
} catch(Exception e) {
LogLog.error("Error while closing JMSQueueAppender ["+name+"].", e);
}
// Help garbage collection
queueSender = null;
queueSession = null;
queueConnection = null;
}
/**
* This method called by {@link AppenderSkeleton#doAppend} method to
* do most of the real appending work. The LoggingEvent will be
* be wrapped in an ObjectMessage to be put on the JMS queue.
*/
public void append(LoggingEvent event) {
if(!checkEntryConditions()) {
return;
}
try {
ObjectMessage msg = queueSession.createObjectMessage();
msg.setObject(event);
queueSender.send(msg);
} catch(Exception e) {
errorHandler.error("Could not send message in JMSQueueAppender ["+name+"].", e,
ErrorCode.GENERIC_FAILURE);
}
}
public boolean requiresLayout() {
return false;
}
}

View File

@ -0,0 +1,28 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Hi,
I have written a JMSQueueAppender that essentially logs to a JMS Queue
(Point 2 Point). The JMSAppender currently packaged with log4j logs to
a JMS Topic (I would rename it JMSTopicAppender). I also made it so
that the InitialContextFactory and ProviderUrl are configurable appender
options. I would like to submit this to Apache.
Thanks,
Jamie Tsao

View File

@ -0,0 +1,210 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.*;
import org.apache.log4j.*;
/**
* An OutputStream that flushes out to a Category.<p>
*
* Note that no data is written out to the Category until the stream is
* flushed or closed.<p>
*
* Example:<pre>
* // make sure everything sent to System.err is logged
* System.setErr(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.WARN), true));
*
* // make sure everything sent to System.out is also logged
* System.setOut(new PrintStream(new LoggingOutputStream(Category.getRoot(), Priority.INFO), true));
* </pre>
*
* @author <a href="mailto://Jim.Moore@rocketmail.com">Jim Moore</a>
* @see Category
*/
public class LoggingOutputStream extends OutputStream {
protected static final String LINE_SEPERATOR = System.getProperty("line.separator");
/**
* Used to maintain the contract of {@link #close()}.
*/
protected boolean hasBeenClosed = false;
/**
* The internal buffer where data is stored.
*/
protected byte[] buf;
/**
* The number of valid bytes in the buffer. This value is always
* in the range <tt>0</tt> through <tt>buf.length</tt>; elements
* <tt>buf[0]</tt> through <tt>buf[count-1]</tt> contain valid
* byte data.
*/
protected int count;
/**
* Remembers the size of the buffer for speed.
*/
private int bufLength;
/**
* The default number of bytes in the buffer. =2048
*/
public static final int DEFAULT_BUFFER_LENGTH = 2048;
/**
* The category to write to.
*/
protected Category category;
/**
* The priority to use when writing to the Category.
*/
protected Priority priority;
private LoggingOutputStream() {
// illegal
}
/**
* Creates the LoggingOutputStream to flush to the given Category.
*
* @param cat the Category to write to
*
* @param priority the Priority to use when writing to the Category
*
* @exception IllegalArgumentException
* if cat == null or priority == null
*/
public LoggingOutputStream(Category cat, Priority priority)
throws IllegalArgumentException {
if (cat == null) {
throw new IllegalArgumentException("cat == null");
}
if (priority == null) {
throw new IllegalArgumentException("priority == null");
}
this.priority = priority;
category = cat;
bufLength = DEFAULT_BUFFER_LENGTH;
buf = new byte[DEFAULT_BUFFER_LENGTH];
count = 0;
}
/**
* Closes this output stream and releases any system resources
* associated with this stream. The general contract of <code>close</code>
* is that it closes the output stream. A closed stream cannot perform
* output operations and cannot be reopened.
*/
public void close() {
flush();
hasBeenClosed = true;
}
/**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
* low-order bits of the argument <code>b</code>. The 24
* high-order bits of <code>b</code> are ignored.
*
* @param b the <code>byte</code> to write
*
* @exception IOException
* if an I/O error occurs. In particular,
* an <code>IOException</code> may be thrown if the
* output stream has been closed.
*/
public void write(final int b) throws IOException {
if (hasBeenClosed) {
throw new IOException("The stream has been closed.");
}
// don't log nulls
if (b == 0) {
return;
}
// would this be writing past the buffer?
if (count == bufLength) {
// grow the buffer
final int newBufLength = bufLength+DEFAULT_BUFFER_LENGTH;
final byte[] newBuf = new byte[newBufLength];
System.arraycopy(buf, 0, newBuf, 0, bufLength);
buf = newBuf;
bufLength = newBufLength;
}
buf[count] = (byte)b;
count++;
}
/**
* Flushes this output stream and forces any buffered output bytes
* to be written out. The general contract of <code>flush</code> is
* that calling it is an indication that, if any bytes previously
* written have been buffered by the implementation of the output
* stream, such bytes should immediately be written to their
* intended destination.
*/
public void flush() {
if (count == 0) {
return;
}
// don't print out blank lines; flushing from PrintStream puts out these
if (count == LINE_SEPERATOR.length()) {
if ( ((char)buf[0]) == LINE_SEPERATOR.charAt(0) &&
( ( count == 1 ) || // <- Unix & Mac, -> Windows
( (count == 2) && ((char)buf[1]) == LINE_SEPERATOR.charAt(1) ) ) ) {
reset();
return;
}
}
final byte[] theBytes = new byte[count];
System.arraycopy(buf, 0, theBytes, 0, count);
category.log(priority, new String(theBytes));
reset();
}
private void reset() {
// not resetting the buffer -- assuming that if it grew that it
// will likely grow similarly again
count = 0;
}
}

View File

@ -0,0 +1,56 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Copied from:
http://www.mail-archive.com/log4j-user@jakarta.apache.org/msg00430.html
--------------------------------------------------------------------------------
diverting System.stderr/stdout into log4j
--------------------------------------------------------------------------------
From: Joseph Panico
Subject: diverting System.stderr/stdout into log4j
Date: Mon, 12 Mar 2001 13:26:41 -0800
--------------------------------------------------------------------------------
Folks,
We use a number of third-party packages that do stderr.print... at various
random places in their code. I'm finding it quite useful to divert these
messages into our log4j heirarchy. I do this by replacing stderr/stdout with
my own PrintStreams that log the lines to a special log4j Category-- as
suggested on this list a while back. The only fly-in-the-ointment with this
scheme is LogLog. If there is a problem with log4j such that it cannot log
for some reason, then log4j internals use LogLog to attempt to print an
error message. This obviously leads to an infinite recursion. Has anyone
else been bothered by this? Would it make sense to add interface to LogLog
which would set the PrintStream it uses to log its error messages to?
thanks for any ideas
joe
_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: log4j-user-help@jakarta.apache.org

View File

@ -0,0 +1,127 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Copied from:
http://www.mail-archive.com/log4j-user@jakarta.apache.org/msg00433.html
--------------------------------------------------------------------------------
RE: diverting System.stderr/stdout into log4j
--------------------------------------------------------------------------------
From: Jim Moore
Subject: RE: diverting System.stderr/stdout into log4j
Date: Mon, 12 Mar 2001 14:54:13 -0800
--------------------------------------------------------------------------------
It doesn't. I haven't worried about it, since log4j doesn't contain any
bugs and therefore it would never happen... :)
Probably the best way to handle it is to add a
LogLog.setPrintStream(PrintStream) method, so you can do something like:
// remember STDERR
PrintStream se = System.err;
// make sure everything sent to System.err is logged
System.setErr(new PrintStream(new LoggingOutputStream(Category.getRoot(),
Priority.WARN), true));
// make sure everything sent to System.out is also logged
System.setOut(new PrintStream(new LoggingOutputStream(Category.getRoot(),
Priority.INFO), true));
// prevent infinate recursion in LogLog
LogLog.setPrintStream(se);
I can't think of any other way to do it in the current version besides
getting extremely kludgey by checking the stack to see if it's being called
from LogLog and logging out the the "real" STDERR then in the
LoggingOutputStream. It can be done on the theory that LogLog wouldn't be
called very often, but still...
-Jim Moore
-----Original Message-----
From: Ceki Gülcü [mailto:cgu@qos.ch]
Sent: Monday, March 12, 2001 5:15 PM
To: LOG4J Users Mailing List
Subject: RE: diverting System.stderr/stdout into log4j
Jim, Joseph,
Here is a link containing Jim's code:
http://marc.theaimsgroup.com/?l=log4j-user&m=98097669218571&w=2
How does this code handle the infinite recursion problem mentioned by
Joseph? Ceki
At 17:03 12.03.2001 -0500, Jim Moore wrote:
>Go to the mailing list archives (theAimsGroup.com is the best) and search
>for the thread with the subject of "Capturing System.err"
>
>-Jim Moore
>"I think so, Brain; but if we gave peas a chance, won't the lima beans get
>jealous?" - Pinky
>
>
>-----Original Message-----
>From: Joseph Panico [mailto:joe_panico@hotmail.com]
>Sent: Monday, March 12, 2001 4:43 PM
>To: log4j-user@jakarta.apache.org
>Subject: diverting System.stderr/stdout into log4j
>
>
>Folks,
>
>We use a number of third-party packages that do stderr.print... at various
>random places in their code. I'm finding it quite useful to divert these
>messages into our log4j heirarchy. I do this by replacing stderr/stdout
with
>
>my own PrintStreams that log the lines to a special log4j Category-- as
>suggested on this list a while back. The only fly-in-the-ointment with this
>scheme is LogLog. If there is a problem with log4j such that it cannot log
>for some reason, then log4j internals use LogLog to attempt to print an
>error message. This obviously leads to an infinite recursion. Has anyone
>else been bothered by this? Would it make sense to add interface to LogLog
>which would set the PrintStream it uses to log its error messages to?
>
>thanks for any ideas
>
>joe
I hope to see you at my ApacheCon 2001 presentation
entitled "Log4j, A Logging Package for Java".
See http://ApacheCon.Com/2001/US/ for more details.
----
Ceki Gülcü Web: http://qos.ch
av. de Rumine 5 email: cgu@qos.ch (preferred)
CH-1005 Lausanne ceki_gulcu@yahoo.com
Switzerland Tel: ++41 21 351 23 15
---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: log4j-user-help@jakarta.apache.org

View File

@ -0,0 +1,229 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Copied from
http://www.mail-archive.com/log4j-user@jakarta.apache.org/msg00445.html
--------------------------------------------------------------------------------
RE: diverting System.stderr/stdout into log4j
--------------------------------------------------------------------------------
From: Michael Smith
Subject: RE: diverting System.stderr/stdout into log4j
Date: Tue, 13 Mar 2001 06:46:04 -0800
--------------------------------------------------------------------------------
There is another way!
In LogLog, completely ignore System.err. Instead, use the following to get
the standard error stream:
PrintStream err =
new PrintStream(new FileOutputStream(FileDescriptor.err));
When you use System.setErr, it changes System.err, but not
FileDescriptor.err, which maintains a descriptor for the original error
stream.
michael
For a sample program to test this, see below:
import java.io.*;
public class Stderr {
public static void main(String[] args) {
// create a print stream to represent a redirect
PrintStream nonStandardErr =
new PrintStream(new ByteArrayOutputStream());
// Redirect standard out and standard err
System.setOut(nonStandardErr);
System.setErr(nonStandardErr);
// attempt to print something
System.err.println("You should *not* see this on the console!");
// the stuff that would appear in LogLog
PrintStream logLogOut =
new PrintStream(new FileOutputStream(FileDescriptor.err));
// attempt to print something
logLogOut.println("You *should* see this on the console!");
}
}
> -----Original Message-----
> From: Ceki Gülcü [mailto:cgu@qos.ch]
> Sent: Monday, March 12, 2001 7:18 PM
> To: LOG4J Users Mailing List
> Subject: RE: diverting System.stderr/stdout into log4j
>
>
>
> Hate to follow up on myself, but the System.setErr method
> reassigns the System.err variable. This can be deduced without
> experimentation because the user calls the System.err variable
> directly to print to the console, whatever it might be. Thus, the
> reference itself must change to allow the System.err variable to
> point to the new target stream.
>
> The funny part is that the err variable is declared 'public
> final' in the JDK source code. The setErr method makes a call to
> setErr0 which is declared as being 'native'. It looks like the
> native part is circumventing the JDK restrictions. I find this
> quite entertaining. Ceki
>
> At 00:58 13.03.2001 +0100, Ceki Gülcü wrote:
>
> >Running the risk of disappointing you here, although not full of
> bugs, log4j is not bug-free as bugs creep out regularly. They
> just get corrected quickly before many people are affected by them.
> >
> >The PrintStream se = System.err; LogLog.setPrintStream(see);
> combination is simple and rather bright. I initially overlooked
> the PrintStream se = System.err; part, making me think that a
> lot of code needed to be modified to cater for the redirected
> console case. The remedy looked worse than the illness. My fears
> are largely unfounded and the solution should work quite well if
> one is careful.
> >
> >Regards, Ceki
> >
> >ps: I wonder if System.err always refers to the real STDERR or
> if really gets reassigned with the setErr call. It's easy to find out...
> >
> >At 23:20 12.03.2001 +0000, Joseph Panico wrote:
> >>Of course log4j is completely bug free, but that doesn't
> preclude user error. For instance, I neglected to add appenders
> in my config file (actually I intentionally left them out,
> thinking that would simply turn off logging) and then log4j went
> into an infinite loop. The setPrintStream makes sense to me.
> >>
> >>joe
> >>
> >>
> >>>From: Jim Moore <jim.moore@veritas.com>
> >>>Reply-To: "LOG4J Users Mailing List" <log4j-user@jakarta.apache.org>
> >>>To: 'LOG4J Users Mailing List' <log4j-user@jakarta.apache.org>
> >>>Subject: RE: diverting System.stderr/stdout into log4j
> >>>Date: Mon, 12 Mar 2001 18:10:37 -0500
> >>>
> >>>It doesn't. I haven't worried about it, since log4j doesn't
> contain any
> >>>bugs and therefore it would never happen... :)
> >>>
> >>>Probably the best way to handle it is to add a
> >>>LogLog.setPrintStream(PrintStream) method, so you can do
> something like:
> >>>
> >>>// remember STDERR
> >>>PrintStream se = System.err;
> >>>
> >>>// make sure everything sent to System.err is logged
> >>>System.setErr(new PrintStream(new
> LoggingOutputStream(Category.getRoot(),
> >>> Priority.WARN), true));
> >>>
> >>>// make sure everything sent to System.out is also logged
> >>>System.setOut(new PrintStream(new
> LoggingOutputStream(Category.getRoot(),
> >>> Priority.INFO), true));
> >>>
> >>>// prevent infinate recursion in LogLog
> >>>LogLog.setPrintStream(se);
> >>>
> >>>
> >>>I can't think of any other way to do it in the current version besides
> >>>getting extremely kludgey by checking the stack to see if it's
> being called
> >>>from LogLog and logging out the the "real" STDERR then in the
> >>>LoggingOutputStream. It can be done on the theory that LogLog
> wouldn't be
> >>>called very often, but still...
> >>>
> >>>-Jim Moore
> >>>
> >>>
> >>>-----Original Message-----
> >>>From: Ceki Gülcü [mailto:cgu@qos.ch]
> >>>Sent: Monday, March 12, 2001 5:15 PM
> >>>To: LOG4J Users Mailing List
> >>>Subject: RE: diverting System.stderr/stdout into log4j
> >>>
> >>>
> >>>Jim, Joseph,
> >>>
> >>>Here is a link containing Jim's code:
> >>>
> >>>http://marc.theaimsgroup.com/?l=log4j-user&m=98097669218571&w=2
> >>>
> >>>How does this code handle the infinite recursion problem mentioned by
> >>>Joseph? Ceki
> >>>
> >>>At 17:03 12.03.2001 -0500, Jim Moore wrote:
> >>>>Go to the mailing list archives (theAimsGroup.com is the
> best) and search
> >>>>for the thread with the subject of "Capturing System.err"
> >>>>
> >>>>-Jim Moore
> >>>>"I think so, Brain; but if we gave peas a chance, won't the
> lima beans get
> >>>>jealous?" - Pinky
> >>>>
> >>>>
> >>>>-----Original Message-----
> >>>>From: Joseph Panico [mailto:joe_panico@hotmail.com]
> >>>>Sent: Monday, March 12, 2001 4:43 PM
> >>>>To: log4j-user@jakarta.apache.org
> >>>>Subject: diverting System.stderr/stdout into log4j
> >>>>
> >>>>
> >>>>Folks,
> >>>>
> >>>>We use a number of third-party packages that do
> stderr.print... at various
> >>>>random places in their code. I'm finding it quite useful to
> divert these
> >>>>messages into our log4j heirarchy. I do this by replacing
> stderr/stdout
> >>>with
> >>>>
> >>>>my own PrintStreams that log the lines to a special log4j
> Category-- as
> >>>>suggested on this list a while back. The only
> fly-in-the-ointment with this
> >>>
> >>>>scheme is LogLog. If there is a problem with log4j such that
> it cannot log
> >>>>for some reason, then log4j internals use LogLog to attempt
> to print an
> >>>>error message. This obviously leads to an infinite recursion.
> Has anyone
> >>>>else been bothered by this? Would it make sense to add
> interface to LogLog
> >>>>which would set the PrintStream it uses to log its error messages to?
> >>>>
> >>>>thanks for any ideas
> >>>>
> >>>>joe

View File

@ -0,0 +1,707 @@
package org.apache.log4j;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import org.apache.log4j.RollingCalendar;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.helpers.CountingQuietWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggingEvent;
import java.util.Date;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.io.File;
import java.io.FilenameFilter;
/**
* <p>CompositeRollingAppender combines RollingFileAppender and DailyRollingFileAppender<br>
* It can function as either or do both at the same time (making size
* based rolling files like RollingFileAppender until a data/time boundary
* is crossed at which time it rolls all of those files as per the DailyRollingFileAppender)
* based on the setting for <code>rollingStyle</code>.<br>
* <br>
* To use CompositeRollingAppender to roll log files as they reach a certain
* size (like RollingFileAppender), set rollingStyle=1 (@see config.size)<br>
* To use CompositeRollingAppender to roll log files at certain time intervals
* (daily for example), set rollingStyle=2 and a datePattern (@see config.time)<br>
* To have CompositeRollingAppender roll log files at a certain size AND rename those
* according to time intervals, set rollingStyle=3 (@see config.composite)<br>
*
* <p>A of few additional optional features have been added:<br>
* -- Attach date pattern for current log file (@see staticLogFileName)<br>
* -- Backup number increments for newer files (@see countDirection)<br>
* -- Infinite number of backups by file size (@see maxSizeRollBackups)<br>
* <br>
* <p>A few notes and warnings: For large or infinite number of backups
* countDirection > 0 is highly recommended, with staticLogFileName = false if
* time based rolling is also used -- this will reduce the number of file renamings
* to few or none. Changing staticLogFileName or countDirection without clearing
* the directory could have nasty side effects. If Date/Time based rolling
* is enabled, CompositeRollingAppender will attempt to roll existing files
* in the directory without a date/time tag based on the last modified date
* of the base log files last modification.<br>
* <br>
* <p>A maximum number of backups based on date/time boundries would be nice
* but is not yet implemented.<br>
*
* @author Kevin Steppe
* @author Heinz Richter
* @author Eirik Lygre
* @author Ceki G&uuml;lc&uuml;
*/
public class CompositeRollingAppender extends org.apache.log4j.FileAppender
{
// The code assumes that the following 'time' constants are in a increasing
// sequence.
static final int TOP_OF_TROUBLE=-1;
static final int TOP_OF_MINUTE = 0;
static final int TOP_OF_HOUR = 1;
static final int HALF_DAY = 2;
static final int TOP_OF_DAY = 3;
static final int TOP_OF_WEEK = 4;
static final int TOP_OF_MONTH = 5;
/** Style of rolling to use */
static final int BY_SIZE = 1;
static final int BY_DATE = 2;
static final int BY_COMPOSITE = 3;
//Not currently used
static final String S_BY_SIZE = "Size";
static final String S_BY_DATE = "Date";
static final String S_BY_COMPOSITE = "Composite";
/**
The date pattern. By default, the pattern is set to
"'.'yyyy-MM-dd" meaning daily rollover.
*/
private String datePattern = "'.'yyyy-MM-dd";
/** The actual formatted filename that is currently being written to
or will be the file transferred to on roll over
(based on staticLogFileName). */
private String scheduledFilename = null;
/** The timestamp when we shall next recompute the filename. */
private long nextCheck = System.currentTimeMillis () - 1;
/** Holds date of last roll over */
Date now = new Date();
SimpleDateFormat sdf;
/** Helper class to determine next rollover time */
RollingCalendar rc = new RollingCalendar();
/** Current period for roll overs */
int checkPeriod = TOP_OF_TROUBLE;
/** The default maximum file size is 10MB. */
protected long maxFileSize = 10*1024*1024;
/** There is zero backup files by default. */
protected int maxSizeRollBackups = 0;
/** How many sized based backups have been made so far */
protected int curSizeRollBackups = 0;
/** not yet implemented */
protected int maxTimeRollBackups = -1;
protected int curTimeRollBackups = 0;
/** By default newer files have lower numbers. (countDirection < 0)
* ie. log.1 is most recent, log.5 is the 5th backup, etc...
* countDirection > 0 does the opposite ie.
* log.1 is the first backup made, log.5 is the 5th backup made, etc.
* For infinite backups use countDirection > 0 to reduce rollOver costs.
*/
protected int countDirection = -1;
/** Style of rolling to Use. BY_SIZE (1), BY_DATE(2), BY COMPOSITE(3) */
protected int rollingStyle = BY_COMPOSITE;
protected boolean rollDate = true;
protected boolean rollSize = true;
/** By default file.log is always the current file. Optionally
* file.log.yyyy-mm-dd for current formated datePattern can by the currently
* logging file (or file.log.curSizeRollBackup or even
* file.log.yyyy-mm-dd.curSizeRollBackup) This will make time based roll
* overs with a large number of backups much faster -- it won't have to
* rename all the backups!
*/
protected boolean staticLogFileName = true;
/** FileName provided in configuration. Used for rolling properly */
protected String baseFileName;
/** The default constructor does nothing. */
public CompositeRollingAppender() {
}
/**
Instantiate a <code>CompositeRollingAppender</code> and open the
file designated by <code>filename</code>. The opened filename will
become the ouput destination for this appender.
*/
public CompositeRollingAppender (Layout layout, String filename,
String datePattern) throws IOException {
this(layout, filename, datePattern, true);
}
/**
Instantiate a CompositeRollingAppender and open the file designated by
<code>filename</code>. The opened filename will become the ouput
destination for this appender.
<p>If the <code>append</code> parameter is true, the file will be
appended to. Otherwise, the file desginated by
<code>filename</code> will be truncated before being opened.
*/
public CompositeRollingAppender(Layout layout, String filename, boolean append)
throws IOException {
super(layout, filename, append);
}
/**
Instantiate a CompositeRollingAppender and open the file designated by
<code>filename</code>. The opened filename will become the ouput
destination for this appender.
*/
public CompositeRollingAppender (Layout layout, String filename,
String datePattern, boolean append) throws IOException {
super(layout, filename, append);
this.datePattern = datePattern;
activateOptions();
}
/**
Instantiate a CompositeRollingAppender and open the file designated by
<code>filename</code>. The opened filename will become the output
destination for this appender.
<p>The file will be appended to. DatePattern is default.
*/
public CompositeRollingAppender(Layout layout, String filename) throws IOException {
super(layout, filename);
}
/**
The <b>DatePattern</b> takes a string in the same format as
expected by {@link SimpleDateFormat}. This options determines the
rollover schedule.
*/
public void setDatePattern(String pattern) {
datePattern = pattern;
}
/** Returns the value of the <b>DatePattern</b> option. */
public String getDatePattern() {
return datePattern;
}
/**
Returns the value of the <b>maxSizeRollBackups</b> option.
*/
public int getMaxSizeRollBackups() {
return maxSizeRollBackups;
}
/**
Get the maximum size that the output file is allowed to reach
before being rolled over to backup files.
@since 1.1
*/
public long getMaximumFileSize() {
return maxFileSize;
}
/**
<p>Set the maximum number of backup files to keep around based on file size.
<p>The <b>MaxSizeRollBackups</b> option determines how many backup
files are kept before the oldest is erased. This option takes
an integer value. If set to zero, then there will be no
backup files and the log file will be truncated when it reaches
<code>MaxFileSize</code>. If a negative number is supplied then
no deletions will be made. Note that this could result in
very slow performance as a large number of files are rolled over unless
{@link #setCountDirection} up is used.
<p>The maximum applys to -each- time based group of files and -not- the total.
Using a daily roll the maximum total files would be (#days run) * (maxSizeRollBackups)
*/
public void setMaxSizeRollBackups(int maxBackups) {
maxSizeRollBackups = maxBackups;
}
/**
Set the maximum size that the output file is allowed to reach
before being rolled over to backup files.
<p>This method is equivalent to {@link #setMaxFileSize} except
that it is required for differentiating the setter taking a
<code>long</code> argument from the setter taking a
<code>String</code> argument by the JavaBeans {@link
java.beans.Introspector Introspector}.
@see #setMaxFileSize(String)
*/
public void setMaxFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
/**
Set the maximum size that the output file is allowed to reach
before being rolled over to backup files.
<p>This method is equivalent to {@link #setMaxFileSize} except
that it is required for differentiating the setter taking a
<code>long</code> argument from the setter taking a
<code>String</code> argument by the JavaBeans {@link
java.beans.Introspector Introspector}.
@see #setMaxFileSize(String)
*/
public void setMaximumFileSize(long maxFileSize) {
this.maxFileSize = maxFileSize;
}
/**
Set the maximum size that the output file is allowed to reach
before being rolled over to backup files.
<p>In configuration files, the <b>MaxFileSize</b> option takes an
long integer in the range 0 - 2^63. You can specify the value
with the suffixes "KB", "MB" or "GB" so that the integer is
interpreted being expressed respectively in kilobytes, megabytes
or gigabytes. For example, the value "10KB" will be interpreted
as 10240.
*/
public void setMaxFileSize(String value) {
maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
}
protected void setQWForFiles(Writer writer) {
qw = new CountingQuietWriter(writer, errorHandler);
}
//Taken verbatum from DailyRollingFileAppender
int computeCheckPeriod() {
RollingCalendar c = new RollingCalendar();
// set sate to 1970-01-01 00:00:00 GMT
Date epoch = new Date(0);
if(datePattern != null) {
for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
String r0 = sdf.format(epoch);
c.setType(i);
Date next = new Date(c.getNextCheckMillis(epoch));
String r1 = sdf.format(next);
//LogLog.debug("Type = "+i+", r0 = "+r0+", r1 = "+r1);
if(r0 != null && r1 != null && !r0.equals(r1)) {
return i;
}
}
}
return TOP_OF_TROUBLE; // Deliberately head for trouble...
}
//Now for the new stuff
/**
* Handles append time behavior for CompositeRollingAppender. This checks
* if a roll over either by date (checked first) or time (checked second)
* is need and then appends to the file last.
*/
protected void subAppend(LoggingEvent event) {
if (rollDate) {
long n = System.currentTimeMillis();
if (n >= nextCheck) {
now.setTime(n);
nextCheck = rc.getNextCheckMillis(now);
rollOverTime();
}
}
if (rollSize) {
if ((fileName != null) && ((CountingQuietWriter) qw).getCount() >= maxFileSize) {
rollOverSize();
}
}
super.subAppend(event);
}
public void setFile(String file)
{
baseFileName = file.trim();
fileName = file.trim();
}
/**
* Creates and opens the file for logging. If <code>staticLogFileName</code>
* is false then the fully qualified name is determined and used.
*/
public synchronized void setFile(String fileName, boolean append) throws IOException {
if (!staticLogFileName) {
scheduledFilename = fileName = fileName.trim() + sdf.format(now);
if (countDirection > 0) {
scheduledFilename = fileName = fileName + '.' + (++curSizeRollBackups);
}
}
super.setFile(fileName, append);
if(append) {
File f = new File(fileName);
((CountingQuietWriter) qw).setCount(f.length());
}
}
public int getCountDirection() {
return countDirection;
}
public void setCountDirection(int direction) {
countDirection = direction;
}
public int getRollingStyle () {
return rollingStyle;
}
public void setRollingStyle(int style) {
rollingStyle = style;
switch (rollingStyle) {
case BY_SIZE:
rollDate = false;
rollSize = true;
break;
case BY_DATE:
rollDate = true;
rollSize = false;
break;
case BY_COMPOSITE:
rollDate = true;
rollSize = true;
break;
default:
errorHandler.error("Invalid rolling Style, use 1 (by size only), 2 (by date only) or 3 (both)");
}
}
/*
public void setRollingStyle(String style) {
if (style == S_BY_SIZE) {
rollingStyle = BY_SIZE;
}
else if (style == S_BY_DATE) {
rollingStyle = BY_DATE;
}
else if (style == S_BY_COMPOSITE) {
rollingStyle = BY_COMPOSITE;
}
}
*/
public boolean getStaticLogFileName() {
return staticLogFileName;
}
public void setStaticLogFileName(boolean s) {
staticLogFileName = s;
}
public void setStaticLogFileName(String value) {
setStaticLogFileName(OptionConverter.toBoolean(value, true));
}
/**
* Initializes based on exisiting conditions at time of <code>
* activateOptions</code>. The following is done:<br>
* <br>
* A) determine curSizeRollBackups<br>
* B) determine curTimeRollBackups (not implemented)<br>
* C) initiates a roll over if needed for crossing a date boundary since
* the last run.
*/
protected void existingInit() {
curSizeRollBackups = 0;
curTimeRollBackups = 0;
//part A starts here
String filter;
if (staticLogFileName || !rollDate) {
filter = baseFileName + ".*";
}
else {
filter = scheduledFilename + ".*";
}
File f = new File(baseFileName);
f = f.getParentFile();
if (f == null)
f = new File(".");
LogLog.debug("Searching for existing files in: " + f);
String[] files = f.list();
if (files != null) {
for (int i = 0; i < files.length; i++) {
if (!files[i].startsWith(baseFileName))
continue;
int index = files[i].lastIndexOf(".");
if (staticLogFileName) {
int endLength = files[i].length() - index;
if (baseFileName.length() + endLength != files[i].length()) {
//file is probably scheduledFilename + .x so I don't care
continue;
}
}
try {
int backup = Integer.parseInt(files[i].substring(index + 1, files[i].length()));
LogLog.debug("From file: " + files[i] + " -> " + backup);
if (backup > curSizeRollBackups)
curSizeRollBackups = backup;
}
catch (Exception e) {
//this happens when file.log -> file.log.yyyy-mm-dd which is normal
//when staticLogFileName == false
LogLog.debug("Encountered a backup file not ending in .x " + files[i]);
}
}
}
LogLog.debug("curSizeRollBackups starts at: " + curSizeRollBackups);
//part A ends here
//part B not yet implemented
//part C
if (staticLogFileName && rollDate) {
File old = new File(baseFileName);
if (old.exists()) {
Date last = new Date(old.lastModified());
if (!(sdf.format(last).equals(sdf.format(now)))) {
scheduledFilename = baseFileName + sdf.format(last);
LogLog.debug("Initial roll over to: " + scheduledFilename);
rollOverTime();
}
}
}
LogLog.debug("curSizeRollBackups after rollOver at: " + curSizeRollBackups);
//part C ends here
}
/**
* Sets initial conditions including date/time roll over information, first check,
* scheduledFilename, and calls <code>existingInit</code> to initialize
* the current # of backups.
*/
public void activateOptions() {
//REMOVE removed rollDate from boolean to enable Alex's change
if(datePattern != null) {
now.setTime(System.currentTimeMillis());
sdf = new SimpleDateFormat(datePattern);
int type = computeCheckPeriod();
//printPeriodicity(type);
rc.setType(type);
//next line added as this removes the name check in rollOver
nextCheck = rc.getNextCheckMillis(now);
} else {
if (rollDate)
LogLog.error("Either DatePattern or rollingStyle options are not set for ["+
name+"].");
}
existingInit();
super.activateOptions();
if (rollDate && fileName != null && scheduledFilename == null)
scheduledFilename = fileName + sdf.format(now);
}
/**
Rollover the file(s) to date/time tagged file(s).
Opens the new file (through setFile) and resets curSizeRollBackups.
*/
protected void rollOverTime() {
curTimeRollBackups++;
//delete the old stuff here
if (staticLogFileName) {
/* Compute filename, but only if datePattern is specified */
if (datePattern == null) {
errorHandler.error("Missing DatePattern option in rollOver().");
return;
}
//is the new file name equivalent to the 'current' one
//something has gone wrong if we hit this -- we should only
//roll over if the new file will be different from the old
String dateFormat = sdf.format(now);
if (scheduledFilename.equals(fileName + dateFormat)) {
errorHandler.error("Compare " + scheduledFilename + " : " + fileName + dateFormat);
return;
}
// close current file, and rename it to datedFilename
this.closeFile();
//we may have to roll over a large number of backups here
String from, to;
for (int i = 1; i <= curSizeRollBackups; i++) {
from = fileName + '.' + i;
to = scheduledFilename + '.' + i;
rollFile(from, to);
}
rollFile(fileName, scheduledFilename);
}
try {
// This will also close the file. This is OK since multiple
// close operations are safe.
curSizeRollBackups = 0; //We're cleared out the old date and are ready for the new
//new scheduled name
scheduledFilename = fileName + sdf.format(now);
this.setFile(baseFileName, false);
}
catch(IOException e) {
errorHandler.error("setFile("+fileName+", false) call failed.");
}
}
/** Renames file <code>from</code> to file <code>to</code>. It
* also checks for existence of target file and deletes if it does.
*/
protected static void rollFile(String from, String to) {
File target = new File(to);
if (target.exists()) {
LogLog.debug("deleting existing target file: " + target);
target.delete();
}
File file = new File(from);
file.renameTo(target);
LogLog.debug(from +" -> "+ to);
}
/** Delete's the specified file if it exists */
protected static void deleteFile(String fileName) {
File file = new File(fileName);
if (file.exists()) {
file.delete();
}
}
/**
Implements roll overs base on file size.
<p>If the maximum number of size based backups is reached
(<code>curSizeRollBackups == maxSizeRollBackups</code) then the oldest
file is deleted -- it's index determined by the sign of countDirection.<br>
If <code>countDirection</code> < 0, then files
{<code>File.1</code>, ..., <code>File.curSizeRollBackups -1</code>}
are renamed to {<code>File.2</code>, ...,
<code>File.curSizeRollBackups</code>}. Moreover, <code>File</code> is
renamed <code>File.1</code> and closed.<br>
A new file is created to receive further log output.
<p>If <code>maxSizeRollBackups</code> is equal to zero, then the
<code>File</code> is truncated with no backup files created.
<p>If <code>maxSizeRollBackups</code> < 0, then <code>File</code> is
renamed if needed and no files are deleted.
*/
// synchronization not necessary since doAppend is alreasy synched
protected void rollOverSize() {
File file;
this.closeFile(); // keep windows happy.
LogLog.debug("rolling over count=" + ((CountingQuietWriter) qw).getCount());
LogLog.debug("maxSizeRollBackups = " + maxSizeRollBackups);
LogLog.debug("curSizeRollBackups = " + curSizeRollBackups);
LogLog.debug("countDirection = " + countDirection);
// If maxBackups <= 0, then there is no file renaming to be done.
if (maxSizeRollBackups != 0) {
if (countDirection < 0) {
// Delete the oldest file, to keep Windows happy.
if (curSizeRollBackups == maxSizeRollBackups) {
deleteFile(fileName + '.' + maxSizeRollBackups);
curSizeRollBackups--;
}
// Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
for (int i = curSizeRollBackups; i >= 1; i--) {
rollFile((fileName + "." + i), (fileName + '.' + (i + 1)));
}
curSizeRollBackups++;
// Rename fileName to fileName.1
rollFile(fileName, fileName + ".1");
} //REMOVE This code branching for Alexander Cerna's request
else if (countDirection == 0) {
//rollFile based on date pattern
curSizeRollBackups++;
now.setTime(System.currentTimeMillis());
scheduledFilename = fileName + sdf.format(now);
rollFile(fileName, scheduledFilename);
}
else { //countDirection > 0
if (curSizeRollBackups >= maxSizeRollBackups && maxSizeRollBackups > 0) {
//delete the first and keep counting up.
int oldestFileIndex = curSizeRollBackups - maxSizeRollBackups + 1;
deleteFile(fileName + '.' + oldestFileIndex);
}
if (staticLogFileName) {
curSizeRollBackups++;
rollFile(fileName, fileName + '.' + curSizeRollBackups);
}
}
}
try {
// This will also close the file. This is OK since multiple
// close operations are safe.
this.setFile(baseFileName, false);
}
catch(IOException e) {
LogLog.error("setFile("+fileName+", false) call failed.", e);
}
}
}

View File

@ -0,0 +1,97 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.varia.test;
import org.apache.log4j.varia.JDBCAppender;
import org.apache.log4j.*;
public class JDBCTest
{
public static void main (String argv[])
{
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println(e.toString());
}
Category rootLog = Category.getRoot();
Layout layout = new PatternLayout("%p [%t] %c - %m%n");
JDBCAppender appender = new JDBCAppender();
appender.setLayout(layout);
appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
appender.setOption(JDBCAppender.USER_OPTION, "auser");
appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
rootLog.addAppender(appender);
try {
Category log = Category.getInstance("main");
log.debug("Debug 1");
Thread.sleep(500);
log.info("info 1");
Thread.sleep(500);
log.warn("warn 1");
Thread.sleep(500);
log.error("error 1");
Thread.sleep(500);
log.fatal("fatal 1");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
log.debug("Debug 2");
Thread.sleep(500);
log.info("info 2");
Thread.sleep(500);
log.warn("warn 2");
Thread.sleep(500);
log.error("error 2");
Thread.sleep(500);
log.fatal("fatal 2");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
appender.setThreshold(Priority.WARN);
log.debug("Debug 3");
Thread.sleep(500);
log.info("info 3");
Thread.sleep(500);
log.warn("warn 3");
Thread.sleep(500);
log.error("error 3");
Thread.sleep(500);
log.fatal("fatal 3");
}
catch (InterruptedException e)
{
System.out.println("Interrupted");
}
}
}

View File

@ -0,0 +1,559 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Delivered-To: urba-cgu@urbanet.ch
Mailing-List: contact log4j-user-help@jakarta.apache.org; run by ezmlm
List-Post: <mailto:log4j-user@jakarta.apache.org>
List-Help: <mailto:log4j-user-help@jakarta.apache.org>
List-Unsubscribe: <mailto:log4j-user-unsubscribe@jakarta.apache.org>
List-Subscribe: <mailto:log4j-user-subscribe@jakarta.apache.org>
Reply-To: "LOG4J Users Mailing List" <log4j-user@jakarta.apache.org>
Delivered-To: mailing list log4j-user@jakarta.apache.org
Date: Thu, 01 Feb 2001 14:26:34 -0800
From: Kevin Steppe <ksteppe@pacbell.net>
X-Mailer: Mozilla 4.76 [en] (Windows NT 5.0; U)
X-Accept-Language: en
To: LOG4J Users Mailing List <log4j-user@jakarta.apache.org>
Subject: JDBC Appender
X-Spam-Rating: h31.sny.collab.net 1.6.2 0/1000/N
Ok, here it is. Since there will be differences in database schemas and
connection/execution methods, I wrote this with the intention that those
parts would be overriden by subclasses (that's what I'm doing for my
company), however it will work as is if you have a stored procedure
spLog @msg. I'm sure there are optimizations which could be done.
The code for org.apache.log4j.varia.JDBCAppender and
org.apache.log4j.varia.test.JDBCTest follow and files attached. At the
bottem is the SQL I used to test this on M$ SQL-Server.
I help this proves useful,
Kevin
package org.apache.log4j.varia;
import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
/**
* Contribution from MD Data Direct.
*
* Implements an ArrayList buffer before storing messages to the DB.
* Override getSQL to fit your database schema (or implement spLog msg
on your DB)
* Override executeSQL to modify how DB connection and SQL execution is
made.
*
* @author: Kevin Steppe
*/
public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
implements org.apache.log4j.Appender
{
protected String databaseURL = "jdbc:odbc:myDB";
protected String databaseUser = "me";
protected String databasePassword = "mypassword";
public static final String URL_OPTION = "URL";
public static final String USER_OPTION = "User";
public static final String PASSWORD_OPTION = "Password";
public static final String BUFFER_OPTION = "Buffer";
protected int bufferSize = 1;
protected List buffer;
public JDBCAppender()
{
super();
buffer = new ArrayList();
}
public void append(LoggingEvent event)
{
buffer.add(event);
if (buffer.size() >= bufferSize)
flushBuffer();
}
public void close()
{
flushBuffer();
this.closed = true;
}
public void setOption(String key, String value)
{
super.setOption(key, value);
if (key.equalsIgnoreCase(URL_OPTION))
databaseURL = value;
else if (key.equalsIgnoreCase(USER_OPTION))
databaseUser = value;
else if (key.equalsIgnoreCase(PASSWORD_OPTION))
databasePassword = value;
else if (key.equalsIgnoreCase(BUFFER_OPTION))
bufferSize = Integer.parseInt(value);
}
/**
* Override this to create the SQL needed for your DB schema
*/
protected String getSQL(LoggingEvent event)
{
String msg = this.layout.format(event);
String sql = "spLog '" + msg + "'";
return sql;
}
/**
* Override this to provide an alertnate method of getting
connections (such as caching)
* This implementation creates a new connection and statement for
every execution which
* is very wastefull. One method to fix this is to open connections
at the start of
* flushBuffer() and close them at the end. MD Data uses a
connection pool outside
* of JDBCAppender which is accessed in the override of this method.
*/
protected void executeSQL(String sql) throws SQLException
{
Connection con = null;
Statement stmt = null;
try {
con = DriverManager.getConnection(databaseURL, databaseUser,
databasePassword);
stmt = con.createStatement();
stmt.executeUpdate(sql);
}
catch (SQLException e)
{
if (con != null)
con.close();
if (stmt != null)
stmt.close();
throw e;
}
stmt.close();
con.close();
}
public void flushBuffer()
{
//Do the actual logging
for (Iterator i = buffer.iterator(); i.hasNext();)
{
try {
String sql = getSQL((LoggingEvent)i.next());
executeSQL(sql);
}
catch (SQLException e)
{
errorHandler.error("Failed to excute sql", e,
ErrorCode.FLUSH_FAILURE);
}
}
buffer.clear();
}
public void finalize()
{
close();
}
public boolean requiresLayout()
{
return true;
}
}
package org.apache.log4j.varia.test;
import org.apache.log4j.varia.JDBCAppender;
import org.apache.log4j.*;
public class JDBCTest
{
public static void main (String argv[])
{
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println(e.toString());
}
Category rootLog = Category.getRoot();
Layout layout = new PatternLayout("%p [%t] %c - %m%n");
JDBCAppender appender = new JDBCAppender();
appender.setLayout(layout);
appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
appender.setOption(JDBCAppender.USER_OPTION, "auser");
appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
rootLog.addAppender(appender);
try {
Category log = Category.getInstance("main");
log.debug("Debug 1");
Thread.sleep(500);
log.info("info 1");
Thread.sleep(500);
log.warn("warn 1");
Thread.sleep(500);
log.error("error 1");
Thread.sleep(500);
log.fatal("fatal 1");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
log.debug("Debug 2");
Thread.sleep(500);
log.info("info 2");
Thread.sleep(500);
log.warn("warn 2");
Thread.sleep(500);
log.error("error 2");
Thread.sleep(500);
log.fatal("fatal 2");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
appender.setThreshold(Priority.WARN);
log.debug("Debug 3");
Thread.sleep(500);
log.info("info 3");
Thread.sleep(500);
log.warn("warn 3");
Thread.sleep(500);
log.error("error 3");
Thread.sleep(500);
log.fatal("fatal 3");
}
catch (InterruptedException e)
{
System.out.println("Interrupted");
}
}
}
drop table JDBCAppenderTest
go
create table JDBCAppenderTest (EventID int identity, entrytime datetime,
message varchar(255))
drop procedure spLog
go
create procedure spLog (@msg varchar(255)) as
insert into JDBCAppenderTest (message, entrytime) values (@msg,
getdate())
select * from JDBCAppenderTest
drop table JDBCAppenderTest
go
create table JDBCAppenderTest (EventID int identity, entrytime datetime, message varchar(255))
drop procedure spLog
go
create procedure spLog (@msg varchar(255)) as
insert into JDBCAppenderTest (message, entrytime) values (@msg, getdate())
select * from JDBCAppenderTest
package org.apache.log4j.varia;
import org.apache.log4j.*;
import org.apache.log4j.spi.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.SQLException;
/**
* Contribution from MD Data Direct.
*
* Implements an ArrayList buffer before storing messages to the DB.
* Override getSQL to fit your database schema (or implement spLog msg on your DB)
* Override executeSQL to modify how DB connection and SQL execution is made.
*
* @author: Kevin Steppe
*/
public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
implements org.apache.log4j.Appender
{
protected String databaseURL = "jdbc:odbc:myDB";
protected String databaseUser = "me";
protected String databasePassword = "mypassword";
public static final String URL_OPTION = "URL";
public static final String USER_OPTION = "User";
public static final String PASSWORD_OPTION = "Password";
public static final String BUFFER_OPTION = "Buffer";
protected int bufferSize = 1;
protected List buffer;
public JDBCAppender()
{
super();
buffer = new ArrayList();
}
public void append(LoggingEvent event)
{
buffer.add(event);
if (buffer.size() >= bufferSize)
flushBuffer();
}
public void close()
{
flushBuffer();
this.closed = true;
}
public void setOption(String key, String value)
{
super.setOption(key, value);
if (key.equalsIgnoreCase(URL_OPTION))
databaseURL = value;
else if (key.equalsIgnoreCase(USER_OPTION))
databaseUser = value;
else if (key.equalsIgnoreCase(PASSWORD_OPTION))
databasePassword = value;
else if (key.equalsIgnoreCase(BUFFER_OPTION))
bufferSize = Integer.parseInt(value);
}
/**
* Override this to create the SQL needed for your DB schema
*/
protected String getSQL(LoggingEvent event)
{
String msg = this.layout.format(event);
String sql = "spLog '" + msg + "'";
System.out.println(sql); //DEBUG
return sql;
}
/**
* Override this to provide an alertnate method of getting connections (such as caching)
* This implementation creates a new connection and statement for every execution which
* is very wastefull. One method to fix this is to open connections at the start of
* flushBuffer() and close them at the end. MD Data uses a connection pool outside
* of JDBCAppender which is accessed in the override of this method.
*/
protected void executeSQL(String sql) throws SQLException
{
Connection con = null;
Statement stmt = null;
try {
con = DriverManager.getConnection(databaseURL, databaseUser, databasePassword);
stmt = con.createStatement();
stmt.executeUpdate(sql);
}
catch (SQLException e)
{
if (con != null)
con.close();
if (stmt != null)
stmt.close();
throw e;
}
stmt.close();
con.close();
}
public void flushBuffer()
{
//Do the actual logging
for (Iterator i = buffer.iterator(); i.hasNext();)
{
try {
String sql = getSQL((LoggingEvent)i.next());
executeSQL(sql);
}
catch (SQLException e)
{
errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE);
}
}
buffer.clear();
}
public void finalize()
{
close();
}
public boolean requiresLayout()
{
return true;
}
}package org.apache.log4j.varia.test;
import org.apache.log4j.varia.JDBCAppender;
import org.apache.log4j.*;
public class JDBCTest
{
public static void main (String argv[])
{
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch (Exception e)
{
e.printStackTrace();
System.out.println(e.toString());
}
Category rootLog = Category.getRoot();
Layout layout = new PatternLayout("%p [%t] %c - %m%n");
JDBCAppender appender = new JDBCAppender();
appender.setLayout(layout);
appender.setOption(JDBCAppender.URL_OPTION, "jdbc:odbc:someDB");
appender.setOption(JDBCAppender.USER_OPTION, "auser");
appender.setOption(JDBCAppender.PASSWORD_OPTION, "thepassword");
rootLog.addAppender(appender);
try {
Category log = Category.getInstance("main");
log.debug("Debug 1");
Thread.sleep(500);
log.info("info 1");
Thread.sleep(500);
log.warn("warn 1");
Thread.sleep(500);
log.error("error 1");
Thread.sleep(500);
log.fatal("fatal 1");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "5");
log.debug("Debug 2");
Thread.sleep(500);
log.info("info 2");
Thread.sleep(500);
log.warn("warn 2");
Thread.sleep(500);
log.error("error 2");
Thread.sleep(500);
log.fatal("fatal 2");
Thread.sleep(500);
appender.setOption(JDBCAppender.BUFFER_OPTION, "2");
appender.setThreshold(Priority.WARN);
log.debug("Debug 3");
Thread.sleep(500);
log.info("info 3");
Thread.sleep(500);
log.warn("warn 3");
Thread.sleep(500);
log.error("error 3");
Thread.sleep(500);
log.fatal("fatal 3");
}
catch (InterruptedException e)
{
System.out.println("Interrupted");
}
}
}

View File

@ -0,0 +1,118 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Received: (qmail 12476 invoked from network); 28 Mar 2002 06:19:49 -0000
Date: Wed, 27 Mar 2002 22:28:58 -0800
From: Kevin Steppe <ksteppe@pacbell.net>
Subject: Re: RollingFileAppender and DailyRollingFileAppender
To: Log4J Users List <log4j-user@jakarta.apache.org>
Reply-To: ksteppe@pacbell.net
Message-id: <3CA2B82A.5C366593@pacbell.net>
MIME-version: 1.0
X-Mailer: Mozilla 4.7 [en] (WinNT; I)
Content-type: multipart/mixed; boundary="Boundary_(ID_TnKsnil+d0oYB9TV0P+fgA)"
X-Accept-Language: en
References:
<8DAB344CC3F3FE42A51FA1A8E295F8682ACD14@tepg-server2.tepgsyd.tycoint.com.au>
X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N
I wrote a CompositeRollingAppender last August to do that. I'm attaching that
code and some example config files to you separately.
Ceki... Could you please put these files in the log4j/contribs/KevinSteppe
folder of the available releases? This seems to be coming up occasionally and
it would be more convenient to just point people there.
Thanks,
Kevin
Janusz Dalecki wrote:
> Is there a way of having the mixture of both RollingFileAppender and
> DailyRollingFileAppender options?. I have a requirement to log daily and
> delete old files weekly.
> Thanks,
> Janusz
>
> --
> To unsubscribe, e-mail: <mailto:log4j-user-unsubscribe@jakarta.apache.org>
> For additional commands, e-mail: <mailto:log4j-user-help@jakarta.apache.org>
#Config file for CompositeRollingAppender
#This is an example config to use CompositeRollingAppender in Size based Backups only
log4j.rootCategory=debug, R
log4j.appender.R=org.apache.log4j.CompositeRollingAppender
#How to perform rolling -- 1 = By Size
#Note -- this is the only difference from RollingFileAppender!
log4j.appender.R.RollingStyle=1
#file to log to
log4j.appender.R.File=example.log
#Size Rolling params
log4j.appender.R.MaxFileSize=10MB
log4j.appender.R.MaxSizeRollBackups=10
#layout options
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
#Config file for CompositeRollingAppender
#This is an example config to use CompositeRollingAppender in Time based Backups only
log4j.rootCategory=debug, R
log4j.appender.R=org.apache.log4j.CompositeRollingAppender
#How to perform rolling -- 2 = By Time
#Note -- this is the only difference from DailyRollingFileAppender!
log4j.appender.R.RollingStyle=2
#file to log to
log4j.appender.R.File=example.log
#Date Rolling params
log4j.appender.R.datePattern='.'yyyy-MM-dd
#layout options
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
#Default Config file for CompositeRollingAppender
log4j.rootCategory=debug, R
log4j.appender.R=org.apache.log4j.CompositeRollingAppender
#How to perform rolling -- Composite is the default
log4j.appender.R.RollingStyle=3
#Use same file name for all inprocess logging?
log4j.appender.R.staticLogFileName=true
log4j.appender.R.File=example.log
#Size Rolling params
log4j.appender.R.CountDirection=-1
log4j.appender.R.MaxFileSize=5KB
log4j.appender.R.MaxSizeRollBackups=10
#Date Rolling params
log4j.appender.R.datePattern='.'yyyy-MM-dd
#layout options
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

View File

@ -0,0 +1,284 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.net;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.UnknownHostException;
import java.net.SocketException;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.Category;
import org.apache.log4j.Priority;
import org.apache.log4j.Layout;
import org.apache.log4j.helpers.SingleLineTracerPrintWriter;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.QuietWriter;
/**
Use DatagramStringAppender to send log messages to a remote daemon
which accepts Datagram (UDP) messages.
<p>
The benefits of UDP are that the client is guarunteed not to
slow down if the network or remote log daemon is slow, and that
no permanent TCP connection between client and server exists.
<p>
The disadvantages are that log messages can be lost if the network
or remote daemon are under excessive load.
<p>
This class builts the final message string <b>before</b> sending
the UDP packet, hence the "string" component in the class name. This
means that the receiving application can be written in any language.
The data is transmitted in whatever encoding is specified in the
configuration file; this may be an 8-bit encoding (eg ISO-8859-1, also
known as LATIN-1) or a larger encoding, eg UTF-16.
<p>
An alternative to building the message string within DatagramStringAppender
would be to serialize & send the complete logging event object (perhaps
such a class could be called a DatagramSerialAppender??). The
receiving end could then be configured with appropriate Layout objects
to generate the actual logged messages. This would ensure that the
logging of messages from different sources is done in a consistent
format, and give a central place to configure that format. It would ensure
(by transmitting messages as unicode) that the receiving end could control
the encoding in which the output is generated. It also would possibly allow
he receiving end to use the full log4j flexibility to pass the event to
different appenders at the receiving end, as the category information is
retained, etc. However, this does require that the receiving end is in
java, and that all clients of the logging daemon are java applications.
In contrast, this DatagramStringAppender can send mesages to a log daemon
that accepts messages from a variety of sources.
@author Simon Kitching
*/
public class DatagramStringAppender extends AppenderSkeleton {
/**
A string constant used in naming the option for setting the destination
server for messages. Current value of this string constant is
<b>DatagramHost</b>. */
public static final String DATAGRAM_HOST_OPTION = "DatagramHost";
/**
A string constant used in naming the option for setting the destination
port for messages. Current value of this string constant is
<b>DatagramPort</b>. */
public static final String DATAGRAM_PORT_OPTION = "DatagramPort";
/**
A string constant used in naming the option for setting the character
encoding used when generating the log message. Current value of this
string constant is <b>DatagramEncoding</b>. */
public static final String DATAGRAM_ENCODING_OPTION = "DatagramEncoding";
/**
The default value for the "host" attribute, ie the machine to which
messages are sent. Current value of this string constant is
<b>localhost</b>. */
public static final String DEFAULT_HOST = "localhost";
/**
The default value for the "port" attribute, ie the UDP port to which
messages are sent. Current value of this integer constant is
<b>8200</b>. This value was chosen for no particular reason. */
public static final int DEFAULT_PORT = 8200;
/**
The default value for the "encoding" attribute, ie the way in which
unicode message strings are converted into a stream of bytes before
their transmission as a UDP packet. The current value of this constant
is <b>null</b>, which means that the default platform encoding will
be used. */
public static final String DEFAULT_ENCODING = null;
String host = DEFAULT_HOST;
int port = DEFAULT_PORT;
String encoding = DEFAULT_ENCODING;
SingleLineTracerPrintWriter stp;
QuietWriter qw;
public
DatagramStringAppender() {
this.setDestination(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_ENCODING);
}
public
DatagramStringAppender(Layout layout) {
this.setLayout(layout);
this.setDestination(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_ENCODING);
}
public
DatagramStringAppender(Layout layout, String host, int port) {
this.setLayout(layout);
this.setDestination(host, port, DEFAULT_ENCODING);
}
public
DatagramStringAppender(Layout layout, String host, int port, String encoding) {
this.setLayout(layout);
this.setDestination(host, port, encoding);
}
/**
Release any resources held by this Appender
*/
public
void close() {
closed = true;
// A DatagramWriter is UDP based and needs no opening. Hence, it
// can't be closed. We just unset the variables here.
qw = null;
stp = null;
}
public
void append(LoggingEvent event) {
if(!isAsSevereAsThreshold(event.priority))
return;
// We must not attempt to append if qw is null.
if(qw == null) {
errorHandler.error(
"No host is set for DatagramStringAppender named \""
+ this.name + "\".");
return;
}
String buffer = layout.format(event);
qw.write(buffer);
if(event.throwable != null)
event.throwable.printStackTrace(stp);
else if (event.throwableInformation != null) {
// we must be the receiver of a serialized/deserialized LoggingEvent;
// the event's throwable member is transient, ie becomes null when
// deserialized, but that's ok because throwableInformation should
// have the string equivalent of the same info (ie stack trace)
qw.write(event.throwableInformation);
}
}
/**
Activate the options set via the setOption method.
@see #setOption
*/
public
void activateOptions() {
this.setDestination(this.host, this.port, this.encoding);
}
/**
Returns the option names for this component, namely the string
array consisting of {{@link #DATAGRAM_HOST_OPTION}, {@link
#DATAGRAM_PORT_OPTION}, {@link #DATAGRAM_ENCODING_OPTION} */
public
String[] getOptionStrings() {
return OptionConverter.concatanateArrays(super.getOptionStrings(),
new String[] {
DATAGRAM_HOST_OPTION,
DATAGRAM_PORT_OPTION,
DATAGRAM_ENCODING_OPTION});
}
/**
The DatagramStringAppender requires a layout. Hence, this method return
<code>true</code>.
@since 0.8.4 */
public
boolean requiresLayout() {
return true;
}
/**
Set DatagramStringAppender specific parameters.
<p>
The recognized options are <b>DatagramHost</b>, <b>DatagramPort</b> and
<b>DatagramEncoding</b>, i.e. the values of the string constants
{@link #DATAGRAM_HOST_OPTION}, {@link #DATAGRAM_PORT_OPTION} and
{@link #DATAGRAM_ENCODING_OPTION} respectively.
<p>
<dl>
<p>
<dt><b>DatagramHost</b>
<dd>
The name (or ip address) of the host machine where log output should go.
If the DatagramHost is not set, then this appender will default to
{@link #DEFAULT_HOST}.
<p>
<dt><b>DatagramPort</b>
<dd>
The UDP port number where log output should go. See {@link #DEFAULT_PORT}
<p>
<dt><b>DatagramEncoding</b>
<dd>
The ISO character encoding to be used when converting the Unicode
message to a sequence of bytes within a UDP packet. If not defined, then
the encoding defaults to the default platform encoding.
</dl>
*/
public
void setOption(String option, String value) {
if(value == null) return;
super.setOption(option, value);
if(option.equals(DATAGRAM_HOST_OPTION))
{
this.host = value;
}
else if(option.equals(DATAGRAM_PORT_OPTION))
{
this.port = OptionConverter.toInt(value, DEFAULT_PORT);
}
else if(option.equals(DATAGRAM_ENCODING_OPTION))
{
this.encoding = value;
}
}
public
void setDestination(String host, int port, String encoding) {
if (host==null) {
LogLog.error("setDestination: host is null");
host = DEFAULT_HOST;
}
this.host = host;
this.port = port;
this.encoding = encoding;
this.qw = new QuietWriter(
new DatagramStringWriter(host, port, encoding),
errorHandler);
this.stp = new SingleLineTracerPrintWriter(qw);
}
public
void setLayout(Layout layout) {
this.layout = layout;
}
}

View File

@ -0,0 +1,173 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.net;
import java.io.Writer;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.UnknownHostException;
import java.net.SocketException;
import java.io.IOException;
import org.apache.log4j.helpers.LogLog;
/**
* DatagramStringWriter is a wrapper around the java.net.DatagramSocket class
* so that it behaves like a java.io.Writer.
*/
public class DatagramStringWriter extends Writer {
static final int SYSLOG_PORT = 514;
private int port;
private String host;
private String encoding;
private String prefix;
private InetAddress address;
private DatagramSocket ds;
/**
* This constructor assumes that it is sending to a remote syslog daemon
* on the normal syslog port (514), and uses the default platform character
* encoding when converting the message string to a byte sequence.
*/
public
DatagramStringWriter(String host) {
this(host, SYSLOG_PORT, null, null);
}
/**
* This constructor sends messages to the specified host and port, and
* uses the default platform character encoding when converting the message
* string to a byte sequence.
*/
public
DatagramStringWriter(String host, int port) {
this(host, port, null, null);
}
/**
* This constructor sends messages to the specified host and port, and
* uses the specified character encoding when converting the message
* string to a byte sequence.
*/
public
DatagramStringWriter(String host, int port, String encoding) {
this(host, port, null, null);
}
/**
* This constructor sends messages to the specified host and port, and
* uses the specified character encoding when converting the message
* string to a byte sequence; the specified prefix (which may be null)
* is prepended to each message.
*/
public
DatagramStringWriter(String host, int port, String encoding, String prefix) {
this.host = host;
this.port = port;
this.encoding = encoding;
this.prefix = prefix;
try {
this.address = InetAddress.getByName(host);
}
catch (UnknownHostException e) {
LogLog.error("Could not find " + host +
". All logging will FAIL.", e);
}
try {
this.ds = new DatagramSocket();
}
catch (SocketException e) {
e.printStackTrace();
LogLog.error("Could not instantiate DatagramSocket to " + host +
". All logging will FAIL.", e);
}
}
public
void write(char[] buf, int off, int len) throws IOException {
this.write(new String(buf, off, len));
}
public
void write(String string) throws IOException {
if (prefix != null) {
string = prefix + string;
}
byte[] rawData;
if (this.encoding == null)
{
// convert to byte sequence using platform's default encoding
rawData = string.getBytes();
}
else
{
// convert to specified encoding - which may be sequence of
// 8-bit chars, or multi-byte encodings like UTF-8 or UTF-16.
// The receiving end had better be expecting whatever encoding
// is used here on the sending end!
rawData = string.getBytes(encoding);
}
DatagramPacket packet =
new DatagramPacket(
rawData,
rawData.length,
address,
port);
if(this.ds != null)
{
ds.send(packet);
}
else
{
LogLog.error(
"write: failed to create DatagramPacket");
}
}
public
void flush() {}
public
void close() {}
/**
* Set a string to be prefixed to every message sent by this Writer.
* For example, this method could be used to prepend a syslog
* facility/priority code on the front of each message.
* <p>
* Note that this method is not synchronised, so should not be called in
* a situation where other threads may be logging messages at the same
* moment.
* <p>
* @param prefix may be a prefix string, or null which indicates no
* prefix should be added.
*/
public
void setPrefix(String prefix){
this.prefix = prefix;
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.helpers;
/**
SingleLineTracerPrintWriter overrides the println function in
TracerPrintWriter by replacing the TAB character with spaces.
It also does not print the "\n".
<p>
The default format generated by TracerPrintWriter for exceptions
prints on multiple lines, which does not interact well with some
logging systems. On the other hand, a stack-trace on one line can be a
mite difficult to read, so this class should only be used where really
necessary :-)
<p>
For syslog daemons, tabs in messages are not friendly, hence the
replacement of tabs by spaces here. It shouldn't do any harm to
do this for all messages...
<p>
Perhaps it might be better to enhance TracerPrintWriter to have
a configuration item for one-line or multi-line mode...
*/
public class SingleLineTracerPrintWriter extends TracerPrintWriter {
static String TAB = " ";
public SingleLineTracerPrintWriter(QuietWriter qWriter) {
super(qWriter);
}
/**
Make the first Exception line print properly by omitting the \n at the
end.
*/
public
void println(Object o) {
this.qWriter.write(o.toString());
}
// Note: the Char[] form is handled by the TracerPrinterWriter super
// class
/**
Remove the first character from the string (usually a TAB) and do
not print "\n"
*/
public
void println(String s) {
// remove '^I' and replace it with 4 spaces
this.qWriter.write(TAB+s.substring(1));
}
}

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<configuration configDebug="true">
<appender name="STDOUT" class="org.apache.log4j.FileAppender">
<param name="File" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="aa:%p#%d#%c#%m%n"/>
</layout>
</appender>
<appender name="UDPVENUS" class="org.apache.log4j.net.DatagramStringAppender">
<param name="DatagramHost" value="Venus" />
<param name="DatagramPort" value="8300" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern"
value="%p#%d#%c#%m"/>
</layout>
<filter class="org.apache.log4j.filters.PriorityRangeFilter">
<param name="PriorityMin" value="WARN" />
</filter>
</appender>
<category name="org.apache.log4j.xml">
<priority value="INFO" />
</category>
<category name="ch">
<priority value ="DEBUG2" class="ch.orange.log.Priority"/>
</category>
<category name="script">
<priority value ="DEBUG2" class="ch.orange.log.Priority"/>
</category>
<root>
<priority value ="DEBUG2" class="ch.orange.log.Priority"/>
<appender-ref ref="STDOUT" />
<appender-ref ref="UDPVENUS" />
</root>
</configuration>

View File

@ -0,0 +1,107 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
Delivered-To: urba-cgu@urbanet.ch
Mailing-List: contact log4j-dev-help@jakarta.apache.org; run by ezmlm
List-Post: <mailto:log4j-dev@jakarta.apache.org>
List-Help: <mailto:log4j-dev-help@jakarta.apache.org>
List-Unsubscribe: <mailto:log4j-dev-unsubscribe@jakarta.apache.org>
List-Subscribe: <mailto:log4j-dev-subscribe@jakarta.apache.org>
Reply-To: "LOG4J Developers Mailing List" <log4j-dev@jakarta.apache.org>
Delivered-To: mailing list log4j-dev@jakarta.apache.org
From: Kitching Simon <Simon.Kitching@orange.ch>
To: "'log4j-dev@jakarta.apache.org'" <log4j-dev@jakarta.apache.org>
Subject: PATCH: New classes: DatagramStreamAppender & friends
Date: Wed, 7 Feb 2001 12:41:54 +0100
X-Mailer: Internet Mail Service (5.5.2650.21)
X-Spam-Rating: h31.sny.collab.net 1.6.2 0/1000/N
Hi log4j developers....
Here, for your consideration, is a set of files that
implement an Appender which sends messages
to a remote host/port via UDP (datagram).
There was brief discussion of this Appender on the
log4j-user group, about a week ago. Ceki suggested
that the Appender send serialized log event objects
over UDP; while this approach has a number of
advantages, I have decided to instead perform the
message formatting at the client end, mainly so
that:
(a) the UDP server application does not have to be in java
(b) non-java clients can send messages to the same UDP
server.
The appender has been named "DatagramStringAppender"
to allow someone to write a serialization-based version at
some later time, if desired, without any name confusion.
------------------------------
Notes:
DatagramStringAppender is based on SyslogAppender, but
with a fair number of changes.
DatagramStringWriter is based on SyslogWriter, with a few changes.
In particular, it takes an "encoding" parameter, so that the character
encoding used can be specified, and a "port".
SingleLineTracerPrintWriter is almost identical to
SyslogTracerPrintWriter; just the name & some comments
have changed. [I didn't want to call a class called SyslogXXX
from the DatagramStreamAppender classes]
------------------------------
If this patch is accepted, then it may be worth rewriting SyslogAppender
to use the DatagramStringWriter and SingleLineTracerPrintWriter
classes. These classes should be compatible with SyslogAppender,
as they implement the same functionality, or a superset of the
Syslog functionality, and have more "general" names.
------------------------------
<<DatagramStringAppender.java>>
<<DatagramStringWriter.java>>
<<SingleLineTracerPrintWriter.java>>
--------------------------------
And here's a simple perl UDP server, and an xml
config file for testing the appender.
<<logconfig.xml>>
<<udpserver.pl>>
Regards,
Simon

View File

@ -0,0 +1,82 @@
#!/opt/perl5/bin/perl -w
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
use strict;
use Socket;
use IO::Socket;
use Sys::Hostname;
main();
exit(0);
sub main()
{
my $MAX_MSG_SIZE = 16384; # 16KBytes should be enough...
my $svrport = 8300;
my $svriaddr = gethostbyname(hostname());
my $svrproto = getprotobyname('udp');
my $svrpaddr = sockaddr_in($svrport, $svriaddr);
socket(SOCKET, PF_INET, SOCK_DGRAM, $svrproto) || die "socket: $!";
bind(SOCKET, $svrpaddr) || die "bind: $!";
my $rin = '';
vec($rin, fileno(SOCKET), 1) = 1;
# timeout after 10.0 seconds
# at some time, I'm going to add signal handlers so that signals can be
# sent to cause logfile rollover, or tidy exit...then the timeout will
# come in useful..
my $exit = 0;
while (!$exit)
{
my $rout = $rin;
# select(readvec, writevec, exceptionvec, timeout)
# : returns # of selected filehandles, modifies
# vector parameters so that set bits indicate
# filehandles which are readable, writable or have
# an exception state
my $nSelected = select($rout, undef, undef, 10.0);
if ($nSelected == 0)
{
# timedout : go back to start of loop
next;
}
my $msgData = '';
my $clientpaddr = recv(SOCKET, $msgData, $MAX_MSG_SIZE, 0);
if (!$clientpaddr)
{
die "recv: $!";
}
my ($clientport, $clientiaddr) = sockaddr_in($clientpaddr);
my $clienthost = gethostbyaddr($clientiaddr, AF_INET);
if (!$clienthost)
{
# unable to determine name for client : show raw ip address
$clienthost = inet_ntoa($clientiaddr);
}
print "$clienthost:$msgData\n";
}
}

View File

@ -0,0 +1,198 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j;
import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
import java.io.BufferedWriter;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.OptionConverter;
import org.apache.log4j.spi.ErrorHandler;
/**
TempFileAppender creates new unique file for each logging statement.
@author <a HREF="mailto:leos.literak@12snap.com">Leos Literak</a>
@author Ceki G&uuml;lc&uuml;
*/
public class TempFileAppender extends AppenderSkeleton {
/**
A string constant used in naming the option for setting the
directory where the log files will be created. Current value
of this string constant is <b>Path</b>. java.io.tmpdir directory
will be used, if ommited.
*/
static final public String PATH_OPTION = "Path";
/**
The default path is actual directory.
*/
protected String path = null;
/**
A string constant used in naming the option for setting the
prefix of the log files. It has to have at least 3 characters!
Current value of this string constant is <b>Prefix</b>.
*/
static final public String PREFIX_OPTION = "Prefix";
/**
The default path is actual directory.
*/
protected String prefix = "l4j_";
/**
A string constant used in naming the option for setting the
suffix of the log files. Current value of this string constant
is <b>Suffix</b>.
*/
static final public String SUFFIX_OPTION = "Suffix";
/**
The default path is actual directory.
*/
protected String suffix = ".tmp";
/**
Default dir
*/
protected File dir = null;
/**
The default constructor simply calls its parent's constructor.
*/
public TempFileAppender() {
super();
}
/**
Retuns the option names for this component
*/
public String[] getOptionStrings() {
return OptionConverter.concatanateArrays(super.getOptionStrings(),
new String[] {PATH_OPTION,PREFIX_OPTION,SUFFIX_OPTION});
}
/**
Set TempFileAppender specific options.
The recognized options are <b>Path</b>, <b>Prefix</b> and <b>Suffix</b>,
i.e. the values of the string constants {@link #PATH_OPTION},
{@link #PREFIX_OPTION} and respectively {@link #SUFFIX_OPTION}.
The options of the super class {@link AppenderSkeleton} are also
recognized.
*/
public void setOption(String key, String value) {
super.setOption(key, value);
if(key.equalsIgnoreCase(PATH_OPTION)) {
path = value;
if(path==null) {
errorHandler.error("Path cannot be empty!",null,0);
}
dir = new File(path);
if(!(dir.exists() && dir.isDirectory() && dir.canWrite())) {
errorHandler.error("Cannot write to directory " + path + "!",null,0);
}
}
else if(key.equalsIgnoreCase(PREFIX_OPTION)) {
if(value!=null && value.length()>=3) {
prefix = value;
} else {
errorHandler.error("Prefix cannot be shorter than 3 characters!",
null,0);
}
}
else if(key.equalsIgnoreCase(SUFFIX_OPTION)) {
if(value!=null && value.length()>=1) {
suffix = value;
} else {
errorHandler.error("Suffix cannot be empty!",null,0);
}
}
}
/**
This method is called by {@link AppenderSkeleton#doAppend}
method.
<p>Whenever this method is called, new unique file is created
with specified prefix and suffix. The file is closed afterwards.
<p>The format of the output will depend on this appender's
layout.
*/
public void append(LoggingEvent event) {
if(!checkEntryConditions()) {
return;
}
subAppend(event);
}
/**
This method determines if there is a sense in attempting to append.
*/
protected boolean checkEntryConditions() {
return true;
}
/**
This method does actual writing
*/
protected void subAppend(LoggingEvent event) {
try {
File tmp = File.createTempFile(prefix,suffix,dir);
Writer out = new BufferedWriter(new FileWriter(tmp));
out.write(event.message);
out.close();
/* this Appender is not supposed to be used for logging of Exceptions */
} catch (Exception e) {
errorHandler.error("Error during creation of temporary File!",e,1);
}
}
public boolean requiresLayout() {
return false;
}
public void close() {
/* nothing to do */
}
}
/*
* @author $Author: carnold $
* @version $Revision: 511036 $
* @since $Date: 2007-02-23 11:48:53 -0600 (Fri, 23 Feb 2007) $
*
* $Log$
* Revision 1.1.2.1 2005/05/27 03:27:54 mwomack
* Fix for #35032. Added license header to .java files that did not already have a license.
*
* Revision 1.1 2001/04/20 17:38:31 ceki
*
* Added LeosLiterak's TempFileAppender.java
*
*/

View File

@ -0,0 +1,58 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
To: ceki@apache.org
Subject: TempFileAppender
Hi Ceki,
I have created one small Appender, which puts each
logging statement into separate file. We use it
for storing incomming messages into second process'es
spool directory. If you like it, please include it
into Log4j with APL license.
Usage:
log4j.appender.A1=org.apache.log4j.TempFileAppender
log4j.appender.A1.Path=spool_dir
log4j.appender.A1.Prefix=out_
log4j.appender.A1.Suffix=.msg
Leo
-----------------------------------------------------
Leos Literak
Software Engineer
12snap s.r.o.
Pstrossova 24
110 00 Praha 1
Czech Republic
mobile: ?605-849-087
phone: ?2-21-970-239
fax: ?2-21-970-241
e-mail: leos.literak@12snap.com

View File

@ -0,0 +1,33 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Connected to client at RDDSOWE2/129.153.78.195
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Starting new socket node.
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Waiting to accept a new client.
INFO ( RDDSOWE2:main ) systemsunion.SSTS.system - ClosedownController: starting
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Connected to client at RDDSOWE2/129.153.78.195
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Starting new socket node.
INFO ( Server:main ) systemsunion.LoggingServer.SocketServer2 - Waiting to accept a new client.
INFO ( RDDSOWE2:main ) systemsunion.SSTS.system - ClosedownController: starting
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing class file d:/SSDev/SSTS/components\Operator\Operator.class for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing class file d:/SSDev/SSTS/components\Operator\Operator.class for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing proxy class file d:/SSDev/SSTS/components\Operator\OperatorProxy.class for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing class file d:/SSDev/SSTS/components\Operator\OperatorProxy.class for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing test XML page file d:/SSDev/SSTS/components\Operator\Operator.XML for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing class file d:/SSDev/SSTS/components\Operator\Operator.XML for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing descriptor file d:/SSDev/SSTS/components\Operator\OperatorDeploymentDescriptor.XML for component Operator
INFO ( RDDSOWE2:AWT-EventQueue-0) systemsunion.SSTS.components - Packing class file d:/SSDev/SSTS/components\Operator\OperatorDeploymentDescriptor.XML for component Operator

View File

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.systemsunion.LoggingServer;
import java.net.InetAddress;
import java.net.Socket;
import java.net.ServerSocket;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.apache.log4j.Category;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.Priority;
import org.apache.log4j.NDC;
// Contributors: Moses Hohman <mmhohman@rainbow.uchicago.edu>
/**
Read {@link LoggingEvent} objects sent from a remote client using
Sockets (TCP). These logging events are logged according to local
policy, as if they were generated locally.
<p>For example, the socket node might decide to log events to a
local file and also resent them to a second socket node.
@author Ceki G&uuml;lc&uuml;
@since 0.8.4
*/
public class SocketNode2 implements Runnable {
Socket socket;
ObjectInputStream ois;
static Category cat = Category.getInstance(SocketNode2.class.getName());
public
SocketNode2(Socket socket) {
this.socket = socket;
try {
ois = new ObjectInputStream(socket.getInputStream());
}
catch(Exception e) {
cat.error("Could not open ObjectInputStream to "+socket, e);
}
}
//public
//void finalize() {
//System.err.println("-------------------------Finalize called");
// System.err.flush();
//}
public void run() {
LoggingEvent event;
Category remoteCategory;
String strClientName;
// Get the client name.
InetAddress addr = socket.getInetAddress();
strClientName = addr.getHostName();
if(strClientName == null || strClientName.length() == 0)
{
strClientName = addr.getHostAddress();
}
try {
while(true) {
event = (LoggingEvent) ois.readObject();
if(event.ndc != null)
{
event.ndc = strClientName + ":" + event.ndc;
}
else
{
event.ndc = strClientName;
}
remoteCategory = Category.getInstance(event.categoryName);
remoteCategory.callAppenders(event);
}
}
catch(java.io.EOFException e) {
cat.info("Caught java.io.EOFException will close conneciton.", e);
}
catch(java.net.SocketException e) {
cat.info("Caught java.net.SocketException, will close conneciton.", e);
}
catch(Exception e) {
cat.error("Unexpected exception. Closing conneciton.", e);
}
try {
ois.close();
}
catch(Exception e) {
cat.info("Could not close connection.", e);
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.systemsunion.LoggingServer;
import java.net.Socket;
import java.net.ServerSocket;
import java.io.IOException;
import org.apache.log4j.Category;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.NDC;
/**
A simple {@link SocketNode} based server.
<pre>
<b>Usage:</b> java org.apache.log4j.net.SocketServer port configFile
where <em>port</em> is a part number where the server listens and
<em>configFile</em> is a configuration file fed to the {@link
PropertyConfigurator}.
</pre>
@author Ceki G&uuml;lc&uuml;
@since 0.8.4 */
public class SocketServer2 {
static Category cat = Category.getInstance(SocketServer2.class.getName());
static int port;
public
static
void main(String argv[]) {
if(argv.length == 2)
init(argv[0], argv[1]);
else
usage("Wrong number of arguments.");
try {
cat.info("Listening on port " + port);
ServerSocket serverSocket = new ServerSocket(port);
while(true) {
cat.info("Waiting to accept a new client.");
Socket socket = serverSocket.accept();
cat.info("Connected to client at " + socket.getInetAddress());
cat.info("Starting new socket node.");
new Thread(new SocketNode2(socket)).start();
}
}
catch(Exception e) {
e.printStackTrace();
}
}
static
void usage(String msg) {
System.err.println(msg);
System.err.println(
"Usage: java " + SocketServer2.class.getName() + " port configFile");
System.exit(1);
}
static
void init(String portStr, String configFile) {
try {
port = Integer.parseInt(portStr);
}
catch(java.lang.NumberFormatException e) {
e.printStackTrace();
usage("Could not interpret port number ["+ portStr +"].");
}
PropertyConfigurator.configure(configFile);
NDC.push("Server");
}
}

View File

@ -0,0 +1,66 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Delivered-To: urba-cgu@urbanet.ch
From: Mark Douglas <Mark_Douglas@systemsunion.com>
To: "'cgu@urbanet.ch'" <cgu@urbanet.ch>
Subject: Enhanced SocketServer and SocketNode classes
Date: Wed, 17 Jan 2001 09:57:38 -0000
X-Mailer: Internet Mail Service (5.5.2650.21)
Hi Ceki,
I have made small changes to SocketServer and SockerNode to allow the client
machine name to be displayed in the logging output. This is important for
us as we are using a single logging server with several clients.
The new SocketServer2 and SocketNode2 classes (couldn't think of better
names) prepends the NDC string with the clients Host Name (or IP address if
the HostName can not be found). I thought the NDC string was a good place
to add this information, but you may have a better place?
I have created an NDC entry for the server such that server logging messages
can easily be identified.
I have included the two new source files plus a short example of the output.
The output was generated with the following layout: %-6p (%9x:%-10t)
%-40c{3} - %m%n
<<Log.txt>> <<SocketNode2.java>> <<SocketServer2.java>>
Note: I have changed the package names for the two classes to fit with our
package names - sorry. Also, I find it difficult to follow the coding
style, so again, sorry.
If you think this may be useful to others, please feel free to include it as
an addition in the next release or change the current SocketServer and
SocketNode objects to include this new behaviour by default.
Mark Douglas
Systems Union Group plc

View File

@ -0,0 +1,365 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.Priority;
import org.apache.log4j.Category;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import javax.swing.JList;
import javax.swing.AbstractListModel;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.BoxLayout;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Container;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.awt.Rectangle;
public class JListView extends JList {
static Category cat = Category.getInstance(JListView.class.getName());
//JListViewModel model;
PatternLayout layout;
static LoggingEvent proto = new LoggingEvent("x", cat, Priority.ERROR,
"Message ", new Throwable());
public
JListView(JListViewModel model) {
super(model);
layout = new PatternLayout("%r %p %c [%t] - %m");
//this.setModel(model);
this.setCellRenderer(new MyCellRenderer());
// setFixedCellWidth(10);
//setFixedCellHeight(20);
}
public
void add(LoggingEvent event) {
((JListViewModel)getModel()).add(event);
}
/*
public
Dimension getPreferredSize() {
System.out.println("getPreferredSize() called");
return super.getPreferredSize();
}
public
int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction) {
System.out.println("getScrollableUnitIncrement called with " + visibleRect +
"orientation: "+orientation+", direction: "+direction);
return super.getScrollableUnitIncrement(visibleRect, orientation,
direction);
}
public
int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
int direction) {
System.out.println("getScrollableBlockIncrement called with " +
visibleRect + "orientation: "+orientation+
", direction: "+direction);
return super.getScrollableBlockIncrement(visibleRect, orientation,
direction);
}
*/
//public
//boolean getScrollableTracksViewportWidth() {
//System.out.println("getScrollableTracksViewportWidth called.");
//return true;
//boolean b = super.getScrollableTracksViewportWidth();
//System.out.println("result is: "+b);
//return b;
//}
//public
//boolean getScrollableTracksViewportHeight() {
// System.out.println("getScrollableTracksViewportHeight called.");
// return true;
//boolean b = super.getScrollableTracksViewportHeight();
//System.out.println("result is: "+b);
//return b;
//}
//public
//int getFirstVisibleIndex() {
//int r = getFirstVisibleIndex();
// System.out.println("----------getFirstVisibleIndex called, result: "+r);
//return r;
//}
//public
//Object getPrototypeCellValue() {
//return proto;
//}
static public void main(String[] args) {
JFrame frame = new JFrame("JListView test");
Container container = frame.getContentPane();
JListView view = new JListView(new JListViewModel(Integer.parseInt(args[0])));
JScrollPane sp = new JScrollPane(view);
sp.setPreferredSize(new Dimension(250, 80));
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
//container.add(view);
container.add(sp);
JButton b1 = new JButton("Add 1");
JButton b10 = new JButton("Add 10");
JButton b100 = new JButton("Add 100");
JButton b1000 = new JButton("Add 1000");
JButton b10000 = new JButton("Add 10000");
JPanel panel = new JPanel(new GridLayout(0,1));
container.add(panel);
panel.add(b1);
panel.add(b10);
panel.add(b100);
panel.add(b1000);
panel.add(b10000);
AddAction a1 = new AddAction(view, 1);
AddAction a10 = new AddAction(view, 10);
AddAction a100 = new AddAction(view, 100);
AddAction a1000 = new AddAction(view, 1000);
AddAction a10000 = new AddAction(view, 10000);
b1.addActionListener(a1);
b10.addActionListener(a10);
b100.addActionListener(a100);
b1000.addActionListener(a1000);
b10000.addActionListener(a10000);
frame.setVisible(true);
frame.setSize(new Dimension(700,700));
long before = System.currentTimeMillis();
int RUN = 1000;
int i = 0;
while(i++ < RUN) {
LoggingEvent event0 = new LoggingEvent("x", cat, Priority.ERROR,
"Message "+i, null);
Throwable t = new Exception("hello "+i);
LoggingEvent event1 = new LoggingEvent("x", cat, Priority.ERROR,
"Message "+i, t);
if(i % 10 == 0) {
event1.getThreadName();
view.add(event1);
} else {
event0.getThreadName();
view.add(event0);
}
}
long after = System.currentTimeMillis();
System.out.println("Time taken :"+ ((after-before)*1000/RUN));
}
class MyCellRenderer extends JTextArea implements ListCellRenderer {
Object o = new Object();
int i = 0;
final ImageIcon longIcon = new ImageIcon("RedFlag.gif");
public
MyCellRenderer() {
System.out.println("----------------------");
}
public
int getTabSize() {
return 2;
}
public Image loadIcon ( String path ) {
Image img = null;
try {
URL url = ClassLoader.getSystemResource(path);
img = (Image) (Toolkit.getDefaultToolkit()).getImage(url);
} catch (Exception e) {
System.out.println("Exception occured: " + e.getMessage() +
" - " + e );
}
return (img);
}
public Component getListCellRendererComponent(JList list,
Object value,
int index, // cell index
boolean isSelected,
boolean cellHasFocus) {
// System.out.println(o + " ============== " + i++);
//LogLog.error("=======", new Exception());
//setIcon(longIcon);
if(value instanceof LoggingEvent) {
LoggingEvent event = (LoggingEvent) value;
String str = layout.format(event);
String t = event.getThrowableInformation();
if(t != null) {
setText(str + Layout.LINE_SEP + t);
} else {
setText(str);
}
} else {
setText(value.toString());
}
return this;
}
}
}
class JListViewModel extends AbstractListModel {
CyclicBuffer cb;
JListViewModel(int size) {
cb = new CyclicBuffer(size);
}
public
void add(LoggingEvent event) {
//System.out.println("JListViewModel.add called");
cb.add(event);
int j = cb.length();
fireContentsChanged(this, 0, j);
}
public
Object getElementAt(int index) {
return cb.get(index);
}
public
int getSize() {
return cb.length();
}
}
class AddAction implements ActionListener {
Thread t;
static int counter = 0;
public
AddAction(JListView view, int burst) {
this.t = new AddThread(view, burst);
t.start();
}
public
void actionPerformed(ActionEvent e) {
System.out.println("Action occured");
synchronized(t) {
t.notify();
}
}
class AddThread extends Thread {
int burst;
JListView view;
Category cat = Category.getInstance("x");
AddThread(JListView view, int burst) {
super();
this.burst = burst;
this.view = view;
setName("AddThread"+burst);
}
public
void run() {
while(true) {
synchronized(this) {
try {
this.wait();
} catch(Exception e) {
}
}
for(int i = 0; i < burst; i++) {
LoggingEvent event = new LoggingEvent("x", cat, Priority.DEBUG,
"Message "+counter, null);
event.getThreadName();
if(counter % 50 == 0) {
//event.throwable = new Exception("hello "+counter);
}
counter++;
view.add(event);
}
}
}
}
}

View File

@ -0,0 +1,237 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui;
import org.apache.log4j.helpers.CyclicBuffer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.Priority;
import org.apache.log4j.Category;
import org.apache.log4j.Layout;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.spi.LoggingEvent;
import javax.swing.JList;
import javax.swing.AbstractListModel;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import java.awt.Component;
import java.awt.FlowLayout;
import javax.swing.BoxLayout;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.Container;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.awt.Rectangle;
public class JTableAppender extends JTable {
static Category cat = Category.getInstance(JTableAppender.class.getName());
PatternLayout layout;
public
JTableAppender() {
layout = new PatternLayout("%r %p %c [%t] - %m");
this.setDefaultRenderer(Object.class, new Renderer());
}
public
void add(LoggingEvent event) {
((JTableAppenderModel)getModel()).add(event);
}
public
Dimension getPreferredSize() {
System.out.println("getPreferredSize() called");
return super.getPreferredSize();
}
static public void main(String[] args) {
JFrame frame = new JFrame("JListView test");
Container container = frame.getContentPane();
JTableAppender appender = new JTableAppender();
JTableAppenderModel model = new
JTableAppenderModel(Integer.parseInt(args[0]));
appender.setModel(model);
//appender.createDefaultColumnsFromModel();
JScrollPane sp = new JScrollPane(appender);
sp.setPreferredSize(new Dimension(250, 80));
container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS));
//container.add(view);
container.add(sp);
JButton button = new JButton("ADD");
container.add(button);
button.addActionListener(new JTableAddAction(appender));
frame.setVisible(true);
frame.setSize(new Dimension(700,700));
long before = System.currentTimeMillis();
int RUN = 10000;
int i = 0;
while(i++ < RUN) {
LoggingEvent event = new LoggingEvent("x", cat, Priority.ERROR,
"Message "+i, null);
event.getThreadName();
if(i % 10 == 0) {
//event.throwable = new Exception("hello "+i);
}
appender.add(event);
}
long after = System.currentTimeMillis();
System.out.println("Time taken :"+ ((after-before)*1000/RUN));
}
class Renderer extends JTextArea implements TableCellRenderer {
Object o = new Object();
int i = 0;
public
Renderer() {
System.out.println("Render() called ----------------------");
}
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
System.out.println(o + " ============== " + i++);
//LogLog.error("=======", new Exception());
//setIcon(longIcon);
if(value instanceof LoggingEvent) {
LoggingEvent event = (LoggingEvent) value;
String str = layout.format(event);
String t = event.getThrowableInformation();
if(t != null) {
System.out.println("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
setText(str + Layout.LINE_SEP + t);
} else {
setText(str);
}
} else {
setText(value.toString());
}
return this;
}
}
}
class JTableAppenderModel extends AbstractTableModel {
CyclicBuffer cb;
JTableAppenderModel(int size) {
cb = new CyclicBuffer(size);
}
public
void add(LoggingEvent event) {
//System.out.println("JListViewModel.add called");
cb.add(event);
int j = cb.length();
fireTableDataChanged();
}
public
int getColumnCount() {
return 1;
}
public int getRowCount() {
return cb.length();
}
//public
//Class getColumnClass(int index) {
// System.out.println("getColumnClass called " + index);
// return LoggingEvent.class;
//}
public
Object getValueAt(int row, int col) {
return cb.get(row);
}
}
class JTableAddAction implements ActionListener {
int j;
JTableAppender appender;
Category cat = Category.getInstance("x");
public
JTableAddAction(JTableAppender appender) {
this.appender = appender;
j = 0;
}
public
void actionPerformed(ActionEvent e) {
System.out.println("Action occured");
LoggingEvent event = new LoggingEvent("x", cat, Priority.DEBUG,
"Message "+j, null);
if(j % 5 == 0) {
//event.throwable = new Exception("hello "+j);
}
j++;
appender.add(event);
}
}

View File

@ -0,0 +1,355 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui;
import java.awt.Color;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.*;
import java.net.URL;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;
import org.apache.log4j.*;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.helpers.Loader;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.helpers.TracerPrintWriter;
import org.apache.log4j.helpers.OptionConverter;
/**
* <b>Experimental</b> TextPaneAppender. <br>
*
*
* Created: Sat Feb 26 18:50:27 2000 <br>
*
* @author Sven Reimers
*/
public class TextPaneAppender extends AppenderSkeleton {
JTextPane textpane;
StyledDocument doc;
TracerPrintWriter tp;
StringWriter sw;
QuietWriter qw;
Hashtable attributes;
Hashtable icons;
private String label;
private boolean fancy;
final String LABEL_OPTION = "Label";
final String COLOR_OPTION_FATAL = "Color.Emerg";
final String COLOR_OPTION_ERROR = "Color.Error";
final String COLOR_OPTION_WARN = "Color.Warn";
final String COLOR_OPTION_INFO = "Color.Info";
final String COLOR_OPTION_DEBUG = "Color.Debug";
final String COLOR_OPTION_BACKGROUND = "Color.Background";
final String FANCY_OPTION = "Fancy";
final String FONT_NAME_OPTION = "Font.Name";
final String FONT_SIZE_OPTION = "Font.Size";
public static Image loadIcon ( String path ) {
Image img = null;
try {
URL url = ClassLoader.getSystemResource(path);
img = (Image) (Toolkit.getDefaultToolkit()).getImage(url);
} catch (Exception e) {
System.out.println("Exception occured: " + e.getMessage() +
" - " + e );
}
return (img);
}
public TextPaneAppender(Layout layout, String name) {
this();
this.layout = layout;
this.name = name;
setTextPane(new JTextPane());
createAttributes();
createIcons();
}
public TextPaneAppender() {
super();
setTextPane(new JTextPane());
createAttributes();
createIcons();
this.label="";
this.sw = new StringWriter();
this.qw = new QuietWriter(sw, errorHandler);
this.tp = new TracerPrintWriter(qw);
this.fancy =true;
}
public
void close() {
}
private void createAttributes() {
Priority prio[] = Priority.getAllPossiblePriorities();
attributes = new Hashtable();
for (int i=0; i<prio.length;i++) {
MutableAttributeSet att = new SimpleAttributeSet();
attributes.put(prio[i], att);
StyleConstants.setFontSize(att,14);
}
StyleConstants.setForeground((MutableAttributeSet)attributes.get(Priority.ERROR),Color.red);
StyleConstants.setForeground((MutableAttributeSet)attributes.get(Priority.WARN),Color.orange);
StyleConstants.setForeground((MutableAttributeSet)attributes.get(Priority.INFO),Color.gray);
StyleConstants.setForeground((MutableAttributeSet)attributes.get(Priority.DEBUG),Color.black);
}
private void createIcons() {
Priority prio[] = Priority.getAllPossiblePriorities();
icons = new Hashtable();
for (int i=0; i<prio.length;i++) {
if (prio[i].equals(Priority.FATAL))
icons.put(prio[i],new ImageIcon(loadIcon("icons/RedFlag.gif")));
if (prio[i].equals(Priority.ERROR))
icons.put(prio[i],new ImageIcon(loadIcon("icons/RedFlag.gif")));
if (prio[i].equals(Priority.WARN))
icons.put(prio[i],new ImageIcon(loadIcon("icons/BlueFlag.gif")));
if (prio[i].equals(Priority.INFO))
icons.put(prio[i],new ImageIcon(loadIcon("icons/GreenFlag.gif")));
if (prio[i].equals(Priority.DEBUG))
icons.put(prio[i],new ImageIcon(loadIcon("icons/GreenFlag.gif")));
}
}
public void append(LoggingEvent event) {
String text = this.layout.format(event);
String trace="";
// Print Stacktrace
// Quick Hack maybe there is a better/faster way?
if (event.throwable!=null) {
event.throwable.printStackTrace(tp);
for (int i=0; i< sw.getBuffer().length(); i++) {
if (sw.getBuffer().charAt(i)=='\t')
sw.getBuffer().replace(i,i+1," ");
}
trace = sw.toString();
sw.getBuffer().delete(0,sw.getBuffer().length());
}
try {
if (fancy) {
textpane.setEditable(true);
textpane.insertIcon((ImageIcon)icons.get(event.priority));
textpane.setEditable(false);
}
doc.insertString(doc.getLength(),text+trace,
(MutableAttributeSet)attributes.get(event.priority));
}
catch (BadLocationException badex) {
System.err.println(badex);
}
textpane.setCaretPosition(doc.getLength());
}
public
JTextPane getTextPane() {
return textpane;
}
private
static
Color parseColor (String v) {
StringTokenizer st = new StringTokenizer(v,",");
int val[] = {255,255,255,255};
int i=0;
while (st.hasMoreTokens()) {
val[i]=Integer.parseInt(st.nextToken());
i++;
}
return new Color(val[0],val[1],val[2],val[3]);
}
private
static
String colorToString(Color c) {
// alpha component emitted only if not default (255)
String res = ""+c.getRed()+","+c.getGreen()+","+c.getBlue();
return c.getAlpha() >= 255 ? res : res + ","+c.getAlpha();
}
public
void setLayout(Layout layout) {
this.layout=layout;
}
public
void setName(String name) {
this.name = name;
}
public
void setTextPane(JTextPane textpane) {
this.textpane=textpane;
textpane.setEditable(false);
textpane.setBackground(Color.lightGray);
this.doc=textpane.getStyledDocument();
}
private
void setColor(Priority p, String v) {
StyleConstants.setForeground(
(MutableAttributeSet)attributes.get(p),parseColor(v));
}
private
String getColor(Priority p) {
Color c = StyleConstants.getForeground(
(MutableAttributeSet)attributes.get(p));
return c == null ? null : colorToString(c);
}
/////////////////////////////////////////////////////////////////////
// option setters and getters
public
void setLabel(String label) {
this.label = label;
}
public
String getLabel() {
return label;
}
public
void setColorEmerg(String color) {
setColor(Priority.FATAL, color);
}
public
String getColorEmerg() {
return getColor(Priority.FATAL);
}
public
void setColorError(String color) {
setColor(Priority.ERROR, color);
}
public
String getColorError() {
return getColor(Priority.ERROR);
}
public
void setColorWarn(String color) {
setColor(Priority.WARN, color);
}
public
String getColorWarn() {
return getColor(Priority.WARN);
}
public
void setColorInfo(String color) {
setColor(Priority.INFO, color);
}
public
String getColorInfo() {
return getColor(Priority.INFO);
}
public
void setColorDebug(String color) {
setColor(Priority.DEBUG, color);
}
public
String getColorDebug() {
return getColor(Priority.DEBUG);
}
public
void setColorBackground(String color) {
textpane.setBackground(parseColor(color));
}
public
String getColorBackground() {
return colorToString(textpane.getBackground());
}
public
void setFancy(boolean fancy) {
this.fancy = fancy;
}
public
boolean getFancy() {
return fancy;
}
public
void setFontSize(int size) {
Enumeration e = attributes.elements();
while (e.hasMoreElements()) {
StyleConstants.setFontSize((MutableAttributeSet)e.nextElement(),size);
}
return;
}
public
int getFontSize() {
AttributeSet attrSet = (AttributeSet) attributes.get(Priority.INFO);
return StyleConstants.getFontSize(attrSet);
}
public
void setFontName(String name) {
Enumeration e = attributes.elements();
while (e.hasMoreElements()) {
StyleConstants.setFontFamily((MutableAttributeSet)e.nextElement(),name);
}
return;
}
public
String getFontName() {
AttributeSet attrSet = (AttributeSet) attributes.get(Priority.INFO);
return StyleConstants.getFontFamily(attrSet);
}
public
boolean requiresLayout() {
return true;
}
} // TextPaneAppender

View File

@ -0,0 +1,111 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.log4j.gui.examples;
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import org.apache.log4j.*;
import org.apache.log4j.gui.*;
public class TextPaneAppenderExample implements ActionListener {
JFrame mainframe;
ButtonGroup priorities;
TextPaneAppender tpa;
Category gui;
Priority prio[];
JTabbedPane logview;
public TextPaneAppenderExample () {
mainframe = new JFrame("Testing the TextPaneAppender...");
mainframe.setSize(300,300);
logview = new JTabbedPane();
createLogger();
createMenuBar();
mainframe.setVisible(true);
mainframe.getContentPane().add(logview);
}
public void createLogger() {
tpa = new TextPaneAppender(new PatternLayout("%-5p %d [%t]: %m%n"),"Debug");
logview.addTab("Events ...",new JScrollPane(tpa.getTextPane()));
gui = Category.getInstance(this.getClass().getName());
gui.addAppender(tpa);
}
public void createMenuBar() {
JMenu file = new JMenu("File");
JMenuItem exit = new JMenuItem("Exit");
exit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
file.add(exit);
JMenuBar mb = new JMenuBar();
mb.add(file);
JMenu logevent = new JMenu("LoggingEvents");
JMenu selectprio = new JMenu("Priority");
prio = Priority.getAllPossiblePriorities();
JRadioButtonMenuItem priority[]= new JRadioButtonMenuItem[prio.length];
priorities = new ButtonGroup();
for (int i=0; i<prio.length;i++) {
if (i==0)
priority[i] = new JRadioButtonMenuItem(prio[i].toString(),true);
else
priority[i] = new JRadioButtonMenuItem(prio[i].toString());
priority[i].setActionCommand(prio[i].toString());
selectprio.add(priority[i]);
priorities.add(priority[i]);
}
logevent.add(selectprio);
JMenuItem lognow = new JMenuItem("LogIt!");
lognow.addActionListener(this);
logevent.add(lognow);
mb.add(logevent);
mainframe.setJMenuBar(mb);
}
public void actionPerformed(ActionEvent ae){
String logtext = JOptionPane.showInputDialog("Text to log");
if (logtext == null) logtext="NO Input";
int i=0;
String name = priorities.getSelection().getActionCommand();
while (!prio[i].toString().equals(name))
i=i+1;
gui.log(prio[i],logtext);
}
static public void main(String args[]) {
TextPaneAppenderExample tpex = new TextPaneAppenderExample();
}
}

View File

@ -0,0 +1,601 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.klopotek.utils.log;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import org.apache.log4j.helpers.*;
import org.apache.log4j.spi.*;
/**
The JDBCAppender, writes messages into a database
<p><b>The JDBCAppender is configurable at runtime by setting options in two alternatives : </b></p>
<dir>
<p><b>1. Use a configuration-file</b></p>
<p>Define the options in a file (<A HREF="configfile_example.txt">example</A>) and call a <code>PropertyConfigurator.configure(filename)</code> in your code.</p>
<p><b>2. Use the methods of JDBCAppender to do it</b></p>
<p>Call <code>JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)</code> to do it analogically without a configuration-file (<A HREF="code_example2.java">example</A>)</p>
</dir>
<p>All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.</p>
<p><b>Here is a description of all available options :</b></p>
<dir>
<p><b>1. Database-options to connect to the database</b></p>
<p>- <b>URL_OPTION</b> : a database url of the form jdbc:subprotocol:subname</p>
<p>- <b>USERNAME_OPTION</b> : the database user on whose behalf the connection is being made</p>
<p>- <b>PASSWORD_OPTION</b> : the user's password</p>
<p><b>2. Connector-option to specify your own JDBCConnectionHandler</b></p>
<p>- <b>CONNECTOR_OPTION</b> : a classname which is implementing the JDBCConnectionHandler-interface</p>
<p>This interface is used to get a customized connection.</p>
<p>If in addition the database-options are given, these options will be used as arguments for the JDBCConnectionHandler-interface to get a connection.</p>
<p>Else if no database-options are given, the JDBCConnectionHandler-interface is called without them.</p>
<p>Else if this option is not defined, the database-options are required to open a connection by the JDBCAppender.</p>
<p><b>3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event</b></p>
<p>- <b>SQL_OPTION</b> : a sql-statement which will be used to write to the database</p>
<p>Use the variable <b>@MSG@</b> on a location in the statement, which has to be dynamically replaced by the message-text.</p>
<p>If you give this option, the table-option and columns-option will be ignored !</p>
<p><b>4. Table-option to specify a table contained by the database</b></p>
<p>- <b>TABLE_OPTION</b> : the table in which the logging will be done</p>
<p><b>5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)</b></p>
<p>- <b>COLUMNS_OPTION</b> : a formatted list of column-descriptions</p>
<p>Each column description consists of</p>
<dir>
<p>- the <b><i>name</i></b> of the column (required)</p>
<p>- a <b><i>logtype</i></b> which is a static constant of class LogType (required)</p>
<p>- and a <b><i>value</i></b> which depends by the LogType (optional/required, depending by logtype)</p>
</dir>
<p>Here is a description of the available logtypes of class <b>{@link LogType}</b> and how to handle the <b><i>value</i></b>:</p>
<dir>
<p>o <b>MSG</b> = a value will be ignored, the column will get the message. (One columns need to be of this type!)</p>
<p>o <b>STATIC</b> = the value will be filled into the column with every logged message. (Ensure that the type of value can be casted into the sql-type of the column!)</p>
<p>o <b>ID</b> = value must be a classname, which implements the JDBCIDHandler-interface.</p>
<p>o <b>TIMESTAMP</b> = a value will be ignored, the column will be filled with a actually timestamp with every logged message.</p>
<p>o <b>EMPTY</b> = a value will be ignored, the column will be ignored when writing to the database (Ensure to fill not nullable columns by a database trigger!)</p>
</dir>
<p>If there are more than one column to describe, the columns must be separated by a Tabulator-delimiter (unicode0008) !</p>
<p>The arguments of a column-description must be separated by the delimiter '~' !</p>
<p><i>(Example : name1~logtype1~value1 name2~logtype2~value2...)</i></p>
<p><b>6. Layout-options to define the layout of the messages (optional)</b></p>
<p>- <b>_</b> : the layout wont be set by a xxx_OPTION</p>
<p>See the configuration-file and code examples below...</p>
<p>The default is a layout of the class {@link org.apache.log4j.PatternLayout} with the pattern=%m which representate only the message.</p>
<p><b>7. Buffer-option to define the size of the message-event-buffer (optional)</b></p>
<p>- <b>BUFFER_OPTION</b> : define how many messages will be buffered until they will be updated to the database.</p>
<p>The default is buffer=1, which will do a update with every happened message-event.</p>
<p><b>8. Commit-option to define a auto-commitment</b></p>
<p>- <b>COMMIT_OPTION</b> : define whether updated messages should be committed to the database (Y) or not (N).</p>
<p>The default is commit=Y.</p>
</dir>
<p><b>The sequence of some options is important :</b></p>
<dir>
<p><b>1. Connector-option OR/AND Database-options</b></p>
<p>Any database connection is required !</p>
<p><b>2. (Table-option AND Columns-option) OR SQL-option</b></p>
<p>Anything of that is required ! Whether where to write something OR what to write somewhere...;-)</p>
<p><b>3. All other options can be set at any time...</b></p>
<p>The other options are optional and have a default initialization, which can be customized.</p>
</dir>
<p><b>Here is a <b>configuration-file example</b>, which can be used as argument for the <b>PropertyConfigurator</b> : </b><A HREF="configfile_example.txt"> configfile_example.txt</A></p>
<p><b>Here is a <b>code-example</b> to configure the JDBCAppender <b>with a configuration-file</b> : </b><A HREF="code_example1.java"> code_example1.java</A></p>
<p><b>Here is a <b>another code-example</b> to configure the JDBCAppender <b>without a configuration-file</b> : </b><A HREF="code_example2.java"> code_example2.java</A></p>
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public class JDBCAppender extends AppenderSkeleton
{
/**
A database-option to to set a database url of the form jdbc:subprotocol:subname.
*/
public static final String URL_OPTION = "url";
/**
A database-option to set the database user on whose behalf the connection is being made.
*/
public static final String USERNAME_OPTION = "username";
/**
A database-option to set the user's password.
*/
public static final String PASSWORD_OPTION = "password";
/**
A table-option to specify a table contained by the database
*/
public static final String TABLE_OPTION = "table";
/**
A connector-option to specify your own JDBCConnectionHandler
*/
public static final String CONNECTOR_OPTION = "connector";
/**
A columns-option to describe the important columns of the table
*/
public static final String COLUMNS_OPTION = "columns";
/**
A sql-option to specify a static sql-statement which will be performed with every occuring message-event
*/
public static final String SQL_OPTION = "sql";
/**
A buffer-option to define the size of the message-event-buffer
*/
public static final String BUFFER_OPTION = "buffer";
/**
A commit-option to define a auto-commitment
*/
public static final String COMMIT_OPTION = "commit";
//Variables to store the options values setted by setOption() :
private String url = null;
private String username = null;
private String password = null;
private String table = null;
private String connection_class = null;
private String sql = null;
private boolean docommit = true;
private int buffer_size = 1;
private JDBCConnectionHandler connectionHandler = null;
//This buffer stores message-events.
//When the buffer_size is reached, the buffer will be flushed and the messages will updated to the database.
private ArrayList buffer = new ArrayList();
//Database-connection
private Connection con = null;
//This class encapsulate the logic which is necessary to log into a table
private JDBCLogger jlogger = new JDBCLogger();
//Flags :
//A flag to indicate a established database connection
private boolean connected = false;
//A flag to indicate configuration status
private boolean configured = false;
//A flag to indicate that everything is ready to get append()-commands.
private boolean ready = false;
/**
If program terminates close the database-connection and flush the buffer
*/
public void finalize()
{
close();
super.finalize();
}
/**
Internal method. Returns a array of strings containing the available options which can be set with method setOption()
*/
public String[] getOptionStrings()
{
// The sequence of options in this string is important, because setOption() is called this way ...
return new String[]{CONNECTOR_OPTION, URL_OPTION, USERNAME_OPTION, PASSWORD_OPTION, SQL_OPTION, TABLE_OPTION, COLUMNS_OPTION, BUFFER_OPTION, COMMIT_OPTION};
}
/**
Sets all necessary options
*/
public void setOption(String _option, String _value)
{
_option = _option.trim();
_value = _value.trim();
if(_option == null || _value == null) return;
if(_option.length() == 0 || _value.length() == 0) return;
_value = _value.trim();
if(_option.equals(CONNECTOR_OPTION))
{
if(!connected) connection_class = _value;
}
else if(_option.equals(URL_OPTION))
{
if(!connected) url = _value;
}
else if(_option.equals(USERNAME_OPTION))
{
if(!connected) username = _value;
}
else if(_option.equals(PASSWORD_OPTION))
{
if(!connected) password = _value;
}
else if(_option.equals(SQL_OPTION))
{
sql = _value;
}
else if(_option.equals(TABLE_OPTION))
{
if(sql != null) return;
table = _value;
}
else if(_option.equals(COLUMNS_OPTION))
{
if(sql != null) return;
String name = null;
int logtype = -1;
String value = null;
String column = null;
String arg = null;
int num_args = 0;
int num_columns = 0;
StringTokenizer st_col;
StringTokenizer st_arg;
//Columns are TAB-separated
st_col = new StringTokenizer(_value, " ");
num_columns = st_col.countTokens();
if(num_columns < 1)
{
errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
return;
}
for(int i=1; i<=num_columns; i++)
{
column = st_col.nextToken();
//Arguments are ~-separated
st_arg = new StringTokenizer(column, "~");
num_args = st_arg.countTokens();
if(num_args < 2)
{
errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION value : " + _value + " !");
return;
}
for(int j=1; j<=num_args; j++)
{
arg = st_arg.nextToken();
if(j == 1) name = arg;
else if(j == 2)
{
try
{
logtype = Integer.parseInt(arg);
}
catch(Exception e)
{
logtype = LogType.parseLogType(arg);
}
if(!LogType.isLogType(logtype))
{
errorHandler.error("JDBCAppender::setOption(), Invalid COLUMN_OPTION LogType : " + arg + " !");
return;
}
}
else if(j == 3) value = arg;
}
if(!setLogType(name, logtype, value)) return;
}
}
else if(_option.equals(BUFFER_OPTION))
{
try
{
buffer_size = Integer.parseInt(_value);
}
catch(Exception e)
{
errorHandler.error("JDBCAppender::setOption(), Invalid BUFFER_OPTION value : " + _value + " !");
return;
}
}
else if(_option.equals(COMMIT_OPTION))
{
docommit = _value.equals("Y");
}
if(_option.equals(SQL_OPTION) || _option.equals(TABLE_OPTION))
{
if(!configured) configure();
}
}
/**
Internal method. Returns true, you may define your own layout...
*/
public boolean requiresLayout()
{
return true;
}
/**
Internal method. Close the database connection & flush the buffer.
*/
public void close()
{
flush_buffer();
if(connection_class == null)
{
try{con.close();}catch(Exception e){errorHandler.error("JDBCAppender::close(), " + e);}
}
this.closed = true;
}
/**
You have to call this function for all provided columns of your log-table !
*/
public boolean setLogType(String _name, int _logtype, Object _value)
{
if(sql != null) return true;
if(!configured)
{
if(!configure()) return false;
}
try
{
jlogger.setLogType(_name, _logtype, _value);
}
catch(Exception e)
{
errorHandler.error("JDBCAppender::setLogType(), " + e);
return false;
}
return true;
}
/**
Internal method. Appends the message to the database table.
*/
public void append(LoggingEvent event)
{
if(!ready)
{
if(!ready())
{
errorHandler.error("JDBCAppender::append(), Not ready to append !");
return;
}
}
buffer.add(event);
if(buffer.size() >= buffer_size) flush_buffer();
}
/**
Internal method. Flushes the buffer.
*/
public void flush_buffer()
{
try
{
int size = buffer.size();
if(size < 1) return;
for(int i=0; i<size; i++)
{
LoggingEvent event = (LoggingEvent)buffer.get(i);
//Insert message into database
jlogger.append(layout.format(event));
}
buffer.clear();
if(docommit) con.commit();
}
catch(Exception e)
{
errorHandler.error("JDBCAppender::flush_buffer(), " + e + " : " + jlogger.getErrorMsg());
try{con.rollback();} catch(Exception ex){}
return;
}
}
/**
Internal method. Returns true, when the JDBCAppender is ready to append messages to the database, else false.
*/
public boolean ready()
{
if(ready) return true;
if(!configured) return false;
ready = jlogger.ready();
if(!ready){errorHandler.error(jlogger.getErrorMsg());}
return ready;
}
/**
Internal method. Connect to the database.
*/
protected void connect() throws Exception
{
if(connected) return;
try
{
if(connection_class == null)
{
if(url == null) throw new Exception("JDBCAppender::connect(), No URL defined.");
if(username == null) throw new Exception("JDBCAppender::connect(), No USERNAME defined.");
if(password == null) throw new Exception("JDBCAppender::connect(), No PASSWORD defined.");
connectionHandler = new DefaultConnectionHandler();
}
else
{
connectionHandler = (JDBCConnectionHandler)(Class.forName(connection_class).newInstance());
}
if(url != null && username != null && password != null)
{
con = connectionHandler.getConnection(url, username, password);
}
else
{
con = connectionHandler.getConnection();
}
if(con.isClosed())
{
throw new Exception("JDBCAppender::connect(), JDBCConnectionHandler returns no connected Connection !");
}
}
catch(Exception e)
{
throw new Exception("JDBCAppender::connect(), " + e);
}
connected = true;
}
/**
Internal method. Configures for appending...
*/
protected boolean configure()
{
if(configured) return true;
if(!connected)
{
if((connection_class == null) && (url == null || username == null || password == null))
{
errorHandler.error("JDBCAppender::configure(), Missing database-options or connector-option !");
return false;
}
try
{
connect();
}
catch(Exception e)
{
connection_class = null;
url = null;
errorHandler.error("JDBCAppender::configure(), " + e);
return false;
}
}
if(sql == null && table == null)
{
errorHandler.error("JDBCAppender::configure(), No SQL_OPTION or TABLE_OPTION given !");
return false;
}
if(!jlogger.isConfigured())
{
try
{
jlogger.setConnection(con);
if(sql == null)
{
jlogger.configureTable(table);
}
else jlogger.configureSQL(sql);
}
catch(Exception e)
{
errorHandler.error("JDBCAppender::configure(), " + e);
return false;
}
}
//Default Message-Layout
if(layout == null)
{
layout = new PatternLayout("%m");
}
configured = true;
return true;
}
}
/**
This is a default JDBCConnectionHandler used by JDBCAppender
*/
class DefaultConnectionHandler implements JDBCConnectionHandler
{
Connection con = null;
public Connection getConnection()
{
return con;
}
public Connection getConnection(String _url, String _username, String _password)
{
try
{
if(con != null && !con.isClosed()) con.close();
con = DriverManager.getConnection(_url, _username, _password);
con.setAutoCommit(false);
}
catch(Exception e){}
return con;
}
}

View File

@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.klopotek.utils.log;
import java.sql.*;
/**
This interface has to be implemented for your own database-connection-handling and its used in class JDBCLogger.
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public interface JDBCConnectionHandler
{
/**Get a connection*/
Connection getConnection();
/**Get a defined connection*/
Connection getConnection(String _url, String _username, String _password);
}

View File

@ -0,0 +1,35 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.klopotek.utils.log;
/**
This interface has to be implemented to provide ID-columns with unique IDs and its used in class JDBCLogger.
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public interface JDBCIDHandler
{
/**Get a unique ID*/
Object getID();
}

View File

@ -0,0 +1,468 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.klopotek.utils.log;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import org.apache.log4j.helpers.*;
import org.apache.log4j.spi.*;
/**
This class encapsulate the logic which is necessary to log into a table.
Used by JDBCAppender
<p><b>Author : </b><A HREF="mailto:t.fenner@klopotek.de">Thomas Fenner</A></p>
@since 1.0
*/
public class JDBCLogger
{
//All columns of the log-table
private ArrayList logcols = null;
//Only columns which will be provided by logging
private String column_list = null;
//Number of all columns
private int num = 0;
//Status for successful execution of method configure()
private boolean isconfigured = false;
//Status for ready to do logging with method append()
private boolean ready = false;
//This message will be filled with a error-string when method ready() failes, and can be got by calling getMsg()
private String errormsg = "";
private Connection con = null;
private Statement stmt = null;
private ResultSet rs = null;
private String table = null;
//Variables for static SQL-statement logging
private String sql = null;
private String new_sql = null;
private String new_sql_part1 = null;
private String new_sql_part2 = null;
private static final String msg_wildcard = "@MSG@";
private int msg_wildcard_pos = 0;
/**
Writes a message into the database table.
Throws an exception, if an database-error occurs !
*/
public void append(String _msg) throws Exception
{
if(!ready) if(!ready()) throw new Exception("JDBCLogger::append(), Not ready to append !");
if(sql != null)
{
appendSQL(_msg);
return;
}
LogColumn logcol;
rs.moveToInsertRow();
for(int i=0; i<num; i++)
{
logcol = (LogColumn)logcols.get(i);
if(logcol.logtype == LogType.MSG)
{
rs.updateObject(logcol.name, _msg);
}
else if(logcol.logtype == LogType.ID)
{
rs.updateObject(logcol.name, logcol.idhandler.getID());
}
else if(logcol.logtype == LogType.STATIC)
{
rs.updateObject(logcol.name, logcol.value);
}
else if(logcol.logtype == LogType.TIMESTAMP)
{
rs.updateObject(logcol.name, new Timestamp((new java.util.Date()).getTime()));
}
}
rs.insertRow();
}
/**
Writes a message into the database using a given sql-statement.
Throws an exception, if an database-error occurs !
*/
public void appendSQL(String _msg) throws Exception
{
if(!ready) if(!ready()) throw new Exception("JDBCLogger::appendSQL(), Not ready to append !");
if(sql == null) throw new Exception("JDBCLogger::appendSQL(), No SQL-Statement configured !");
if(msg_wildcard_pos > 0)
{
new_sql = new_sql_part1 + _msg + new_sql_part2;
}
else new_sql = sql;
try
{
stmt.executeUpdate(new_sql);
}
catch(Exception e)
{
errormsg = new_sql;
throw e;
}
}
/**
Configures this class, by reading in the structure of the log-table
Throws an exception, if an database-error occurs !
*/
public void configureTable(String _table) throws Exception
{
if(isconfigured) return;
//Fill logcols with META-informations of the table-columns
stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs = stmt.executeQuery("SELECT * FROM " + _table + " WHERE 1 = 2");
LogColumn logcol;
ResultSetMetaData rsmd = rs.getMetaData();
num = rsmd.getColumnCount();
logcols = new ArrayList(num);
for(int i=1; i<=num; i++)
{
logcol = new LogColumn();
logcol.name = rsmd.getColumnName(i).toUpperCase();
logcol.type = rsmd.getColumnTypeName(i);
logcol.nullable = (rsmd.isNullable(i) == rsmd.columnNullable);
logcol.isWritable = rsmd.isWritable(i);
if(!logcol.isWritable) logcol.ignore = true;
logcols.add(logcol);
}
table = _table;
isconfigured = true;
}
/**
Configures this class, by storing and parsing the given sql-statement.
Throws an exception, if somethings wrong !
*/
public void configureSQL(String _sql) throws Exception
{
if(isconfigured) return;
if(!isConnected()) throw new Exception("JDBCLogger::configureSQL(), Not connected to database !");
if(_sql == null || _sql.trim().equals("")) throw new Exception("JDBCLogger::configureSQL(), Invalid SQL-Statement !");
sql = _sql.trim();
stmt = con.createStatement();
msg_wildcard_pos = sql.indexOf(msg_wildcard);
if(msg_wildcard_pos > 0)
{
new_sql_part1 = sql.substring(0, msg_wildcard_pos-1) + "'";
//between the message...
new_sql_part2 = "'" + sql.substring(msg_wildcard_pos+msg_wildcard.length());
}
isconfigured = true;
}
/**
Sets a connection. Throws an exception, if the connection is not open !
*/
public void setConnection(Connection _con) throws Exception
{
con = _con;
if(!isConnected()) throw new Exception("JDBCLogger::setConnection(), Given connection isnt connected to database !");
}
/**
Sets a columns logtype (LogTypes) and value, which depends on that logtype.
Throws an exception, if the given arguments arent correct !
*/
public void setLogType(String _name, int _logtype, Object _value) throws Exception
{
if(!isconfigured) throw new Exception("JDBCLogger::setLogType(), Not configured !");
//setLogType() makes only sense for further configuration of configureTable()
if(sql != null) return;
_name = _name.toUpperCase();
if(_name == null || !(_name.trim().length() > 0)) throw new Exception("JDBCLogger::setLogType(), Missing argument name !");
if(!LogType.isLogType(_logtype)) throw new Exception("JDBCLogger::setLogType(), Invalid logtype '" + _logtype + "' !");
if((_logtype != LogType.MSG && _logtype != LogType.EMPTY) && _value == null) throw new Exception("JDBCLogger::setLogType(), Missing argument value !");
LogColumn logcol;
for(int i=0; i<num; i++)
{
logcol = (LogColumn)logcols.get(i);
if(logcol.name.equals(_name))
{
if(!logcol.isWritable) throw new Exception("JDBCLogger::setLogType(), Column " + _name + " is not writeable !");
//Column gets the message
if(_logtype == LogType.MSG)
{
logcol.logtype = _logtype;
return;
}
//Column will be provided by JDBCIDHandler::getID()
else if(_logtype == LogType.ID)
{
logcol.logtype = _logtype;
try
{
//Try to cast directly Object to JDBCIDHandler
logcol.idhandler = (JDBCIDHandler)_value;
}
catch(Exception e)
{
try
{
//Assuming _value is of class string which contains the classname of a JDBCIDHandler
logcol.idhandler = (JDBCIDHandler)(Class.forName((String)_value).newInstance());
}
catch(Exception e2)
{
throw new Exception("JDBCLogger::setLogType(), Cannot cast value of class " + _value.getClass() + " to class JDBCIDHandler !");
}
}
return;
}
//Column will be statically defined with Object _value
else if(_logtype == LogType.STATIC)
{
logcol.logtype = _logtype;
logcol.value = _value;
return;
}
//Column will be provided with a actually timestamp
else if(_logtype == LogType.TIMESTAMP)
{
logcol.logtype = _logtype;
return;
}
//Column will be fully ignored during process.
//If this column is not nullable, the column has to be filled by a database trigger,
//else a database error occurs !
//Columns which are not nullable, but should be not filled, must be explicit assigned with LogType.EMPTY,
//else a value is required !
else if(_logtype == LogType.EMPTY)
{
logcol.logtype = _logtype;
logcol.ignore = true;
return;
}
}
}
}
/**
Return true, if this class is ready to append(), else false.
When not ready, a reason-String is stored in the instance-variable msg.
*/
public boolean ready()
{
if(ready) return true;
if(!isconfigured){ errormsg = "Not ready to append ! Call configure() first !"; return false;}
//No need to doing the whole rest...
if(sql != null)
{
ready = true;
return true;
}
boolean msgcol_defined = false;
LogColumn logcol;
for(int i=0; i<num; i++)
{
logcol = (LogColumn)logcols.get(i);
if(logcol.ignore || !logcol.isWritable) continue;
if(!logcol.nullable && logcol.logtype == LogType.EMPTY)
{
errormsg = "Not ready to append ! Column " + logcol.name + " is not nullable, and must be specified by setLogType() !";
return false;
}
if(logcol.logtype == LogType.ID && logcol.idhandler == null)
{
errormsg = "Not ready to append ! Column " + logcol.name + " is specified as an ID-column, and a JDBCIDHandler has to be set !";
return false;
}
else if(logcol.logtype == LogType.STATIC && logcol.value == null)
{
errormsg = "Not ready to append ! Column " + logcol.name + " is specified as a static field, and a value has to be set !";
return false;
}
else if(logcol.logtype == LogType.MSG) msgcol_defined = true;
}
if(!msgcol_defined) return false;
//create the column_list
for(int i=0; i<num; i++)
{
logcol = (LogColumn)logcols.get(i);
if(logcol.ignore || !logcol.isWritable) continue;
if(logcol.logtype != LogType.EMPTY)
{
if(column_list == null)
{
column_list = logcol.name;
}
else column_list += ", " + logcol.name;
}
}
try
{
rs = stmt.executeQuery("SELECT " + column_list + " FROM " + table + " WHERE 1 = 2");
}
catch(Exception e)
{
errormsg = "Not ready to append ! Cannot select columns '" + column_list + "' of table " + table + " !";
return false;
}
ready = true;
return true;
}
/**
Return true, if this class is configured, else false.
*/
public boolean isConfigured(){ return isconfigured;}
/**
Return true, if this connection is open, else false.
*/
public boolean isConnected()
{
try
{
return (con != null && !con.isClosed());
}
catch(Exception e){return false;}
}
/**
Return the internal error message stored in instance variable msg.
*/
public String getErrorMsg(){String r = new String(errormsg); errormsg = null; return r;}
}
/**
This class encapsulate all by class JDBCLogger needed data around a column
*/
class LogColumn
{
//column name
String name = null;
//column type
String type = null;
//not nullability means that this column is mandatory
boolean nullable = false;
//isWritable means that the column can be updated, else column is only readable
boolean isWritable = false;
//if ignore is true, this column will be ignored by building sql-statements.
boolean ignore = false;
//Must be filled for not nullable columns ! In other case it is optional.
int logtype = LogType.EMPTY;
Object value = null; //Generic storage for typewrapper-classes Long, String, etc...
JDBCIDHandler idhandler = null;
}
/**
This class contains all constants which are necessary to define a columns log-type.
*/
class LogType
{
//A column of this type will receive the message.
public static final int MSG = 1;
//A column of this type will be a unique identifier of the logged row.
public static final int ID = 2;
//A column of this type will contain a static, one-time-defined value.
public static final int STATIC = 3;
//A column of this type will be filled with an actual timestamp depending by the time the logging will be done.
public static final int TIMESTAMP = 4;
//A column of this type will contain no value and will not be included in logging insert-statement.
//This could be a column which will be filled not by creation but otherwhere...
public static final int EMPTY = 5;
public static boolean isLogType(int _lt)
{
if(_lt == MSG || _lt == STATIC || _lt == ID || _lt == TIMESTAMP || _lt == EMPTY) return true;
return false;
}
public static int parseLogType(String _lt)
{
if(_lt.equals("MSG")) return MSG;
if(_lt.equals("ID")) return ID;
if(_lt.equals("STATIC")) return STATIC;
if(_lt.equals("TIMESTAMP")) return TIMESTAMP;
if(_lt.equals("EMPTY")) return EMPTY;
return -1;
}
}

View File

@ -0,0 +1,271 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
// Class JDBCAppender, writes messages into a database
// The JDBCAppender is configurable at runtime in two alternatives :
// 1. Configuration-file
// Define the options in a file and call a PropertyConfigurator.configure(file)-method.
// 2. method JDBCAppender::setOption(JDBCAppender.xxx_OPTION, String value)
// The sequence of some options is important :
// 1. Connector-option OR/AND Database-options
// Any database connection is required !
// 2. (Table-option AND Columns-option) OR SQL-option
// Any statement is required !
// 3. All other options can be set at any time...
// The other options are optional and have a default initialization, which can be custumized.
// All available options are defined as static String-constants in JDBCAppender named xxx_OPTION.
// Here is a description of all available options :
// 1. Database-options to connect to the database
// - URL_OPTION : a database url of the form jdbc:subprotocol:subname
// - USERNAME_OPTION : the database user on whose behalf the connection is being made
// - PASSWORD_OPTION : the user's password
//
// 2. Connector-option to specify your own JDBCConnectionHandler
// - CONNECTOR_OPTION : a classname which is implementing the JDBCConnectionHandler-interface
// This interface is used to get a customized connection.
// If in addition the database-options are given, these options will be used
// for invocation of the JDBCConnectionHandler-interface to get a connection.
// Else if no database-options are given, the JDBCConnectionHandler-interface is called without these options.
//
// 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
// - SQL_OPTION : a sql-statement which will be used to write to the database
// If you give this option, the table-option and columns-option will be ignored !
// Use the variable @MSG@ on that location in the statement, which has to be dynamically replaced by the message.
//
// 4. Table-option to specify one table contained by the database
// - TABLE_OPTION : the table in which the logging will be done
//
// 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
// - COLUMNS_OPTION : a formatted list of column-descriptions
// Each column description consists of
// - the name of the column (required)
// - a logtype which is a static constant of class LogType (required)
// - and a value which depends by the LogType (optional/required, depending by logtype)
// Here is a description of the available logtypes of class LogType :
// o MSG = a value will be ignored, the column will get the message. (One columns need to be of this type!)
// o STATIC = the value will be filled into the column with every logged message. (Ensure that the type of value can be casted into the sql-type of the column!)
// o ID = value must be a classname, which implements the JDBCIDHandler-interface.
// o TIMESTAMP = a value will be ignored, the column will be filled with a actually timestamp with every logged message.
// o EMPTY = a value will be ignored, the column will be ignored when writing to the database (Ensure to fill not nullable columns by a database trigger!)
// If there are more than one column to describe, the columns must be separated by a TAB-delimiter (' ') !
// The arguments of a column-description must be separated by the delimiter '~' !
// (Example : name1~logtype1~value1 name2~logtype2~value2...)
//
// 6. Layout-options to define the layout of the messages (optional)
// - the layout wont be set by a xxx_OPTION
// Configuration-file : see at the following configuration-file example
// JDBCAppender::setOption() : see at the following code example
// The default is a layout of class org.apache.log4j.PatternLayout with ConversionPattern=%m
//
// 7. Buffer-option to define the size of the message-event-buffer (optional)
// - BUFFER_OPTION : define how many messages will be buffered until they will be updated to the database.
// The default is a update on every message (buffer=1=no buffer).
//
// 8. Commit-option to define a auto-commitment
// - COMMIT_OPTION : define whether updated messages should be committed to the database (Y) or not (N).
// The default is a commit on every buffer-flush.
// Here is a Configuration-file example, which can be used with the PropertyConfigurator :
//
// Declare a appender variable named JDBC
log4j.rootCategory=JDBC
// JDBC is a class of JDBCAppender, which writes messages into a database
log4j.appender.JDBC=JDBCAppender
// 1. Database-options to connect to the database
log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
log4j.appender.JDBC.username=mex_pr_dev60
log4j.appender.JDBC.password=mex_pr_dev60
// 2. Connector-option to specify your own JDBCConnectionHandler
log4j.appender.JDBC.connector=MyConnectionHandler
// 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
// 4. Table-option to specify one table contained by the database
log4j.appender.JDBC.table=logtest
// 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
log4j.appender.JDBC.columns=id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~Thomas Fenner (t.fenner@klopotek.de)
// 6. Layout-options to define the layout of the messages (optional)
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.layout.ConversionPattern=%m
// 7. Buffer-option to define the size of the message-event-buffer (optional)
log4j.appender.JDBC.buffer=1
// 8. Commit-option to define a auto-commitment
log4j.appender.JDBC.commit=Y
*/
// Here is a code example to configure the JDBCAppender with a configuration-file :
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.INFO);
// Configuration with configuration-file
PropertyConfigurator.configure("log4jtestprops.txt");
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug"); //this not, because Priority DEBUG is less than INFO
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
// Here is a code example to configure the JDBCAppender without a configuration-file :
/*
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// A JDBCIDHandler
MyIDHandler idhandler = new MyIDHandler();
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.DEBUG);
// Create a new instance of JDBCAppender
JDBCAppender ja = new JDBCAppender();
// Set options with method setOption()
ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
// There are two ways to setup the column-descriptions :
// 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
ja.setLogType("id_seq", LogType.EMPTY, "");
ja.setLogType("id", LogType.ID, idhandler);
ja.setLogType("msg", LogType.MSG, "");
ja.setLogType("created_on", LogType.TIMESTAMP, "");
ja.setLogType("created_by", LogType.STATIC, "FEN");
// If you just want to perform a static sql-statement, forget about the table- and columns-options,
// and use this one :
//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
// other options
//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
// Define a layout
//ja.setLayout(new PatternLayout("%m"));
// Add the appender to a category
cat.addAppender(ja);
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug");
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
*/
// Implement a sample JDBCConnectionHandler
class MyConnectionHandler implements JDBCConnectionHandler
{
Connection con = null;
//Default connection
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
String username = "mex_pr_dev60";
String password = "mex_pr_dev60";
public Connection getConnection()
{
return getConnection(url, username, password);
}
public Connection getConnection(String _url, String _username, String _password)
{
try
{
if(con != null && !con.isClosed()) con.close();
con = DriverManager.getConnection(_url, _username, _password);
con.setAutoCommit(false);
}
catch(Exception e){}
return con;
}
}
// Implement a sample JDBCIDHandler
class MyIDHandler implements JDBCIDHandler
{
private static long id = 0;
public synchronized Object getID()
{
return new Long(++id);
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
// Here is a code example to configure the JDBCAppender with a configuration-file
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.INFO);
// Configuration with configuration-file
PropertyConfigurator.configure("log4jtestprops.txt");
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug"); //this not, because Priority DEBUG is less than INFO
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}

View File

@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
// Here is a code example to configure the JDBCAppender without a configuration-file
import org.apache.log4j.*;
import java.sql.*;
import java.lang.*;
import java.util.*;
public class Log4JTest
{
// Create a category instance for this class
static Category cat = Category.getInstance(Log4JTest.class.getName());
public static void main(String[] args)
{
// A JDBCIDHandler
MyIDHandler idhandler = new MyIDHandler();
// Ensure to have all necessary drivers installed !
try
{
Driver d = (Driver)(Class.forName("oracle.jdbc.driver.OracleDriver").newInstance());
DriverManager.registerDriver(d);
}
catch(Exception e){}
// Set the priority which messages have to be logged
cat.setPriority(Priority.DEBUG);
// Create a new instance of JDBCAppender
JDBCAppender ja = new JDBCAppender();
// Set options with method setOption()
ja.setOption(JDBCAppender.CONNECTOR_OPTION, "MyConnectionHandler");
ja.setOption(JDBCAppender.URL_OPTION, "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))");
ja.setOption(JDBCAppender.USERNAME_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.PASSWORD_OPTION, "mex_pr_dev60");
ja.setOption(JDBCAppender.TABLE_OPTION, "logtest");
// There are two ways to setup the column-descriptions :
// 1. Use the the method setOption(JDBCAppender.COLUMNS_OPTION, column-description)
//ja.setOption(JDBCAppender.COLUMNS_OPTION, "id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~:-) Thomas Fenner (t.fenner@klopotek.de)");
// 2. Use the better way of coding with method setLogType(String columnname, int LogType.xxx, Object xxx)
ja.setLogType("id_seq", LogType.EMPTY, "");
ja.setLogType("id", LogType.ID, idhandler);
ja.setLogType("msg", LogType.MSG, "");
ja.setLogType("created_on", LogType.TIMESTAMP, "");
ja.setLogType("created_by", LogType.STATIC, "FEN");
// If you just want to perform a static sql-statement, forget about the table- and columns-options,
// and use this one :
//ja.setOption(JDBCAppender.SQL_OPTION, "INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')");
// other options
//ja.setOption(JDBCAppender.BUFFER_OPTION, "1");
//ja.setOption(JDBCAppender.COMMIT_OPTION, "Y");
// Define a layout
//ja.setLayout(new PatternLayout("%m"));
// Add the appender to a category
cat.addAppender(ja);
// These messages with Priority >= setted priority will be logged to the database.
cat.debug("debug");
cat.info("info");
cat.error("error");
cat.fatal("fatal");
}
}
// Implement a sample JDBCConnectionHandler
class MyConnectionHandler implements JDBCConnectionHandler
{
Connection con = null;
//Default connection
String url = "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))";
String username = "mex_pr_dev60";
String password = "mex_pr_dev60";
public Connection getConnection()
{
return getConnection(url, username, password);
}
public Connection getConnection(String _url, String _username, String _password)
{
try
{
if(con != null && !con.isClosed()) con.close();
con = DriverManager.getConnection(_url, _username, _password);
con.setAutoCommit(false);
}
catch(Exception e){}
return con;
}
}
// Implement a sample JDBCIDHandler
class MyIDHandler implements JDBCIDHandler
{
private static long id = 0;
public synchronized Object getID()
{
return new Long(++id);
}
}

View File

@ -0,0 +1,52 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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.
# Here is a Configuration-file example, which can be used with the PropertyConfigurator :
# Declare a appender variable named JDBC
log4j.rootCategory=JDBC
# JDBC is a class of JDBCAppender, which writes messages into a database
log4j.appender.JDBC=JDBCAppender
# 1. Database-options to connect to the database
log4j.appender.JDBC.url=jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1521))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=LENZI)(Port=1526)))(CONNECT_DATA=(SID=LENZI)))
log4j.appender.JDBC.username=mex_pr_dev60
log4j.appender.JDBC.password=mex_pr_dev60
# 2. Connector-option to specify your own JDBCConnectionHandler
#log4j.appender.JDBC.connector=MyConnectionHandler
# 3. SQL-option to specify a static sql-statement which will be performed with every occuring message-event
#log4j.appender.JDBC.sql=INSERT INTO LOGTEST (id, msg, created_on, created_by) VALUES (1, @MSG@, sysdate, 'me')
# 4. Table-option to specify one table contained by the database
log4j.appender.JDBC.table=logtest
# 5. Columns-option to describe the important columns of the table (Not nullable columns are mandatory to describe!)
log4j.appender.JDBC.columns=id_seq~EMPTY id~ID~MyIDHandler msg~MSG created_on~TIMESTAMP created_by~STATIC~Thomas Fenner (t.fenner@klopotek.de)
# 6. Layout-options to define the layout of the messages (optional)
#log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
#log4j.appender.JDBC.layout.ConversionPattern=%m
# 7. Buffer-option to define the size of the message-event-buffer (optional)
#log4j.appender.JDBC.buffer=1
# 8. Commit-option to define a auto-commitment (optional)
#log4j.appender.JDBC.commit=Y

View File

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.psibt.framework.net;
import java.io.*;
import java.net.*;
/**
* This interface defines all methods that have to be implemented for a HTTPRequestHandler for the
* PluggableHTTPServer.
*
* @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
*/
public interface HTTPRequestHandler {
/**
* Gets the title for html page
*/
public String getTitle();
/**
* Sets the title for html page
*/
public void setTitle(String title);
/**
* Gets the description for html page
*/
public String getDescription();
/**
* Sets the description for html page
*/
public void setDescription(String description);
/**
* Gets the virtual path in the HTTP server that ist handled in this HTTPRequestHandler.
* So the root path handler will return "/" (without brackets) because it handles the path
* "http://servername/" or a handler for "http://servername/somepath/" will return "/somepath/"
* It is important to include the trailing "/" because all HTTPRequestHandler have to serve a path!
*/
public String getHandledPath();
/**
* Sets the virtual path in the HTTP server that ist handled in this HTTPRequestHandler.
* So set the path to "/" for the root path handler because it handles the path
* "http://servername/" or set it to "/somepath/" for a handler for "http://servername/somepath/".
* It is important to include the trailing "/" because all HTTPRequestHandler have to serve a path!
*/
public void setHandledPath(String path);
/**
* Handles the given request and writes the reply to the given out-stream. Every handler has to check
* the request for the right path info.
*
* @param request - client browser request
* @param out - Out stream for sending data to client browser
* @return if the request was handled by this handler : true, else : false
*/
public boolean handleRequest(String request, Writer out);
}

View File

@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.psibt.framework.net;
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.log4j.*;
/**
* This class implements a RequestHandler for log4j configuration. It serves the "/log4j/" path
* in the PluggableHTTPServer. If this path is requested a list of all current log4j categories
* with their current priorities is created. All priority settings can be changed by the user
* and can be submitted and taken over.
*
* @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
*/
public class Log4jRequestHandler extends RootRequestHandler {
private Priority[] prios = Priority.getAllPossiblePriorities();
/**
* Creates a new Log4jRequestHandler object
*/
public Log4jRequestHandler() {
this.setTitle("log4j");
this.setDescription("log4j configuration");
this.setHandledPath("/log4j/");
}
/**
* Handles the given request and writes the reply to the given out-stream.
*
* @param request - client browser request
* @param out - Out stream for sending data to client browser
* @return if the request was handled by this handler : true, else : false
*/
public boolean handleRequest(String request, Writer out) {
String path = "";
String query = null;
String name;
try {
// check request url
URL url = new URL("http://localhost"+request);
path = url.getPath();
query = url.getQuery();
if (path.startsWith(this.getHandledPath()) == false) {
return false;
}
out.write("HTTP/1.0 200 OK\r\n");
out.write("Content-type: text/html\r\n\r\n");
out.write("<HTML><HEAD><TITLE>" + this.getTitle() + "</TITLE></HEAD>\r\n");
out.write("<BODY><H1>log4j</H1>\r\n");
out.write(this.getDescription() + "<br><br>\r\n");
// handle a request with query
if ((query != null) && (query.length() >= 0)) {
StringTokenizer st = new StringTokenizer(query, "&");
String cmd;
String catname;
String catval;
int idx;
while (st.hasMoreTokens()) {
cmd = st.nextToken();
idx = cmd.indexOf("=");
catname = cmd.substring(0, idx);
catval = cmd.substring(idx+1, cmd.length());
if (catname.equalsIgnoreCase("root"))
Category.getRoot().setPriority(Priority.toPriority(catval));
else
Category.getInstance(catname).setPriority(Priority.toPriority(catval));
}
}
// output category information in a form with a simple table
out.write("<form name=\"Formular\" ACTION=\""+this.getHandledPath()+"\" METHOD=\"PUT\">");
out.write("<table cellpadding=4>\r\n");
out.write(" <tr>\r\n");
out.write(" <td><b>Category</b></td>\r\n");
out.write(" <td><b>Priority</b></td>\r\n");
out.write(" <td><b>Appender</b></td>\r\n");
out.write(" </tr>\r\n");
// output for root category
Category cat = Category.getRoot();
out.write(" <tr><td>root</td>\r\n");
out.write(" <td>\r\n");
out.write(" <select size=1 name=\""+ cat.getName() +"\">");
for (int i = 0; i < prios.length; i++) {
if (cat.getChainedPriority().toString().equals(prios[i].toString()))
out.write("<option selected>"+prios[i].toString());
else
out.write("<option>"+prios[i].toString());
}
out.write("</select>\r\n");
out.write(" </td>\r\n");
out.write(" <td>\r\n");
for (Enumeration apds = cat.getAllAppenders(); apds.hasMoreElements();) {
Appender apd = (Appender)apds.nextElement();
name = apd.getName();
if (name == null)
name = "<i>(no name)</i>";
out.write(name);
if (apd instanceof AppenderSkeleton) {
try {
AppenderSkeleton apskel = (AppenderSkeleton)apd;
out.write(" [" + apskel.getThreshold().toString() + "]");
} catch (Exception ex) {
}
}
if (apds.hasMoreElements())
out.write(", ");
}
out.write(" </td>\r\n");
out.write(" </tr>\r\n");
// output for all other categories
for (Enumeration en = Category.getCurrentCategories(); en.hasMoreElements();) {
cat = (Category)en.nextElement();
out.write(" <tr>\r\n");
out.write(" <td>" + cat.getName() + "</td>\r\n");
out.write(" <td>\r\n");
out.write(" <select size=1 name=\""+ cat.getName() +"\">");
for (int i = 0; i < prios.length; i++) {
if (cat.getChainedPriority().toString().equals(prios[i].toString()))
out.write("<option selected>"+prios[i].toString());
else
out.write("<option>"+prios[i].toString());
}
out.write("</select>\r\n");
out.write(" </td>\r\n");
out.write(" <td>\r\n");
for (Enumeration apds = cat.getAllAppenders(); apds.hasMoreElements();) {
Appender apd = (Appender)apds.nextElement();
name = apd.getName();
if (name == null)
name = "<i>(no name)</i>";
out.write(name);
if (apd instanceof AppenderSkeleton) {
try {
AppenderSkeleton apskel = (AppenderSkeleton)apd;
out.write(" [" + apskel.getThreshold().toString() + "]");
} catch (Exception ex) {
}
}
if (apds.hasMoreElements())
out.write(", ");
}
out.write(" </td>\r\n");
out.write(" </tr>\r\n");
}
out.write("</table>\r\n");
out.write("<input type=submit value=\"Submit\">");
out.write("</form>");
out.write("</BODY></HTML>\r\n");
out.flush();
return true;
} catch (Exception ex) {
return false;
}
}
}

View File

@ -0,0 +1,262 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.psibt.framework.net;
import java.net.*;
import java.io.*;
import java.util.*;
import org.apache.log4j.*;
/**
* This class implements a HTTP-server frame. All HTTP-requests are handled by HTTPRequestHandler
* classes which implement the <code>HTTPRequestHandler</code> interface. Every RequestHandler has
* to be registered in the PluggableHTTPServer with the <code>addRequestHandler</code> method.
* A new thread is created for each connection to handle the request. If all reply data are sent
* to the client the connection is closed and the thread ends.
* An example how to use the PluggableHTTPServer class can be found in the <code>main</code> method
* at the end of the source file.
*
* @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
*/
public class PluggableHTTPServer implements Runnable {
public static final int DEFAULT_PORT = 80;
static Category cat = Category.getInstance("PluggableHTTPServer");
private int port;
private Vector handler;
private ServerSocket server;
/**
* Creates a new server object on the given TCP port.
* If the port is occupied by another process a IOException (java.net.BindException) is thrown.
*
* @param port - TCP port number to listen on for requests
*/
public PluggableHTTPServer(int port) throws IOException {
this.port = port;
this.handler = new Vector();
cat.setPriority(Priority.ERROR);
server = new ServerSocket(this.port);
}
/**
* Creates a new server object on the default TCP port 80
* If the port is occupied by another process a IOException (java.net.BindException) is thrown.
*/
public PluggableHTTPServer() throws IOException {
this(DEFAULT_PORT);
}
/**
* Registers the given HTTPRequestHandler
*
* @param h - the HTTPRequestHandler to register
*/
public void addRequestHandler(HTTPRequestHandler h) {
handler.add(h);
}
/**
* Unregisters the given HTTPRequestHandler
*
* @param h - the HTTPRequestHandler to unregister
*/
public void removeRequestHandler(HTTPRequestHandler h) {
handler.remove(h);
}
/**
* Sends the HTTP message 404 - File Not Found
* see RFC2616 for details
*
* @param out - Out stream for sending data to client browser
*/
public static void replyNotFound(Writer out) {
try {
out.write("HTTP/1.0 404 Not Found\r\n");
out.write("<HTML><HEAD><TITLE>Not Found</TITLE></HEAD>\r\n");
out.write("<BODY><H1>Not Found</H1>\r\n");
out.write("</BODY></HTML>\r\n");
out.flush();
} // end try
catch (IOException e) {
}
}
/**
* Sends the HTTP message 405 - Method Not Allowed
* see RFC2616 for details
*
* @param out - Out stream for sending data to client browser
*/
public static void replyMethodNotAllowed(Writer out) {
try {
out.write("HTTP/1.1 405 Method Not Allowed\r\n");
out.write("Allow: GET, PUT\r\n");
out.write("<HTML><HEAD><TITLE>Method Not Allowed</TITLE></HEAD>\r\n");
out.write("<BODY><H1>Method Not Allowed</H1>\r\n");
out.write("</BODY></HTML>\r\n");
out.flush();
} // end try
catch (IOException e) {
}
}
/**
* Creates the ReplyHTML data for the root page
*
* @param index - index of the RootRequestHandler
*/
public void autoCreateRootPage(int index) {
if (handler.get(index) instanceof RootRequestHandler) {
RootRequestHandler r = (RootRequestHandler)handler.get(index);
String html = "<HTML><HEAD><TITLE>"+r.getTitle()+"</TITLE></HEAD>\r\n";
html = html + "<BODY><H1>"+r.getDescription()+"</H1>\r\n";
for (int i = 0; i < handler.size(); i++) {
html = html + "<a href=\"" + ((HTTPRequestHandler)handler.get(i)).getHandledPath();
html = html + "\">" + ((HTTPRequestHandler)handler.get(i)).getDescription() + "</a><br>";
}
html = html + "</BODY></HTML>\r\n";
r.setReplyHTML(html);
}
}
/**
* Main loop of the PluggableHTTPServer
*/
public void run() {
while (true) {
try {
Socket s = server.accept();
Thread t = new ServerThread(s);
t.start();
}
catch (IOException e) {
}
}
}
/**
* This class handles the incomming connection for one request.
*/
class ServerThread extends Thread {
private Socket connection;
ServerThread(Socket s) {
this.connection = s;
}
/**
* Serves the HTTP request.
*/
public void run() {
try {
Writer out = new BufferedWriter(
new OutputStreamWriter(
connection.getOutputStream(), "ASCII"
)
);
Reader in = new InputStreamReader(
new BufferedInputStream(
connection.getInputStream()
)
);
// read the first line only; that's all we need
StringBuffer req = new StringBuffer(80);
while (true) {
int c = in.read();
if (c == '\r' || c == '\n' || c == -1) break;
req.append((char) c);
}
String get = req.toString();
cat.debug(get);
StringTokenizer st = new StringTokenizer(get);
String method = st.nextToken();
String request = st.nextToken();
String version = st.nextToken();
if (method.equalsIgnoreCase("GET")) {
boolean served = false;
for (int i = 0; i < handler.size(); i++) {
if (handler.get(i) instanceof HTTPRequestHandler) {
if (((HTTPRequestHandler)handler.get(i)).handleRequest(request, out)) {
served = true;
break;
}
}
}
if (!served)
PluggableHTTPServer.replyNotFound(out);
}
else {
PluggableHTTPServer.replyMethodNotAllowed(out);
}
} // end try
catch (IOException e) {
}
finally {
try {
if (connection != null) connection.close();
}
catch (IOException e) {}
}
} // end run
} // end class ServerThread
/**
* Demo how to use the PluggableHTTPServer.
*/
public static void main(String[] args) {
int thePort;
// create some logging stuff
BasicConfigurator.configure();
Category cat1 = Category.getInstance("cat1");
cat1.addAppender(new org.apache.log4j.ConsoleAppender(new PatternLayout("%m%n")));
Category cat2 = Category.getInstance("cat2");
cat2.setPriority(Priority.INFO);
cat2.addAppender(new org.apache.log4j.ConsoleAppender(new PatternLayout("%c - %m%n")));
// set TCP port number
try {
thePort = Integer.parseInt(args[1]);
}
catch (Exception e) {
thePort = PluggableHTTPServer.DEFAULT_PORT;
}
PluggableHTTPServer server = null;
while (server == null) {
try {
server = new PluggableHTTPServer(thePort);
server.addRequestHandler(new RootRequestHandler());
server.addRequestHandler(new Log4jRequestHandler());
server.addRequestHandler(new UserDialogRequestHandler());
server.autoCreateRootPage(0);
Thread t = new Thread(server);
t.start();
} catch (IOException e) {
server = null;
thePort++;
}
}
} // end main
}

View File

@ -0,0 +1,159 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 com.psibt.framework.net;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* This class implements a RequestHandler for the root path "/" in the PluggableHTTPServer.
* A simple HTML message will be replied to the client.
*
* @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
*/
public class RootRequestHandler implements HTTPRequestHandler {
private String title;
private String description;
private String handledPath;
private String ReplyType = "Content-type: text/html\r\n\r\n";
private String ReplyHTML = "<HTML><HEAD><TITLE>Root</TITLE></HEAD>\r\n"
+ "<BODY><H1>Root</H1>\r\n"
+ "</BODY></HTML>\r\n";
/**
* Creates a new RootRequestHandler object
*/
public RootRequestHandler() {
this.setTitle("root page");
this.setDescription("root page");
this.setHandledPath("/");
}
/**
* Gets the content type of the reply HTTP message
*
* @return content type as String
*/
public String getReplyType() {
return this.ReplyType;
}
/**
* Sets the content type of the reply HTTP message
*
* @param ReplyType - content type as String
*/
public void setReplyType(String ReplyType) {
this.ReplyType = ReplyType;
}
/**
* Gets the HTML data of the reply HTTP message
*
* @return HTML message as String
*/
public String getReplyHTML() {
return this.ReplyHTML;
}
/**
* Sets the HTML data of the reply HTTP message
*
* @param ReplyHTML - HTML message as String
*/
public void setReplyHTML(String ReplyHTML) {
this.ReplyHTML = ReplyHTML;
}
/**
* Gets the title for html page
*/
public String getTitle() {
return this.title;
}
/**
* Sets the title for html page
*/
public void setTitle(String title) {
this.title = title;
}
/**
* Gets the description for html page
*/
public String getDescription() {
return this.description;
}
/**
* Sets the description for html page
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Gets the server path
*
* @return the server path
*/
public String getHandledPath() {
return this.handledPath;
}
/**
* Sets the server path
*
* @param path - the server path
*/
public void setHandledPath(String path) {
this.handledPath = path;
}
/**
* Handles the given request and writes the reply to the given out-stream.
*
* @param request - client browser request
* @param out - Out stream for sending data to client browser
* @return if the request was handled by this handler : true, else : false
*/
public boolean handleRequest(String request, Writer out) {
String path = "";
String query = null;
try {
URL url = new URL("http://localhost"+request);
path = url.getPath();
query = url.getPath();
if (path.equals(handledPath) == false) {
return false;
}
out.write("HTTP/1.0 200 OK\r\n");
if (ReplyType != null)
out.write(ReplyType);
if (ReplyHTML != null)
out.write(ReplyHTML);
out.flush();
return true;
} catch (Exception ex) {
return false;
}
}
}

View File

@ -0,0 +1,135 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* Title: PSI Java Framework: UserDialogRequestHandler<p>
* Copyright: PSI-BT AG<p>
* History:
* Date Author What's new
* 16.04.2001 VMentzner Created
*/
package com.psibt.framework.net;
/**
* This class implements a RequestHandler for the path "/userdialog/" in the PluggableHTTPServer.
* A simple input form is presented in the browser where you can enter a message. This message will be sent
* to the PluggableHTTPServer and shown in a JOptionPane MessageDialog.
*
* @author <a HREF="mailto:V.Mentzner@psi-bt.de">Volker Mentzner</a>
*/
public class UserDialogRequestHandler extends RootRequestHandler {
private Component parentComponent;
/**
* Creates a new UserDialogRequestHandler object
*/
public UserDialogRequestHandler() {
this(null);
}
/**
* Creates a new UserDialogRequestHandler object with a parentComponent reference
*/
public UserDialogRequestHandler(Component parentComponent) {
this.setTitle("user dialog");
this.setDescription("show user dialog");
this.setHandledPath("/userdialog/");
this.parentComponent = parentComponent;
}
/**
* Handles the given request and writes the reply to the given out-stream.
*
* @param request - client browser request
* @param out - Out stream for sending data to client browser
* @return if the request was handled by this handler : true, else : false
*/
public boolean handleRequest(String request, Writer out) {
String path = "";
String query = null;
try {
URL url = new URL("http://localhost"+request);
path = url.getPath();
query = url.getQuery();
if (path.startsWith(this.getHandledPath()) == false) {
return false;
}
out.write("HTTP/1.0 200 OK\r\n");
out.write("Content-type: text/html\r\n\r\n");
out.write("<HTML><HEAD><TITLE>" + this.getTitle() + "</TITLE></HEAD>\r\n");
out.write("<BODY><H1>" + this.getDescription() + "</H1>\r\n");
if ((query != null) && (query.length() >= 0)) {
int idx = query.indexOf("=");
String message = query.substring(idx+1, query.length());
// replace '+' by space
message = message.replace('+', ' ');
// replace hex strings starting with '%' by their values
idx = message.indexOf("%");
while (idx >= 0) {
String sl = message.substring(0, idx);
String sm = message.substring(idx+1, idx+3);
String sr = message.substring(idx+3, message.length());
try {
int i = Integer.parseInt(sm, 16);
sm = String.valueOf((char)i);
}
catch (Exception ex) {
sm = "";
}
message = sl + sm + sr;
idx = message.indexOf("%");
}
// show message in a new thread
if ((message != null) && (message.length() > 0)) {
Thread t = new Thread(new DialogThread(parentComponent, message));
t.start();
}
}
out.write("<form name=\"Formular\" ACTION=\""+this.getHandledPath()+"+\" METHOD=\"PUT\">");
out.write("<table>\r\n");
out.write(" <tr><td>Send message to user</td></tr>\r\n");
out.write(" <tr><td><textarea name=\"message\" rows=10 cols=50></textarea></td></tr>\r\n");
out.write("</table>\r\n");
out.write("<input type=submit value=\"Submit\">");
out.write("</form>");
out.write("</BODY></HTML>\r\n");
out.flush();
return true;
} catch (Exception ex) {
return false;
}
}
/**
* Internal class to start the user dialog in a new thread. This makes the RequestHandler return
* immediatly
*/
class DialogThread implements Runnable {
private Component parentComponent;
private String message;
public DialogThread(Component parentComponent, String message) {
this.parentComponent = parentComponent;
this.message = message;
}
public void run() {
JOptionPane.showMessageDialog(parentComponent, message);
}
}
}

View File

@ -0,0 +1,42 @@
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
Delivered-To: cgu@qos.ch
From: "Mentzner, Volker" <V.Mentzner@PSI-BT.de>
To: "'cgu@qos.ch'" <cgu@qos.ch>
Subject: Remote configuring log4j
Date: Thu, 3 May 2001 08:10:25 +0200
X-Mailer: Internet Mail Service (5.5.2653.19)
Hi Ceki,
I am using log4j in a GUI project. Working at the application I found
it useful to remotely configure log4j with my favourite browser. This
is reached by using a very small web server (PluggableHTTPServer) with
extentions for tasks like configuring log4j. You can easily put this
web server in an application, an example can be found in
PluggableHTTPServer.main(). Maybe someone else can use this too, so I
sent it to you to give it to the contrib dir if you like it.
Regards
Volker Mentzner
<<pluggableServer.zip>>

View File

@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import org.apache.log4j.*;
import org.apache.log4j.helpers.PatternParser;
/**
Example showing how to extend PatternLayout to recognize additional
conversion characters.
<p>In this case MyPatternLayout recognizes %# conversion pattern. It
outputs the value of an internal counter which is also incremented
at each call.
<p>See <a href=doc-files/MyPatternLayout.java><b>source</b></a> code
for more details.
@see MyPatternParser
@see org.apache.log4j.PatternLayout
@author Anders Kristensen
*/
public class MyPatternLayout extends PatternLayout {
public
MyPatternLayout() {
this(DEFAULT_CONVERSION_PATTERN);
}
public
MyPatternLayout(String pattern) {
super(pattern);
}
public
PatternParser createPatternParser(String pattern) {
return new MyPatternParser(
pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
}
public
static void main(String[] args) {
Layout layout = new MyPatternLayout("[counter=%.10#] - %m%n");
Logger logger = Logger.getLogger("some.cat");
logger.addAppender(new ConsoleAppender(layout, ConsoleAppender.SYSTEM_OUT));
logger.debug("Hello, log");
logger.info("Hello again...");
}
}

View File

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import org.apache.log4j.helpers.FormattingInfo;
import org.apache.log4j.helpers.PatternConverter;
import org.apache.log4j.helpers.PatternParser;
import org.apache.log4j.spi.LoggingEvent;
/**
Example showing how to extend PatternParser to recognize additional
conversion characters. The examples shows that minimum and maximum
width and alignment settings apply for "extension" conversion
characters just as they do for PatternLayout recognized characters.
<p>In this case MyPatternParser recognizes %# and outputs the value
of an internal counter which is also incremented at each call.
See <a href=doc-files/MyPatternParser.java><b>source</b></a> code
for more details.
@see org.apache.log4j.examples.MyPatternLayout
@see org.apache.log4j.helpers.PatternParser
@see org.apache.log4j.PatternLayout
@author Anders Kristensen
*/
public class MyPatternParser extends PatternParser {
int counter = 0;
public
MyPatternParser(String pattern) {
super(pattern);
}
public
void finalizeConverter(char c) {
if (c == '#') {
addConverter(new UserDirPatternConverter(formattingInfo));
currentLiteral.setLength(0);
} else {
super.finalizeConverter(c);
}
}
private class UserDirPatternConverter extends PatternConverter {
UserDirPatternConverter(FormattingInfo formattingInfo) {
super(formattingInfo);
}
public
String convert(LoggingEvent event) {
return String.valueOf(++counter);
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
NumberCruncher's factor positive integers. See <a
href=doc-files/NumberCruncher.java>source</a> code for more details.
@author Ceki G&uuml;lc&uuml;
*/
public interface NumberCruncher extends Remote {
/**
Factor a positive integer <code>number</code> and return its
<em>distinct</em> factor's as an integer array.
*/
int[] factor(int number) throws RemoteException;
}

View File

@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import java.rmi.RemoteException;
import java.rmi.Naming;
import java.io.*;
/**
NumberCruncherClient is a simple client for factoring integers. A
remote NumberCruncher is contacted and asked to factor an
integer. The factors returned by the {@link NumberCruncherServer}
are displayed on the screen.
<p>See <a href=doc-files/NumberCruncherClient.java>source</a> code of
<code>NumberCruncherClient</code> for more details.
<pre>
<b>Usage:</b> java org.apache.log4j.examples.NumberCruncherClient HOST
&nbsp;&nbsp;&nbsp;&nbsp;where HOST is the machine where the NumberCruncherServer is running
</pre>
<p>Note that class files for the example code is not included in
any of the distributed log4j jar files. You will have to add the
directory <code>/dir-where-you-unpacked-log4j/classes</code> to
your classpath before trying out the examples.
@author Ceki G&uuml;lc&uuml;
*/
public class NumberCruncherClient {
public static void main(String[] args) {
if(args.length == 1) {
try {
String url = "rmi://"+args[0]+ "/Factor";
NumberCruncher nc = (NumberCruncher) Naming.lookup(url);
loop(nc);
}
catch(Exception e) {
e.printStackTrace();
}
}
else
usage("Wrong number of arguments.");
}
static
void usage(String msg) {
System.err.println(msg);
System.err.println(
"Usage: java org.apache.log4j.examples.NumberCruncherClient HOST\n" +
" where HOST is the machine where the NumberCruncherServer is running.");
System.exit(1);
}
static
void loop(NumberCruncher nc) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int i = 0;
while (true) {
System.out.print("Enter a number to factor, '-1' to quit: ");
try {
i = Integer.parseInt(in.readLine());
}
catch(Exception e) {
e.printStackTrace();
}
if(i == -1) {
System.out.print("Exiting loop.");
return;
}
else {
try {
System.out.println("Will attempt to factor "+i);
int[] factors = nc.factor(i);
System.out.print("The factors of "+i+" are");
for(int k=0; k < factors.length; k++) {
System.out.print(" " + factors[k]);
}
System.out.println(".");
}
catch(RemoteException e) {
System.err.println("Could not factor "+i);
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,172 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;
import java.rmi.Naming;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.log4j.PropertyConfigurator;
/**
A simple {@link NumberCruncher} implementation that logs its
progress when factoring numbers. The purpose of the whole exercise
is to show the use of nested diagnostic contexts in order to
distinguish the log output from different client requests.
<pre>
<b>Usage:</b> java org.apache.log4j.examples.NumberCruncherServer <em>configFile</em>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;where <em>configFile</em> is a log4j configuration file.
</pre>
We supply a simple config file <a href=doc-files/factor.lcf>factor.lcf</a>
for directing log output to the file <code>factor.log</code>.
<p>Try it yourself by starting a <code>NumberCruncherServer</code>
and make queries from multiple {@link NumberCruncherClient
NumberCruncherClients} to factor numbers.
<p><b><a href="doc-files/factor.html">Sample output</a></b> shows the log
output when two clients connect to the server near simultaneously.
<p>See <a href=doc-files/NumberCruncherServer.java>source</a> code
of <code>NumberCruncherServer</code> for more details.
<p>Note that class files for the example code is not included in
any of the distributed log4j jar files. You will have to add the
directory <code>/dir-where-you-unpacked-log4j/classes</code> to
your classpath before trying out the examples.
*/
public class NumberCruncherServer extends UnicastRemoteObject
implements NumberCruncher {
private static final long serialVersionUID = 2626753561969426769L;
static Logger logger = Logger.getLogger(NumberCruncherServer.class);
public
NumberCruncherServer() throws RemoteException {
}
public
int[] factor(int number) throws RemoteException {
// The client's host is an important source of information.
try {
NDC.push(getClientHost());
}
catch(java.rmi.server.ServerNotActiveException e) {
// we are being called from same VM
NDC.push("localhost");
}
// The information contained within the request is another source of
// distinctive information. It might reveal the users name, date of request,
// request ID etc. In servlet type environments, much information is
// contained in cookies.
NDC.push(String.valueOf(number));
logger.info("Beginning to factor.");
if(number <= 0) {
throw new IllegalArgumentException(number+" is not a positive integer.");
}
else if(number == 1)
return new int[] {1};
Vector factors = new Vector();
int n = number;
for(int i = 2; (i <= n) && (i*i <= number); i++) {
// It is bad practice to place log requests within tight loops.
// It is done here to show interleaved log output from
// different requests.
logger.debug("Trying to see if " + i + " is a factor.");
if((n % i) == 0) {
logger.info("Found factor "+i);
factors.addElement(new Integer(i));
do {
n /= i;
} while((n % i) == 0);
}
// Placing artificial delays in tight-loops will also lead to sub-optimal
// resuts. :-)
delay(100);
}
if(n != 1) {
logger.info("Found factor "+n);
factors.addElement(new Integer(n));
}
int len = factors.size();
int[] result = new int[len];
for(int i = 0; i < len; i++) {
result[i] = ((Integer) factors.elementAt(i)).intValue();
}
// Before leaving a thread we call NDC.remove. This deletes the reference
// to the thread in the internal hash table. Version 0.8.5 introduces a
// a lazy removal mechanism in case you forget to call remove when
// exiting a thread. See the java documentation in NDC.remove for further
// details.
NDC.remove();
return result;
}
static
void usage(String msg) {
System.err.println(msg);
System.err.println(
"Usage: java org.apache.log4j.examples.NumberCruncherServer configFile\n" +
" where configFile is a log4j configuration file.");
System.exit(1);
}
public static
void delay(int millis) {
try{Thread.sleep(millis);}
catch(InterruptedException e) {}
}
public static void main(String[] args) {
if(args.length != 1)
usage("Wrong number of arguments.");
NumberCruncherServer ncs;
PropertyConfigurator.configure(args[0]);
try {
ncs = new NumberCruncherServer();
Naming.rebind("Factor", ncs);
logger.info("NumberCruncherServer bound and ready to serve.");
}
catch(Exception e) {
logger.error("Could not bind NumberCruncherServer.", e);
return;
}
NumberCruncherClient.loop(ncs);
}
}

View File

@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Logger;
/**
Example code for log4j to viewed in conjunction with the {@link
examples.SortAlgo SortAlgo} class.
<p>This program expects a configuration file name as its first
argument, and the size of the array to sort as the second and last
argument. See its <b><a href="doc-files/Sort.java">source
code</a></b> for more details.
<p>Play around with different values in the configuration file and
watch the changing behavior.
<p>Example configuration files can be found in <a
href="doc-files/sort1.properties">sort1.properties</a>, <a
href="doc-files/sort2.properties">sort2.properties</a>, <a
href="doc-files/sort3.properties">sort3.properties</a> and <a
href="doc-files/sort4.properties">sort4.properties</a> are supplied with the
package.
<p>If you are also interested in logging performance, then have
look at the {@link org.apache.log4j.performance.Logging} class.
@author Ceki G&uuml;lc&uuml; */
public class Sort {
static Logger logger = Logger.getLogger(Sort.class.getName());
public static void main(String[] args) {
if(args.length != 2) {
usage("Incorrect number of parameters.");
}
int arraySize = -1;
try {
arraySize = Integer.valueOf(args[1]).intValue();
if(arraySize <= 0)
usage("Negative array size.");
}
catch(java.lang.NumberFormatException e) {
usage("Could not number format ["+args[1]+"].");
}
PropertyConfigurator.configure(args[0]);
int[] intArray = new int[arraySize];
logger.info("Populating an array of " + arraySize + " elements in" +
" reverse order.");
for(int i = arraySize -1 ; i >= 0; i--) {
intArray[i] = arraySize - i - 1;
}
SortAlgo sa1 = new SortAlgo(intArray);
sa1.bubbleSort();
sa1.dump();
// We intentionally initilize sa2 with null.
SortAlgo sa2 = new SortAlgo(null);
logger.info("The next log statement should be an error message.");
sa2.dump();
logger.info("Exiting main method.");
}
static
void usage(String errMsg) {
System.err.println(errMsg);
System.err.println("\nUsage: java org.apache.examples.Sort " +
"configFile ARRAY_SIZE\n"+
"where configFile is a configuration file\n"+
" ARRAY_SIZE is a positive integer.\n");
System.exit(1);
}
}

View File

@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
/**
Example code for log4j to viewed in conjunction with the {@link
examples.Sort Sort} class.
<p>SortAlgo uses the bubble sort algorithm to sort an integer
array. See also its <b><a href="doc-files/SortAlgo.java">source
code</a></b>.
@author Ceki G&uuml;lc&uuml; */
public class SortAlgo {
final static String className = SortAlgo.class.getName();
final static Logger LOG = Logger.getLogger(className);
final static Logger OUTER = Logger.getLogger(className + ".OUTER");
final static Logger INNER = Logger.getLogger(className + ".INNER");
final static Logger DUMP = Logger.getLogger(className + ".DUMP");
final static Logger SWAP = Logger.getLogger(className + ".SWAP");
int[] intArray;
SortAlgo(int[] intArray) {
this.intArray = intArray;
}
void bubbleSort() {
LOG.info( "Entered the sort method.");
for(int i = intArray.length -1; i >= 0 ; i--) {
NDC.push("i=" + i);
OUTER.debug("in outer loop.");
for(int j = 0; j < i; j++) {
NDC.push("j=" + j);
// It is poor practice to ship code with log staments in tight loops.
// We do it anyway in this example.
INNER.debug( "in inner loop.");
if(intArray[j] > intArray[j+1])
swap(j, j+1);
NDC.pop();
}
NDC.pop();
}
}
void dump() {
if(! (this.intArray instanceof int[])) {
DUMP.error("Tried to dump an uninitialized array.");
return;
}
DUMP.info("Dump of integer array:");
for(int i = 0; i < this.intArray.length; i++) {
DUMP.info("Element [" + i + "]=" + this.intArray[i]);
}
}
void swap(int l, int r) {
// It is poor practice to ship code with log staments in tight
// loops or code called potentially millions of times.
SWAP.debug( "Swapping intArray["+l+"]=" + intArray[l] +
" and intArray["+r+"]=" + intArray[r]);
int temp = this.intArray[l];
this.intArray[l] = this.intArray[r];
this.intArray[r] = temp;
}
}

View File

@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 examples;
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.NDC;
/**
View the <a href="doc-files/Trivial.java">source code</a> of this a
trivial usage example. Running <code>java examples.Trivial</code>
should output something similar to:
<pre>
0 INFO [main] examples.Trivial (Client #45890) - Awake awake. Put on thy strength.
15 DEBUG [main] examples.Trivial (Client #45890 DB) - Now king David was old.
278 INFO [main] examples.Trivial$InnerTrivial (Client #45890) - Entered foo.
293 INFO [main] examples.Trivial (Client #45890) - Exiting Trivial.
</pre>
<p> The increasing numbers at the beginning of each line are the
times elapsed since the start of the program. The string between
the parentheses is the nested diagnostic context.
<p>See {@link Sort} and {@link SortAlgo} for sligtly more elaborate
examples.
<p>Note thent class files for the example code is not included in
any of the distributed log4j jar files. You will have to add the
directory <code>/dir-where-you-unpacked-log4j/classes</code> to
your classpath before trying out the examples.
*/
public class Trivial {
static Logger logger = Logger.getLogger(Trivial.class);
public static void main(String[] args) {
BasicConfigurator.configure();
NDC.push("Client #45890");
logger.info("Awake awake. Put on thy strength.");
Trivial.foo();
InnerTrivial.foo();
logger.info("Exiting Trivial.");
}
static
void foo() {
NDC.push("DB");
logger.debug("Now king David was old.");
NDC.pop();
}
static class InnerTrivial {
static Logger logger = Logger.getLogger(InnerTrivial.class);
static
void foo() {
logger.info("Entered foo.");
}
}
}

Some files were not shown because too many files have changed in this diff Show More