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 #if defined(_MSC_VER)
18 	#pragma warning ( disable: 4231 4251 4275 4786 )
19 #endif
20 
21 #define __STDC_CONSTANT_MACROS
22 #include <log4cxx/net/socketappenderskeleton.h>
23 #include <log4cxx/helpers/loglog.h>
24 #include <log4cxx/helpers/optionconverter.h>
25 #include <log4cxx/helpers/stringhelper.h>
26 #include <log4cxx/spi/loggingevent.h>
27 #include <log4cxx/helpers/synchronized.h>
28 #include <log4cxx/helpers/transcoder.h>
29 #include <log4cxx/helpers/bytearrayoutputstream.h>
30 
31 using namespace log4cxx;
32 using namespace log4cxx::helpers;
33 using namespace log4cxx::net;
34 
SocketAppenderSkeleton(int defaultPort,int reconnectionDelay1)35 SocketAppenderSkeleton::SocketAppenderSkeleton(int defaultPort, int reconnectionDelay1)
36 	:  remoteHost(),
37 	   address(),
38 	   port(defaultPort),
39 	   reconnectionDelay(reconnectionDelay1),
40 	   locationInfo(false),
41 	   thread()
42 {
43 }
44 
SocketAppenderSkeleton(InetAddressPtr address1,int port1,int delay)45 SocketAppenderSkeleton::SocketAppenderSkeleton(InetAddressPtr address1, int port1, int delay)
46 	:
47 	remoteHost(),
48 	address(address1),
49 	port(port1),
50 	reconnectionDelay(delay),
51 	locationInfo(false),
52 	thread()
53 {
54 	remoteHost = this->address->getHostName();
55 }
56 
SocketAppenderSkeleton(const LogString & host,int port1,int delay)57 SocketAppenderSkeleton::SocketAppenderSkeleton(const LogString& host, int port1, int delay)
58 	:   remoteHost(host),
59 		address(InetAddress::getByName(host)),
60 		port(port1),
61 		reconnectionDelay(delay),
62 		locationInfo(false),
63 		thread()
64 {
65 }
66 
~SocketAppenderSkeleton()67 SocketAppenderSkeleton::~SocketAppenderSkeleton()
68 {
69 	finalize();
70 
71 	try
72 	{
73 		thread.join();
74 	}
75 	catch (ThreadException& ex)
76 	{
77 		LogLog::error(LOG4CXX_STR("Error closing socket appender connection thread"), ex);
78 	}
79 }
80 
activateOptions(Pool & p)81 void SocketAppenderSkeleton::activateOptions(Pool& p)
82 {
83 	AppenderSkeleton::activateOptions(p);
84 	connect(p);
85 }
86 
close()87 void SocketAppenderSkeleton::close()
88 {
89 	LOCK_W sync(mutex);
90 
91 	if (closed)
92 	{
93 		return;
94 	}
95 
96 	closed = true;
97 	cleanUp(pool);
98 	thread.interrupt();
99 }
100 
connect(Pool & p)101 void SocketAppenderSkeleton::connect(Pool& p)
102 {
103 	if (address == 0)
104 	{
105 		LogLog::error(LogString(LOG4CXX_STR("No remote host is set for Appender named \"")) +
106 			name + LOG4CXX_STR("\"."));
107 	}
108 	else
109 	{
110 		cleanUp(p);
111 
112 		try
113 		{
114 			SocketPtr socket(new Socket(address, port));
115 			setSocket(socket, p);
116 		}
117 		catch (SocketException& e)
118 		{
119 			LogString msg = LOG4CXX_STR("Could not connect to remote log4cxx server at [")
120 				+ address->getHostName() + LOG4CXX_STR("].");
121 
122 			if (reconnectionDelay > 0)
123 			{
124 				msg += LOG4CXX_STR(" We will try again later. ");
125 			}
126 
127 			fireConnector(); // fire the connector thread
128 			LogLog::error(msg, e);
129 		}
130 	}
131 }
132 
setOption(const LogString & option,const LogString & value)133 void SocketAppenderSkeleton::setOption(const LogString& option, const LogString& value)
134 {
135 	if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("REMOTEHOST"), LOG4CXX_STR("remotehost")))
136 	{
137 		setRemoteHost(value);
138 	}
139 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PORT"), LOG4CXX_STR("port")))
140 	{
141 		setPort(OptionConverter::toInt(value, getDefaultPort()));
142 	}
143 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo")))
144 	{
145 		setLocationInfo(OptionConverter::toBoolean(value, false));
146 	}
147 	else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("RECONNECTIONDELAY"), LOG4CXX_STR("reconnectiondelay")))
148 	{
149 		setReconnectionDelay(OptionConverter::toInt(value, getDefaultDelay()));
150 	}
151 	else
152 	{
153 		AppenderSkeleton::setOption(option, value);
154 	}
155 }
156 
fireConnector()157 void SocketAppenderSkeleton::fireConnector()
158 {
159 	LOCK_W sync(mutex);
160 
161 	if ( !thread.isAlive() )
162 	{
163 		LogLog::debug(LOG4CXX_STR("Connector thread not alive: starting monitor."));
164 
165 		try
166 		{
167 			thread.run(monitor, this);
168 		}
169 		catch ( ThreadException& te )
170 		{
171 			LogLog::error(LOG4CXX_STR("Monitor not started: "), te);
172 		}
173 	}
174 }
175 
monitor(apr_thread_t *,void * data)176 void* LOG4CXX_THREAD_FUNC SocketAppenderSkeleton::monitor(apr_thread_t* /* thread */, void* data)
177 {
178 	SocketAppenderSkeleton* socketAppender = (SocketAppenderSkeleton*) data;
179 	SocketPtr socket;
180 	bool isClosed = socketAppender->closed;
181 
182 	while (!isClosed)
183 	{
184 		try
185 		{
186 			Thread::sleep(socketAppender->reconnectionDelay);
187 
188 			if (!socketAppender->closed)
189 			{
190 				LogLog::debug(LogString(LOG4CXX_STR("Attempting connection to "))
191 					+ socketAppender->address->getHostName());
192 				socket = new Socket(socketAppender->address, socketAppender->port);
193 				Pool p;
194 				socketAppender->setSocket(socket, p);
195 				LogLog::debug(LOG4CXX_STR("Connection established. Exiting connector thread."));
196 			}
197 
198 			return NULL;
199 		}
200 		catch (InterruptedException&)
201 		{
202 			LogLog::debug(LOG4CXX_STR("Connector interrupted.  Leaving loop."));
203 			return NULL;
204 		}
205 		catch (ConnectException&)
206 		{
207 			LogLog::debug(LOG4CXX_STR("Remote host ")
208 				+ socketAppender->address->getHostName()
209 				+ LOG4CXX_STR(" refused connection."));
210 		}
211 		catch (IOException& e)
212 		{
213 			LogString exmsg;
214 			log4cxx::helpers::Transcoder::decode(e.what(), exmsg);
215 
216 			LogLog::debug(((LogString) LOG4CXX_STR("Could not connect to "))
217 				+ socketAppender->address->getHostName()
218 				+ LOG4CXX_STR(". Exception is ")
219 				+ exmsg);
220 		}
221 
222 		isClosed = socketAppender->closed;
223 	}
224 
225 	LogLog::debug(LOG4CXX_STR("Exiting Connector.run() method."));
226 	return NULL;
227 }
228