blob: 8a049ee8589aae1c030ad3e574abf6da88e49c7d [file] [log] [blame]
// **********************************************************************
// @@@ START COPYRIGHT @@@
//
// 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.
//
// @@@ END COPYRIGHT @@@
// **********************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "CommonLogger.h"
#include <errno.h>
#include <string.h>
#include <string>
#include <stdexcept>
#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/helpers/exception.h>
#define SLASH_S "/"
#define SLASH_C '/'
using namespace log4cxx;
using namespace log4cxx::helpers;
bool gv_commonLoggerInitialized = false;
// **************************************************************************
// Reads configuration file, creates an appender, layout, and categories.
// Attaches layout to the appender and appender to categories.
// Will be called by constructor qms, qmp, qmm, and mxcmp after calling
// setLogFileName().
// Return TRUE if all was well, or FALSE if the config file was not read
// successfully.
// **************************************************************************
bool CommonLogger::initLog4cxx(const char* configFileName, const char* fileSuffix)
{
std::string configPath;
logFolder_ = "";
// Form the config path $TRAF_HOME/conf
char *installPath = getenv("TRAF_HOME");
if (installPath)
{
configPath = installPath;
// Last char may or may not be a slash.
if (configPath[configPath.length()-1] != SLASH_C)
configPath += SLASH_C; // LCOV_EXCL_LINE :nu
configPath += "conf" SLASH_S;
}
else
{
// Environment variable $TRAF_HOME not found.
// use the current directory.
configPath = "";
}
if (configFileName[0] == SLASH_C)
configPath = configFileName;
else
configPath += configFileName;
/*
if (configFileName[0] == SLASH_C)
configPath = configFileName;
else
{
configPath = logFolder_;
configPath += configFileName;
}
*/
try
{
if ( fileSuffix && fileSuffix[0] != '\0' )
{
setenv("TRAFODION_LOG_FILENAME_SUFFIX", fileSuffix, 1);
}
else
{
setenv("TRAFODION_LOG_FILENAME_SUFFIX", "", 1);
}
log4cxx::PropertyConfigurator::configure(configPath.data());
gv_commonLoggerInitialized = true;
return true;
}
//catch (log4cxx::ConfigureFailure e)
catch (Exception e)
{
return false;
}
}
CommonLogger& CommonLogger::instance()
{
static CommonLogger onlyInstance_;
return onlyInstance_;
}
// **************************************************************************
// Is the category set to log DEBUG messages?
// This method should be used when the work of producing the logging text
// is expensive, such as a join graph or an extensive dump.
// **************************************************************************
bool CommonLogger::isCategoryInDebug(std::string &cat)
{
bool result = false;
log4cxx::LoggerPtr myLogger(log4cxx::Logger::getLogger(cat));
if ( myLogger )
{
log4cxx::LevelPtr myLevel = myLogger->getLevel();
if ( myLevel )
{
result = ( myLevel != log4cxx::Level::getOff() && myLevel->isGreaterOrEqual(log4cxx::Level::getDebug()) );
}
}
return result;
}
// **************************************************************************
// The generic message logging method for any message type and length.
// **************************************************************************
void CommonLogger::log1(std::string &cat,
logLevel level,
const char* cmsg,
unsigned int eventId)
{
// Have to use a std::string for the message. If a char* is passed, the
// overload of the various logging functions (debug(), fatal(), etc.) that
// accepts a char* is used, and they all use the char* as a format string.
// This causes any contained % character to be treated as the beginning of
// a format specifier, with ugly consequences.
std::string msg(cmsg);
switch(level)
{
case LL_FATAL:
LOG4CXX_FATAL(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_ERROR:
LOG4CXX_ERROR(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_MVQR_FAIL:
LOG4CXX_ERROR(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_WARN:
LOG4CXX_WARN(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_INFO:
LOG4CXX_INFO(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_DEBUG:
LOG4CXX_DEBUG(log4cxx::Logger::getLogger(cat),msg);
break;
case LL_TRACE:
LOG4CXX_TRACE(log4cxx::Logger::getLogger(cat),msg);
break;
}
}
char* CommonLogger::buildMsgBuffer(std::string &cat,
logLevel level,
const char* logMsgTemplate,
va_list args)
{
// calculate needed bytes to allocate memory from system heap.
int bufferSize = 20049;
int msgSize = 0;
static __thread char *buffer = NULL;
va_list args2;
va_copy(args2, args);
bool secondTry = false;
if (buffer == NULL)
buffer = new char[bufferSize];
// For messages shorter than the initial 20K limit, a single pass through
// the loop will suffice.
// For longer messages, 2 passes will do it. The 2nd pass uses a buffer of the
// size returned by the call to vsnprintf. If a second pass is necessary, we
// pass the copy of the va_list we made above, because the first call changes
// the state of the va_list, and can cause a crash if reused.
while (1)
{
msgSize = vsnprintf(buffer, bufferSize, logMsgTemplate, secondTry ? args2 : args);
// if the initial size of 20K works, then break out from while loop.
if ( msgSize < 0 )
{
// LCOV_EXCL_START :rfi
// Some error happened.
// Report the first 40 chars.
#define SHORTSIZE 40
char smallBuff[SHORTSIZE+5];
memset(smallBuff, 0, SHORTSIZE+5);
if (strlen(logMsgTemplate) <= SHORTSIZE)
strcpy(smallBuff, logMsgTemplate);
else
{
memcpy(smallBuff, logMsgTemplate, SHORTSIZE);
strcpy(smallBuff+SHORTSIZE, "...");
}
char errorMsg[100];
sprintf(errorMsg, "Cannot log full message, error %d returned from vsnprintf()", msgSize);
LOG4CXX_ERROR(log4cxx::Logger::getLogger(cat),errorMsg);
LOG4CXX_ERROR(log4cxx::Logger::getLogger(cat),smallBuff);
return buffer;
}
else if (msgSize < bufferSize)
{
// Buffer is large enough - we're good.
break;
}
else
{
// Buffer is too small, reallocate it and go through the loop again.
bufferSize = msgSize + 1; // Use correct size now.
delete [] buffer;
buffer = new char[bufferSize];
secondTry = true;
}
}
va_end(args2);
return buffer;
}
//
// **************************************************************************
// The generic message logging method for any message type and length.
// **************************************************************************
void CommonLogger::log(std::string &cat,
logLevel level,
const char* logMsgTemplate...)
{
// Don't do anything if this message will be ignored anyway.
log4cxx::LoggerPtr myLogger(log4cxx::Logger::getLogger(cat));
if ( myLogger )
{
log4cxx::LevelPtr myLevel = myLogger->getLevel();
log4cxx::LevelPtr paramLevel = log4cxx::Level::toLevel(level);
if ( myLevel && paramLevel )
{
// if no logging - return; if configured logging is more restrictive
// (greater than) the requested level - return
if ( myLevel == log4cxx::Level::getOff() || myLevel->toInt() > paramLevel->toInt() )
{
return;
}
}
}
va_list args ;
va_start(args, logMsgTemplate);
char* buffer = buildMsgBuffer(cat, level, logMsgTemplate, args);
log1(cat, level, buffer);
va_end(args);
}