| /* | |
| * 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. | |
| */ | |
| #include <log4cxx/writerappender.h> | |
| #include <log4cxx/helpers/loglog.h> | |
| #include <log4cxx/layout.h> | |
| #include <log4cxx/helpers/stringhelper.h> | |
| #include <log4cxx/private/appenderskeleton_priv.h> | |
| #include <log4cxx/private/writerappender_priv.h> | |
| #include <mutex> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::helpers; | |
| using namespace LOG4CXX_NS::spi; | |
| #define _priv static_cast<WriterAppenderPriv*>(m_priv.get()) | |
| IMPLEMENT_LOG4CXX_OBJECT(WriterAppender) | |
| WriterAppender::WriterAppender() : | |
| AppenderSkeleton (std::make_unique<WriterAppenderPriv>()) | |
| { | |
| } | |
| WriterAppender::WriterAppender(const LayoutPtr& layout, const helpers::WriterPtr& writer) | |
| : AppenderSkeleton(std::make_unique<WriterAppenderPriv>(layout, writer)) | |
| { | |
| activateOptions(); | |
| } | |
| WriterAppender::WriterAppender(std::unique_ptr<WriterAppenderPriv> priv) | |
| : AppenderSkeleton (std::move(priv)) | |
| { | |
| } | |
| WriterAppender::~WriterAppender() | |
| { | |
| if (_priv->setClosed()) | |
| _priv->close(); | |
| } | |
| void WriterAppender::activateOptions( ) | |
| { | |
| _priv->activateOptions(); | |
| } | |
| void WriterAppender::WriterAppenderPriv::activateOptions() | |
| { | |
| if (this->layout == 0) | |
| { | |
| this->errorHandler->error(LOG4CXX_STR("No layout set for the appender named [") | |
| + this->name + LOG4CXX_STR("].")); | |
| } | |
| if (this->writer == 0) | |
| { | |
| this->errorHandler->error(LOG4CXX_STR("No writer set for the appender named [") | |
| + this->name + LOG4CXX_STR("].")); | |
| } | |
| } | |
| void WriterAppender::append( const spi::LoggingEventPtr& event ) | |
| { | |
| if (!checkEntryConditions()) | |
| { | |
| return; | |
| } | |
| subAppend( event ); | |
| } | |
| /** | |
| This method determines if there is a sense in attempting to append. | |
| <p>It checks whether there is a set output target and also if | |
| there is a set layout. If these checks fail, then the boolean | |
| value <code>false</code> is returned. */ | |
| bool WriterAppender::checkEntryConditions() const | |
| { | |
| return _priv->checkWriter() && _priv->checkLayout() && _priv->checkNotClosed(); | |
| } | |
| bool WriterAppender::WriterAppenderPriv::checkWriter() | |
| { | |
| if (this->writer == 0) | |
| { | |
| if (!this->warnedNoWriter) | |
| { | |
| this->errorHandler->error( | |
| LogString(LOG4CXX_STR("No output stream or file set for the appender named [")) + | |
| this->name + LOG4CXX_STR("].")); | |
| this->warnedNoWriter = true; | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| void WriterAppender::close() | |
| { | |
| if (_priv->setClosed()) | |
| _priv->close(); | |
| } | |
| void WriterAppender::WriterAppenderPriv::close() | |
| { | |
| if (this->writer != NULL) | |
| { | |
| try | |
| { | |
| // before closing we have to output out layout's footer | |
| // | |
| // Using the object's pool since this is a one-shot operation | |
| // and pool is likely to be reclaimed soon when appender is destructed. | |
| // | |
| this->writeFooter(); | |
| this->writer->close(); | |
| this->writer = 0; | |
| } | |
| catch (IOException& e) | |
| { | |
| LogLog::warn(LOG4CXX_STR("Could not close writer for WriterAppender named ") + this->name, e); | |
| } | |
| } | |
| } | |
| /** | |
| Returns an OutputStreamWriter when passed an OutputStream. The | |
| encoding used will depend on the value of the | |
| <code>encoding</code> property. If the encoding value is | |
| specified incorrectly the writer will be opened using the default | |
| system encoding (an error message will be printed to the loglog. */ | |
| WriterPtr WriterAppender::createWriter(const OutputStreamPtr& os) | |
| { | |
| LogString enc(getEncoding()); | |
| CharsetEncoderPtr encoder; | |
| if (enc.empty()) | |
| { | |
| encoder = CharsetEncoder::getDefaultEncoder(); | |
| } | |
| else | |
| { | |
| if (StringHelper::equalsIgnoreCase(enc, | |
| LOG4CXX_STR("utf-16"), LOG4CXX_STR("UTF-16"))) | |
| { | |
| encoder = CharsetEncoder::getEncoder(LOG4CXX_STR("UTF-16BE")); | |
| } | |
| else | |
| { | |
| encoder = CharsetEncoder::getEncoder(enc); | |
| } | |
| if (encoder == NULL) | |
| { | |
| encoder = CharsetEncoder::getDefaultEncoder(); | |
| LogLog::warn(LOG4CXX_STR("Error initializing output writer.")); | |
| LogLog::warn(LOG4CXX_STR("Unsupported encoding?")); | |
| } | |
| } | |
| return std::make_shared<OutputStreamWriter>(os, encoder); | |
| } | |
| LogString WriterAppender::getEncoding() const | |
| { | |
| return _priv->encoding; | |
| } | |
| void WriterAppender::setEncoding(const LogString& enc) | |
| { | |
| _priv->encoding = enc; | |
| } | |
| void WriterAppender::subAppend( const spi::LoggingEventPtr& event ) | |
| { | |
| #if ENABLE_FAILING_APPENDER_SIMULATION_TESTING | |
| if (event->getRenderedMessage() == _priv->exceptionTriggeringMessage) | |
| throw RuntimeException(LOG4CXX_STR("Simulated fault")); | |
| #endif | |
| LogString msg; | |
| _priv->layout->format(msg, event); | |
| if (_priv->writer != NULL) | |
| { | |
| _priv->writer->write(msg); | |
| if (_priv->immediateFlush) | |
| { | |
| _priv->writer->flush(); | |
| } | |
| } | |
| } | |
| void WriterAppender::WriterAppenderPriv::writeFooter() | |
| { | |
| if (this->layout != NULL) | |
| { | |
| LogString foot; | |
| this->layout->appendFooter(foot); | |
| this->writer->write(foot); | |
| } | |
| } | |
| void WriterAppender::WriterAppenderPriv::writeHeader() | |
| { | |
| if (this->layout != NULL) | |
| { | |
| LogString header; | |
| this->layout->appendHeader(header); | |
| this->writer->write(header); | |
| } | |
| } | |
| bool WriterAppender::requiresLayout() const | |
| { | |
| return true; | |
| } | |
| void WriterAppender::setOption(const LogString& option, const LogString& value) | |
| { | |
| if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("ENCODING"), LOG4CXX_STR("encoding"))) | |
| { | |
| setEncoding(value); | |
| } | |
| else | |
| { | |
| AppenderSkeleton::setOption(option, value); | |
| } | |
| } | |
| void WriterAppender::setImmediateFlush(bool value) | |
| { | |
| _priv->immediateFlush = value; | |
| } | |
| bool WriterAppender::getImmediateFlush() const | |
| { | |
| return _priv->immediateFlush; | |
| } | |