View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j.net;
19  
20  import org.apache.log4j.Logger;
21  import org.apache.log4j.helpers.Constants;
22  import org.apache.log4j.plugins.Receiver;
23  import org.apache.log4j.spi.ComponentBase;
24  import org.apache.log4j.spi.Decoder;
25  import org.apache.log4j.spi.LoggerRepository;
26  import org.apache.log4j.spi.LoggingEvent;
27  
28  import java.io.IOException;
29  import java.io.InputStream;
30  import java.net.Socket;
31  import java.util.List;
32  
33  
34  /**
35   * Read {@link LoggingEvent} objects sent from a remote client using XML over
36   * Sockets (TCP). These logging events are logged according to local
37   * policy, as if they were generated locally.
38   * <p>
39   * <p>For example, the socket node might decide to log events to a
40   * local file and also resent them to a second socket node.
41   *
42   * @author Scott Deboy &lt;sdeboy@apache.org&gt;;
43   * @since 0.8.4
44   */
45  public class XMLSocketNode extends ComponentBase implements Runnable {
46      Socket socket;
47      Receiver receiver;
48      Decoder decoder;
49      SocketNodeEventListener listener;
50  
51      /**
52       * Constructor for socket and logger repository.
53       */
54      public XMLSocketNode(
55          String decoder, Socket socket, LoggerRepository hierarchy) {
56          this.repository = hierarchy;
57          try {
58              Class c = Class.forName(decoder);
59              Object o = c.newInstance();
60  
61              if (o instanceof Decoder) {
62                  this.decoder = (Decoder) o;
63              }
64          } catch (ClassNotFoundException cnfe) {
65              getLogger().warn("Unable to find decoder", cnfe);
66          } catch (IllegalAccessException | InstantiationException iae) {
67              getLogger().warn("Unable to construct decoder", iae);
68          }
69  
70          this.socket = socket;
71      }
72  
73      /**
74       * Constructor for socket and reciever.
75       */
76      public XMLSocketNode(String decoder, Socket socket, Receiver receiver) {
77          try {
78              Class c = Class.forName(decoder);
79              Object o = c.newInstance();
80  
81              if (o instanceof Decoder) {
82                  this.decoder = (Decoder) o;
83              }
84          } catch (ClassNotFoundException cnfe) {
85              getLogger().warn("Unable to find decoder", cnfe);
86          } catch (IllegalAccessException | InstantiationException iae) {
87              getLogger().warn("Unable to construct decoder", iae);
88          }
89  
90          this.socket = socket;
91          this.receiver = receiver;
92      }
93  
94      /**
95       * Set the event listener on this node.
96       */
97      public void setListener(SocketNodeEventListener _listener) {
98          listener = _listener;
99      }
100 
101     public void run() {
102         Logger remoteLogger;
103         Exception listenerException = null;
104         InputStream is;
105 
106         if ((this.receiver == null) || (this.decoder == null)) {
107             listenerException =
108                 new Exception(
109                     "No receiver or decoder provided.  Cannot process xml socket events");
110             getLogger().error(
111                 "Exception constructing XML Socket Receiver", listenerException);
112         }
113 
114         try {
115             is = socket.getInputStream();
116         } catch (Exception e) {
117             is = null;
118             listenerException = e;
119             getLogger().error("Exception opening InputStream to " + socket, e);
120         }
121 
122         if (is != null) {
123             String hostName = socket.getInetAddress().getHostName();
124             String remoteInfo = hostName + ":" + socket.getPort();
125 
126             try {
127                 //read data from the socket
128                 //it's up to the individual decoder to handle incomplete event data
129                 while (true) {
130                     byte[] b = new byte[1024];
131                     int length = is.read(b);
132                     if (length == -1) {
133                         getLogger().info(
134                             "no bytes read from stream - closing connection.");
135                         break;
136                     }
137                     List<LoggingEvent> v = decoder.decodeEvents(new String(b, 0, length));
138 
139                     if (v != null) {
140 
141                         for (Object aV : v) {
142                             LoggingEvent e = (LoggingEvent) aV;
143                             e.setProperty(Constants.HOSTNAME_KEY, hostName);
144 
145                             // store the known remote info in an event property
146                             e.setProperty("log4j.remoteSourceInfo", remoteInfo);
147 
148                             // if configured with a receiver, tell it to post the event
149                             if (receiver != null) {
150                                 receiver.doPost(e);
151 
152                                 // else post it via the hierarchy
153                             } else {
154                                 // get a logger from the hierarchy. The name of the logger
155                                 // is taken to be the name contained in the event.
156                                 remoteLogger = repository.getLogger(e.getLoggerName());
157 
158                                 //event.logger = remoteLogger;
159                                 // apply the logger-level filter
160                                 if (
161                                     e.getLevel().isGreaterOrEqual(
162                                         remoteLogger.getEffectiveLevel())) {
163                                     // finally log the event as if was generated locally
164                                     remoteLogger.callAppenders(e);
165                                 }
166                             }
167                         }
168                     }
169                 }
170             } catch (java.io.EOFException e) {
171                 getLogger().info("Caught java.io.EOFException closing connection.");
172                 listenerException = e;
173             } catch (java.net.SocketException e) {
174                 getLogger().info(
175                     "Caught java.net.SocketException closing connection.");
176                 listenerException = e;
177             } catch (IOException e) {
178                 getLogger().info("Caught java.io.IOException: " + e);
179                 getLogger().info("Closing connection.");
180                 listenerException = e;
181             } catch (Exception e) {
182                 getLogger().error("Unexpected exception. Closing connection.", e);
183                 listenerException = e;
184             }
185         }
186 
187         // close the socket
188         try {
189             if (is != null) {
190                 is.close();
191             }
192         } catch (Exception e) {
193             //logger.info("Could not close connection.", e);
194         }
195 
196         // send event to listener, if configured
197         if (listener != null) {
198             listener.socketClosedEvent(listenerException);
199         }
200     }
201 }