| /* |
| * 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/db/dbappender.h> |
| #include <log4cxx/appenderskeleton.h> |
| #include <log4cxx/helpers/stringhelper.h> |
| #include <log4cxx/helpers/pool.h> |
| #include <log4cxx/helpers/loglog.h> |
| #include <log4cxx/patternlayout.h> |
| #include <log4cxx/helpers/transcoder.h> |
| #include <log4cxx/pattern/loggerpatternconverter.h> |
| #include <log4cxx/pattern/classnamepatternconverter.h> |
| #include <log4cxx/pattern/datepatternconverter.h> |
| #include <log4cxx/pattern/filelocationpatternconverter.h> |
| #include <log4cxx/pattern/fulllocationpatternconverter.h> |
| #include <log4cxx/pattern/shortfilelocationpatternconverter.h> |
| #include <log4cxx/pattern/linelocationpatternconverter.h> |
| #include <log4cxx/pattern/messagepatternconverter.h> |
| #include <log4cxx/pattern/methodlocationpatternconverter.h> |
| #include <log4cxx/pattern/levelpatternconverter.h> |
| #include <log4cxx/pattern/threadpatternconverter.h> |
| #include <log4cxx/pattern/threadusernamepatternconverter.h> |
| #include <log4cxx/pattern/ndcpatternconverter.h> |
| #include <log4cxx/private/appenderskeleton_priv.h> |
| #include <apr_dbd.h> |
| #include <assert.h> |
| |
| using namespace LOG4CXX_NS; |
| using namespace LOG4CXX_NS::helpers; |
| using namespace LOG4CXX_NS::db; |
| using namespace LOG4CXX_NS::spi; |
| using namespace LOG4CXX_NS::pattern; |
| |
| IMPLEMENT_LOG4CXX_OBJECT(DBAppender) |
| |
| #define _priv static_cast<DBAppenderPriv*>(m_priv.get()) |
| |
| struct DBAppender::DBAppenderPriv : public AppenderSkeleton::AppenderSkeletonPrivate |
| { |
| DBAppenderPriv() : |
| AppenderSkeletonPrivate() |
| { |
| static bool initialized = false; |
| if (!initialized) |
| { |
| initialized = true; |
| apr_status_t stat = apr_dbd_init(m_pool.getAPRPool()); |
| assert(stat == APR_SUCCESS); |
| } |
| } |
| |
| apr_dbd_driver_t* m_driver = nullptr; |
| apr_dbd_t* m_databaseHandle = nullptr; |
| apr_dbd_prepared_t* preparedStmt = nullptr; |
| std::vector<LogString> mappedName; |
| std::string driverName; |
| std::string driverParams; |
| std::string databaseName; |
| std::string sqlStatement; |
| Pool m_pool; |
| std::vector<pattern::LoggingEventPatternConverterPtr> converters; |
| }; |
| |
| #define RULES_PUT(spec, cls) \ |
| specs.insert(PatternMap::value_type(LogString(LOG4CXX_STR(spec)), cls ::newInstance)) |
| |
| static PatternMap getFormatSpecifiers() |
| { |
| PatternMap specs; |
| if (specs.empty()) |
| { |
| RULES_PUT("logger", LoggerPatternConverter); |
| RULES_PUT("class", ClassNamePatternConverter); |
| RULES_PUT("time", DatePatternConverter); |
| RULES_PUT("shortfilename", ShortFileLocationPatternConverter); |
| RULES_PUT("fullfilename", FileLocationPatternConverter); |
| RULES_PUT("location", FullLocationPatternConverter); |
| RULES_PUT("line", LineLocationPatternConverter); |
| RULES_PUT("message", MessagePatternConverter); |
| RULES_PUT("method", MethodLocationPatternConverter); |
| RULES_PUT("level", LevelPatternConverter); |
| RULES_PUT("thread", ThreadPatternConverter); |
| RULES_PUT("threadname", ThreadUsernamePatternConverter); |
| RULES_PUT("ndc", NDCPatternConverter); |
| } |
| return specs; |
| } |
| |
| DBAppender::DBAppender() |
| : AppenderSkeleton (std::make_unique<DBAppenderPriv>()) |
| { |
| } |
| |
| DBAppender::~DBAppender() |
| { |
| close(); |
| } |
| |
| void DBAppender::close(){ |
| if(_priv->m_driver && _priv->m_databaseHandle){ |
| apr_dbd_close(_priv->m_driver, _priv->m_databaseHandle); |
| } |
| _priv->m_driver = nullptr; |
| _priv->m_databaseHandle = nullptr; |
| } |
| |
| void DBAppender::setOption(const LogString& option, const LogString& value){ |
| if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("COLUMNMAPPING"), LOG4CXX_STR("columnmapping"))) |
| { |
| _priv->mappedName.push_back(value); |
| } |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("DRIVERNAME"), LOG4CXX_STR("drivername"))) |
| { |
| Transcoder::encodeUTF8(value, _priv->driverName); |
| } |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("DRIVERPARAMS"), LOG4CXX_STR("driverparams"))) |
| { |
| Transcoder::encodeUTF8(value, _priv->driverParams); |
| } |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("DATABASENAME"), LOG4CXX_STR("databasename"))) |
| { |
| Transcoder::encodeUTF8(value, _priv->databaseName); |
| } |
| else if (StringHelper::equalsIgnoreCase(option, LOG4CXX_STR("SQL"), LOG4CXX_STR("sql"))) |
| { |
| Transcoder::encodeUTF8(value, _priv->sqlStatement); |
| } |
| else |
| { |
| AppenderSkeleton::setOption(option, value); |
| } |
| } |
| |
| void DBAppender::activateOptions(helpers::Pool& p){ |
| apr_status_t stat = apr_dbd_get_driver(_priv->m_pool.getAPRPool(), |
| _priv->driverName.c_str(), |
| const_cast<const apr_dbd_driver_t**>(&_priv->m_driver)); |
| |
| if(stat != APR_SUCCESS){ |
| LogString errMsg = LOG4CXX_STR("Unable to get driver named "); |
| LOG4CXX_DECODE_CHAR(driverName, _priv->driverName); |
| errMsg.append(driverName); |
| LogLog::error(errMsg); |
| _priv->errorHandler->error(errMsg); |
| return; |
| } |
| |
| stat = apr_dbd_open(_priv->m_driver, |
| _priv->m_pool.getAPRPool(), |
| _priv->driverParams.c_str(), |
| &_priv->m_databaseHandle); |
| if(stat != APR_SUCCESS){ |
| LogLog::error(LOG4CXX_STR("Unable to open database")); |
| _priv->errorHandler->error(LOG4CXX_STR("Unable to open database")); |
| return; |
| } |
| |
| if(!_priv->databaseName.empty()){ |
| apr_dbd_set_dbname(_priv->m_driver, |
| _priv->m_pool.getAPRPool(), |
| _priv->m_databaseHandle, |
| _priv->databaseName.c_str()); |
| } |
| |
| stat = apr_dbd_prepare(_priv->m_driver, |
| _priv->m_pool.getAPRPool(), |
| _priv->m_databaseHandle, |
| _priv->sqlStatement.c_str(), |
| "log_insert", |
| &_priv->preparedStmt); |
| if(stat != APR_SUCCESS){ |
| LogString error = LOG4CXX_STR("Unable to prepare statement: "); |
| std::string dbdErr(apr_dbd_error(_priv->m_driver, _priv->m_databaseHandle, stat)); |
| LOG4CXX_DECODE_CHAR(dbdErrLS, dbdErr); |
| error.append(dbdErrLS); |
| LogLog::error(error); |
| _priv->errorHandler->error(error); |
| return; |
| } |
| |
| auto specs = getFormatSpecifiers(); |
| for (auto& name : _priv->mappedName) |
| { |
| auto pItem = specs.find(StringHelper::toLowerCase(name)); |
| if (specs.end() == pItem) |
| LogLog::error(name + LOG4CXX_STR(" is not a supported ColumnMapping value")); |
| else |
| { |
| std::vector<LogString> options; |
| if (LOG4CXX_STR("time") == pItem->first) |
| options.push_back(LOG4CXX_STR("yyyy-MM-ddTHH:mm:ss.SSS")); |
| pattern::LoggingEventPatternConverterPtr converter = LOG4CXX_NS::cast<LoggingEventPatternConverter>((pItem->second)(options)); |
| _priv->converters.push_back(converter); |
| } |
| } |
| } |
| |
| void DBAppender::append(const spi::LoggingEventPtr& event, helpers::Pool& p){ |
| std::vector<std::string> ls_args; |
| std::vector<const char*> args; |
| int stat; |
| int num_rows; |
| |
| if(_priv->m_driver == nullptr || |
| _priv->m_databaseHandle == nullptr || |
| _priv->preparedStmt == nullptr){ |
| _priv->errorHandler->error(LOG4CXX_STR("DBAppender not initialized properly: logging not available")); |
| return; |
| } |
| |
| for(auto& converter : _priv->converters){ |
| LogString str_data; |
| converter->format(event, str_data, p); |
| LOG4CXX_ENCODE_CHAR(new_str_data, str_data); |
| ls_args.push_back(new_str_data); |
| } |
| |
| for(std::string& str : ls_args){ |
| args.push_back(str.data()); |
| } |
| args.push_back(nullptr); |
| |
| stat = apr_dbd_pquery(_priv->m_driver, |
| _priv->m_pool.getAPRPool(), |
| _priv->m_databaseHandle, |
| &num_rows, |
| _priv->preparedStmt, |
| int(args.size()), |
| args.data()); |
| if(stat != APR_SUCCESS){ |
| LogString error = LOG4CXX_STR("Unable to insert: "); |
| LOG4CXX_DECODE_CHAR(local_error, apr_dbd_error(_priv->m_driver, _priv->m_databaseHandle, stat)); |
| error.append(local_error); |
| LogLog::error(error); |
| _priv->errorHandler->error(error); |
| } |
| } |