| /* | |
| * 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. | |
| */ | |
| #define __STDC_CONSTANT_MACROS | |
| #include <log4cxx/net/socketappenderskeleton.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/helpers/charsetencoder.h> | |
| #include <log4cxx/helpers/optionconverter.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/spi/loggingevent.h> | |
| #include <log4cxx/helpers/threadutility.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <log4cxx/helpers/bytearrayoutputstream.h> | |
| #include <log4cxx/helpers/outputstreamwriter.h> | |
| #include <log4cxx/helpers/socketoutputstream.h> | |
| #include <log4cxx/helpers/threadutility.h> | |
| #include <log4cxx/private/socketappenderskeleton_priv.h> | |
| #include <functional> | |
| #include <chrono> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::helpers; | |
| using namespace LOG4CXX_NS::net; | |
| #define _priv static_cast<SocketAppenderSkeletonPriv*>(m_priv.get()) | |
| SocketAppenderSkeleton::SocketAppenderSkeleton(int defaultPort, int reconnectionDelay) | |
| : AppenderSkeleton(std::make_unique<SocketAppenderSkeletonPriv>(defaultPort, reconnectionDelay)) | |
| { | |
| } | |
| SocketAppenderSkeleton::SocketAppenderSkeleton(const helpers::InetAddressPtr& address, int port, int reconnectionDelay) | |
| : AppenderSkeleton(std::make_unique<SocketAppenderSkeletonPriv>(address, port, reconnectionDelay)) | |
| { | |
| } | |
| SocketAppenderSkeleton::SocketAppenderSkeleton(const LogString& host, int port, int reconnectionDelay) | |
| : AppenderSkeleton(std::make_unique<SocketAppenderSkeletonPriv>(host, port, reconnectionDelay)) | |
| { | |
| } | |
| SocketAppenderSkeleton::SocketAppenderSkeleton(std::unique_ptr<SocketAppenderSkeletonPriv> priv) | |
| : AppenderSkeleton (std::move(priv)) | |
| { | |
| } | |
| SocketAppenderSkeleton::~SocketAppenderSkeleton() | |
| { | |
| } | |
| void SocketAppenderSkeleton::activateOptions( ) | |
| { | |
| _priv->connect(); | |
| } | |
| void SocketAppenderSkeleton::close() | |
| { | |
| if (_priv->setClosed()) | |
| _priv->close(); | |
| } | |
| void SocketAppenderSkeleton::SocketAppenderSkeletonPriv::connect() | |
| { | |
| if (this->address == 0) | |
| { | |
| LogLog::error(LogString(LOG4CXX_STR("No remote host is set for Appender named \"")) + | |
| this->name + LOG4CXX_STR("\".")); | |
| } | |
| else | |
| { | |
| this->close(); | |
| try | |
| { | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Connecting to [") | |
| + this->address->toString() + LOG4CXX_STR(":")); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::debug(msg); | |
| } | |
| this->setOutputSink(Socket::create(this->address, this->port, this->socketSubclass)); | |
| } | |
| catch (SocketException& e) | |
| { | |
| LogString msg = LOG4CXX_STR("Could not connect to [") | |
| + this->address->toString() + LOG4CXX_STR(":"); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| this->fireConnector(); // fire the connector thread | |
| LogLog::warn(msg, e); | |
| } | |
| } | |
| } | |
| void SocketAppenderSkeleton::setOption(const LogString& option, const LogString& value) | |
| { | |
| if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("REMOTEHOST"), LOG4CXX_STR("remotehost"))) | |
| { | |
| setRemoteHost(value); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("PORT"), LOG4CXX_STR("port"))) | |
| { | |
| setPort(OptionConverter::toInt(value, getDefaultPort())); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("LOCATIONINFO"), LOG4CXX_STR("locationinfo"))) | |
| { | |
| setLocationInfo(OptionConverter::toBoolean(value, false)); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("RECONNECTIONDELAY"), LOG4CXX_STR("reconnectiondelay"))) | |
| { | |
| setReconnectionDelay(OptionConverter::toInt(value, getDefaultDelay())); | |
| } | |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SOCKETSUBCLASS"), LOG4CXX_STR("socketsubclass"))) | |
| { | |
| setSocketSubclass(value); | |
| } | |
| else | |
| { | |
| AppenderSkeleton::setOption(option, value); | |
| } | |
| } | |
| void SocketAppenderSkeleton::SocketAppenderSkeletonPriv::fireConnector() | |
| { | |
| std::lock_guard<std::recursive_mutex> lock(this->mutex); | |
| if (this->taskName.empty()) | |
| { | |
| this->taskName = this->name + LOG4CXX_STR(":") | |
| + this->address->toString() + LOG4CXX_STR(":"); | |
| StringHelper::toString(this->port, this->taskName); | |
| } | |
| auto taskManager = ThreadUtility::instancePtr(); | |
| if (!taskManager->value().hasPeriodicTask(this->taskName)) | |
| { | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Waiting ")); | |
| StringHelper::toString(this->reconnectionDelay, msg); | |
| msg += LOG4CXX_STR(" ms before retrying [") | |
| + this->address->toString() + LOG4CXX_STR(":"); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::debug(msg); | |
| } | |
| taskManager->value().addPeriodicTask(this->taskName | |
| , std::bind(&SocketAppenderSkeleton::SocketAppenderSkeletonPriv::retryConnect, this) | |
| , std::chrono::milliseconds(this->reconnectionDelay) | |
| ); | |
| } | |
| this->taskManager = taskManager; | |
| } | |
| void SocketAppenderSkeleton::SocketAppenderSkeletonPriv::retryConnect() | |
| { | |
| if (this->closed) | |
| { | |
| if (auto pManager = this->taskManager.lock()) | |
| pManager->value().removePeriodicTask(this->taskName); | |
| } | |
| else | |
| { | |
| try | |
| { | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Attempting connection to [") | |
| + this->address->toString() + LOG4CXX_STR(":")); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::debug(msg); | |
| } | |
| this->setOutputSink(Socket::create(this->address, this->port, this->socketSubclass)); | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Connection established to [") | |
| + this->address->toString() + LOG4CXX_STR(":")); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::debug(msg); | |
| } | |
| if (auto pManager = this->taskManager.lock()) | |
| pManager->value().removePeriodicTask(this->taskName); | |
| return; | |
| } | |
| catch (ConnectException& e) | |
| { | |
| LogLog::warn(LOG4CXX_STR("Remote host ") | |
| + this->address->toString() | |
| + LOG4CXX_STR(" refused connection."), e); | |
| } | |
| catch (IOException& e) | |
| { | |
| LogString msg(LOG4CXX_STR("Could not connect to [") | |
| + this->address->toString() + LOG4CXX_STR(":")); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::warn(msg, e); | |
| } | |
| if (this->reconnectionDelay > 0) | |
| { | |
| if (LogLog::isDebugEnabled()) | |
| { | |
| LogString msg(LOG4CXX_STR("Waiting ")); | |
| StringHelper::toString(this->reconnectionDelay, msg); | |
| msg += LOG4CXX_STR(" ms before retrying [") | |
| + this->address->toString() + LOG4CXX_STR(":"); | |
| StringHelper::toString(this->port, msg); | |
| msg += LOG4CXX_STR("]."); | |
| LogLog::debug(msg); | |
| } | |
| } | |
| } | |
| } | |
| void SocketAppenderSkeleton::SocketAppenderSkeletonPriv::setOutputSink(const SocketPtr& socket) | |
| { | |
| OutputStreamPtr os = std::make_shared<SocketOutputStream>(socket); | |
| auto charset = CharsetEncoder::getUTF8Encoder(); | |
| // Serialize with AppenderSkeleton::doAppend | |
| std::lock_guard<std::recursive_mutex> lock(this->mutex); | |
| this->outputSink = std::make_shared<OutputStreamWriter>(os, charset); | |
| } | |
| void SocketAppenderSkeleton::SocketAppenderSkeletonPriv::close() | |
| { | |
| if (this->taskName.empty()) | |
| ; | |
| else if (auto pManager = this->taskManager.lock()) | |
| pManager->value().removePeriodicTask(this->taskName); | |
| std::lock_guard<std::recursive_mutex> lock(this->mutex); | |
| if (this->outputSink) | |
| { | |
| try | |
| { | |
| this->outputSink->close(); | |
| this->outputSink.reset(); | |
| } | |
| catch (std::exception&) | |
| { | |
| } | |
| } | |
| } | |
| void SocketAppenderSkeleton::setRemoteHost(const LogString& host) | |
| { | |
| _priv->address = helpers::InetAddress::getByName(host); | |
| _priv->remoteHost.assign(host); | |
| } | |
| const LogString& SocketAppenderSkeleton::getRemoteHost() const | |
| { | |
| return _priv->remoteHost; | |
| } | |
| void SocketAppenderSkeleton::setPort(int port1) | |
| { | |
| _priv->port = port1; | |
| } | |
| int SocketAppenderSkeleton::getPort() const | |
| { | |
| return _priv->port; | |
| } | |
| void SocketAppenderSkeleton::setLocationInfo(bool locationInfo1) | |
| { | |
| _priv->locationInfo = locationInfo1; | |
| } | |
| bool SocketAppenderSkeleton::getLocationInfo() const | |
| { | |
| return _priv->locationInfo; | |
| } | |
| void SocketAppenderSkeleton::setReconnectionDelay(int reconnectionDelay1) | |
| { | |
| _priv->reconnectionDelay = reconnectionDelay1; | |
| if (_priv->taskName.empty()) | |
| return; | |
| auto pManager = _priv->taskManager.lock(); | |
| if (pManager && pManager->value().hasPeriodicTask(_priv->taskName)) | |
| { | |
| pManager->value().removePeriodicTask(_priv->taskName); | |
| pManager->value().addPeriodicTask(_priv->taskName | |
| , std::bind(&SocketAppenderSkeleton::SocketAppenderSkeletonPriv::retryConnect, _priv) | |
| , std::chrono::milliseconds(_priv->reconnectionDelay) | |
| ); | |
| } | |
| } | |
| int SocketAppenderSkeleton::getReconnectionDelay() const | |
| { | |
| return _priv->reconnectionDelay; | |
| } | |
| void SocketAppenderSkeleton::setSocketSubclass(const LogString& newValue) | |
| { | |
| _priv->socketSubclass = newValue; | |
| } | |
| const LogString& SocketAppenderSkeleton::getSocketSubclass() const | |
| { | |
| return _priv->socketSubclass; | |
| } |