1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.net;
15
16 import java.io.EOFException;
17 import java.io.IOException;
18 import java.io.ObjectInputStream;
19 import java.net.ConnectException;
20 import java.net.InetAddress;
21 import java.net.Socket;
22 import java.net.UnknownHostException;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.RejectedExecutionException;
26
27 import javax.net.SocketFactory;
28
29 import ch.qos.logback.classic.Logger;
30 import ch.qos.logback.classic.LoggerContext;
31 import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream;
32 import ch.qos.logback.classic.spi.ILoggingEvent;
33 import ch.qos.logback.core.net.DefaultSocketConnector;
34 import ch.qos.logback.core.net.AbstractSocketAppender;
35 import ch.qos.logback.core.net.SocketConnector;
36 import ch.qos.logback.core.util.CloseUtil;
37
38
39
40
41
42
43
44 public class SocketReceiver extends ReceiverBase implements Runnable, SocketConnector.ExceptionHandler {
45
46 private static final int DEFAULT_ACCEPT_CONNECTION_DELAY = 5000;
47
48 private String remoteHost;
49 private InetAddress address;
50 private int port;
51 private int reconnectionDelay;
52 private int acceptConnectionTimeout = DEFAULT_ACCEPT_CONNECTION_DELAY;
53
54 private String receiverId;
55 private volatile Socket socket;
56 private Future<Socket> connectorTask;
57
58
59
60
61 protected boolean shouldStart() {
62 int errorCount = 0;
63 if (port == 0) {
64 errorCount++;
65 addError("No port was configured for receiver. "
66 + "For more information, please visit http://logback.qos.ch/codes.html#receiver_no_port");
67 }
68
69 if (remoteHost == null) {
70 errorCount++;
71 addError("No host name or address was configured for receiver. "
72 + "For more information, please visit http://logback.qos.ch/codes.html#receiver_no_host");
73 }
74
75 if (reconnectionDelay == 0) {
76 reconnectionDelay = AbstractSocketAppender.DEFAULT_RECONNECTION_DELAY;
77 }
78
79 if (errorCount == 0) {
80 try {
81 address = InetAddress.getByName(remoteHost);
82 } catch (UnknownHostException ex) {
83 addError("unknown host: " + remoteHost);
84 errorCount++;
85 }
86 }
87
88 if (errorCount == 0) {
89 receiverId = "receiver " + remoteHost + ":" + port + ": ";
90 }
91
92 return errorCount == 0;
93 }
94
95
96
97
98 protected void onStop() {
99 if (socket != null) {
100 CloseUtil.closeQuietly(socket);
101 }
102 }
103
104 @Override
105 protected Runnable getRunnableTask() {
106 return this;
107 }
108
109
110
111
112 public void run() {
113 try {
114 LoggerContext lc = (LoggerContext) getContext();
115 while (!Thread.currentThread().isInterrupted()) {
116 SocketConnector connector = createConnector(address, port, 0, reconnectionDelay);
117 connectorTask = activateConnector(connector);
118 if (connectorTask == null) {
119 break;
120 }
121 socket = waitForConnectorToReturnASocket();
122 if (socket == null)
123 break;
124 dispatchEvents(lc);
125 }
126 } catch (InterruptedException ex) {
127 assert true;
128 }
129 addInfo("shutting down");
130 }
131
132 private SocketConnector createConnector(InetAddress address, int port, int initialDelay, int retryDelay) {
133 SocketConnector connector = newConnector(address, port, initialDelay, retryDelay);
134 connector.setExceptionHandler(this);
135 connector.setSocketFactory(getSocketFactory());
136 return connector;
137 }
138
139 private Future<Socket> activateConnector(SocketConnector connector) {
140 try {
141 return getContext().getScheduledExecutorService().submit(connector);
142 } catch (RejectedExecutionException ex) {
143 return null;
144 }
145 }
146
147 private Socket waitForConnectorToReturnASocket() throws InterruptedException {
148 try {
149 Socket s = connectorTask.get();
150 connectorTask = null;
151 return s;
152 } catch (ExecutionException e) {
153 return null;
154 }
155 }
156
157 private void dispatchEvents(LoggerContext lc) {
158 ObjectInputStream ois = null;
159 try {
160 socket.setSoTimeout(acceptConnectionTimeout);
161 ois = new HardenedLoggingEventInputStream(socket.getInputStream());
162 socket.setSoTimeout(0);
163 addInfo(receiverId + "connection established");
164 while (true) {
165 ILoggingEvent event = (ILoggingEvent) ois.readObject();
166 Logger remoteLogger = lc.getLogger(event.getLoggerName());
167 if (remoteLogger.isEnabledFor(event.getLevel())) {
168 remoteLogger.callAppenders(event);
169 }
170 }
171 } catch (EOFException ex) {
172 addInfo(receiverId + "end-of-stream detected");
173 } catch (IOException ex) {
174 addInfo(receiverId + "connection failed: " + ex);
175 } catch (ClassNotFoundException ex) {
176 addInfo(receiverId + "unknown event class: " + ex);
177 } finally {
178 CloseUtil.closeQuietly(ois);
179 CloseUtil.closeQuietly(socket);
180 socket = null;
181 addInfo(receiverId + "connection closed");
182 }
183 }
184
185
186
187
188 public void connectionFailed(SocketConnector connector, Exception ex) {
189 if (ex instanceof InterruptedException) {
190 addInfo("connector interrupted");
191 } else if (ex instanceof ConnectException) {
192 addInfo(receiverId + "connection refused");
193 } else {
194 addInfo(receiverId + ex);
195 }
196 }
197
198 protected SocketConnector newConnector(InetAddress address, int port, int initialDelay, int retryDelay) {
199 return new DefaultSocketConnector(address, port, initialDelay, retryDelay);
200 }
201
202 protected SocketFactory getSocketFactory() {
203 return SocketFactory.getDefault();
204 }
205
206 public void setRemoteHost(String remoteHost) {
207 this.remoteHost = remoteHost;
208 }
209
210 public void setPort(int port) {
211 this.port = port;
212 }
213
214 public void setReconnectionDelay(int reconnectionDelay) {
215 this.reconnectionDelay = reconnectionDelay;
216 }
217
218 public void setAcceptConnectionTimeout(int acceptConnectionTimeout) {
219 this.acceptConnectionTimeout = acceptConnectionTimeout;
220 }
221
222 }