| /* |
| * 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.InputStream; |
| import org.apache.log4j.plugins.Pauseable; |
| import org.apache.log4j.plugins.Plugin; |
| import org.apache.log4j.plugins.Receiver; |
| import org.apache.log4j.spi.LoggerRepository; |
| import org.apache.log4j.spi.LoggingEvent; |
| |
| import java.net.ServerSocket; |
| import java.net.Socket; |
| import java.util.List; |
| import java.util.Vector; |
| import org.apache.log4j.chainsaw.ChainsawReceiverSkeleton; |
| import org.apache.log4j.chainsaw.logevents.ChainsawLoggingEvent; |
| import org.apache.log4j.spi.Decoder; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| |
| |
| /** |
| * XMLSocketReceiver receives a remote logging event via XML on a configured |
| * socket and "posts" it to a LoggerRepository as if the event were |
| * generated locally. This class is designed to receive events from |
| * the XMLSocketAppender class (or classes that send compatible events). |
| * <p> |
| * This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging |
| * XMLFormatter (via the org.apache.log4j.spi.Decoder interface). |
| * <p> |
| * By default, log4j's XMLLayout is supported (no need to specify a decoder in that case). |
| * <p> |
| * To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param |
| * of org.apache.log4j.xml.UtilLoggingXMLDecoder. |
| * <p> |
| * Once the event has been "posted", it will be handled by the |
| * appenders currently configured in the LoggerRespository. |
| * |
| * @author Mark Womack |
| * @author Scott Deboy <sdeboy@apache.org> |
| */ |
| public class XMLSocketReceiver extends ChainsawReceiverSkeleton implements Runnable, PortBased { |
| //default to log4j xml decoder |
| protected String decoder = "org.apache.log4j.xml.XMLDecoder"; |
| private ServerSocket serverSocket; |
| private List<Socket> socketList = new Vector<>(); |
| private Thread rThread; |
| public static final int DEFAULT_PORT = 4448; |
| protected int port = DEFAULT_PORT; |
| private boolean advertiseViaMulticastDNS; |
| private ZeroConfSupport zeroConf; |
| private boolean active = false; |
| |
| private static final Logger logger = LogManager.getLogger(); |
| |
| /** |
| * The MulticastDNS zone advertised by an XMLSocketReceiver |
| */ |
| public static final String ZONE = "_log4j_xml_tcpaccept_receiver.local."; |
| |
| /* |
| * Log4j doesn't provide an XMLSocketAppender, but the MulticastDNS zone that should be advertised by one is: |
| * _log4j_xml_tcpconnect_appender.local. |
| */ |
| |
| public XMLSocketReceiver() { |
| } |
| |
| /** |
| * Get the port to receive logging events on. |
| */ |
| public int getPort() { |
| return port; |
| } |
| |
| /** |
| * Set the port to receive logging events on. |
| */ |
| public void setPort(int _port) { |
| port = _port; |
| } |
| |
| public String getDecoder() { |
| return decoder; |
| } |
| |
| /** |
| * Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file. |
| */ |
| public void setDecoder(String _decoder) { |
| decoder = _decoder; |
| } |
| |
| /** |
| * Starts the XMLSocketReceiver with the current options. |
| */ |
| public void activateOptions() { |
| if (!isActive()) { |
| rThread = new Thread(this); |
| rThread.setDaemon(true); |
| rThread.start(); |
| |
| if (advertiseViaMulticastDNS) { |
| zeroConf = new ZeroConfSupport(ZONE, port, getName()); |
| zeroConf.advertise(); |
| } |
| |
| active = true; |
| } |
| } |
| |
| public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) { |
| this.advertiseViaMulticastDNS = advertiseViaMulticastDNS; |
| } |
| |
| public boolean isAdvertiseViaMulticastDNS() { |
| return advertiseViaMulticastDNS; |
| } |
| |
| /** |
| * Called when the receiver should be stopped. Closes the |
| * server socket and all of the open sockets. |
| */ |
| @Override |
| public synchronized void shutdown() { |
| // mark this as no longer running |
| active = false; |
| |
| if (rThread != null) { |
| rThread.interrupt(); |
| rThread = null; |
| } |
| doShutdown(); |
| } |
| |
| /** |
| * Does the actual shutting down by closing the server socket |
| * and any connected sockets that have been created. |
| */ |
| private synchronized void doShutdown() { |
| active = false; |
| |
| logger.debug("{} doShutdown called", getName()); |
| |
| // close the server socket |
| closeServerSocket(); |
| |
| if (advertiseViaMulticastDNS) { |
| zeroConf.unadvertise(); |
| } |
| } |
| |
| /** |
| * Closes the server socket, if created. |
| */ |
| private void closeServerSocket() { |
| logger.debug("{} closing server socket", getName()); |
| |
| try { |
| if (serverSocket != null) { |
| serverSocket.close(); |
| } |
| } catch (Exception e) { |
| // ignore for now |
| } |
| |
| serverSocket = null; |
| } |
| |
| /** |
| * Loop, accepting new socket connections. |
| */ |
| public void run() { |
| /** |
| * Ensure we start fresh. |
| */ |
| logger.debug("performing socket cleanup prior to entering loop for {}", name); |
| closeServerSocket(); |
| logger.debug("socket cleanup complete for {}", name); |
| active = true; |
| |
| // start the server socket |
| try { |
| serverSocket = new ServerSocket(port); |
| } catch (Exception e) { |
| logger.error( |
| "error starting XMLSocketReceiver (" + this.getName() |
| + "), receiver did not start", e); |
| active = false; |
| doShutdown(); |
| |
| return; |
| } |
| |
| Socket socket = null; |
| |
| try { |
| logger.debug("in run-about to enter while isactiveloop"); |
| |
| active = true; |
| |
| while (!rThread.isInterrupted()) { |
| // if we have a socket, start watching it |
| if (socket != null ) { |
| logger.debug("socket not null - parsing data"); |
| parseIncomingData(socket); |
| } |
| |
| logger.debug("waiting to accept socket"); |
| |
| // wait for a socket to open, then loop to start it |
| socket = serverSocket.accept(); |
| logger.debug("accepted socket"); |
| } |
| |
| // socket not watched because we a no longer running |
| // so close it now. |
| if (socket != null) { |
| socket.close(); |
| } |
| } catch (Exception e) { |
| logger.warn( |
| "socket server disconnected, stopping"); |
| } |
| } |
| |
| @Override |
| public void start() { |
| logger.debug("Starting receiver"); |
| if (!isActive()) { |
| rThread = new Thread(this); |
| rThread.setDaemon(true); |
| rThread.start(); |
| |
| if (advertiseViaMulticastDNS) { |
| zeroConf = new ZeroConfSupport(ZONE, port, getName()); |
| zeroConf.advertise(); |
| } |
| |
| active = true; |
| } |
| } |
| |
| @Override |
| public boolean isActive() { |
| return active; |
| } |
| |
| private void parseIncomingData(Socket sock){ |
| InputStream is; |
| Decoder d = null; |
| |
| try{ |
| d = (Decoder) Class.forName(decoder).getDeclaredConstructor().newInstance(); |
| } catch (Exception e) { |
| logger.error("Unable to load correct decoder", e); |
| return; |
| } |
| |
| try { |
| is = sock.getInputStream(); |
| } catch (Exception e) { |
| is = null; |
| logger.error("Exception opening InputStream to " + sock, e); |
| return; |
| } |
| |
| while (is != null) { |
| try{ |
| byte[] b = new byte[1024]; |
| int length = is.read(b); |
| if (length == -1) { |
| logger.info( |
| "no bytes read from stream - closing connection."); |
| break; |
| } |
| List<ChainsawLoggingEvent> v = d.decodeEvents(new String(b, 0, length)); |
| |
| for( ChainsawLoggingEvent evt : v ){ |
| append(evt); |
| } |
| }catch(Exception ex){ |
| logger.error(ex); |
| break; |
| } |
| } |
| |
| // close the socket |
| try { |
| if (is != null) { |
| is.close(); |
| } |
| } catch (Exception e) { |
| //logger.info("Could not close connection.", e); |
| } |
| } |
| } |