488 lines
10 KiB
C++
488 lines
10 KiB
C++
/***************************************************************************
|
|
Logger.cpp
|
|
-----------------------------------------
|
|
begin : 2011/10/26
|
|
copyright : (C) 2005 SolutionBox Inc.
|
|
author : Service 1 Team
|
|
email : svc1@solbox.com
|
|
version : 1.0
|
|
|
|
CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
|
|
Redistribution and use in source and binary forms, with or with out
|
|
modification, are not permitted in outside of SolutionBox Inc.
|
|
***************************************************************************/
|
|
#include "Logger.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <iostream>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
|
#define DEFAULT_LOG_LEVEL LINF
|
|
|
|
using namespace std;
|
|
|
|
CLogger* CLogger::m_pInstance = NULL;
|
|
bool CLogger::m_bIsInitialized = false;
|
|
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
CLogger::CLogger( string programName, string logDir, int logLevel )
|
|
{
|
|
m_szProgramName = programName;
|
|
m_szLogDir = logDir;
|
|
|
|
if( IsValidLogLevel( logLevel ) == true )
|
|
{
|
|
m_nLogLevel = logLevel;
|
|
}
|
|
else
|
|
{
|
|
m_nLogLevel = DEFAULT_LOG_LEVEL;
|
|
}
|
|
|
|
MakeLogLevelString();
|
|
}
|
|
|
|
void CLogger::MakeLogLevelString()
|
|
{
|
|
m_vectorLogLevelString.push_back( "EMR" );
|
|
m_vectorLogLevelString.push_back( "ALT" );
|
|
m_vectorLogLevelString.push_back( "CRT" );
|
|
m_vectorLogLevelString.push_back( "ERR" );
|
|
m_vectorLogLevelString.push_back( "WAR" );
|
|
m_vectorLogLevelString.push_back( "NOT" );
|
|
m_vectorLogLevelString.push_back( "INF" );
|
|
m_vectorLogLevelString.push_back( "DBG" );
|
|
m_vectorLogLevelString.push_back( "DEV" );
|
|
m_vectorLogLevelString.push_back( "DEV1" );
|
|
m_vectorLogLevelString.push_back( "DEV2" );
|
|
}
|
|
|
|
CLogger::~CLogger()
|
|
{
|
|
m_vectorLogLevelString.clear();
|
|
}
|
|
|
|
bool CLogger::Init( string programName, string logDir, int logLevel )
|
|
{
|
|
if( m_bIsInitialized == true )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
pthread_mutex_lock( &m_mutex );
|
|
|
|
// 변수 유효성 검사.
|
|
if( programName.empty() == true || logDir.empty() == true )
|
|
{
|
|
pthread_mutex_unlock( &m_mutex );
|
|
return false;
|
|
}
|
|
|
|
if( IsValidLogLevel( logLevel ) == false )
|
|
{
|
|
pthread_mutex_unlock( &m_mutex );
|
|
return false;
|
|
}
|
|
|
|
if( MakeLogDir( programName, logDir ) == false )
|
|
{
|
|
pthread_mutex_unlock( &m_mutex );
|
|
return false;
|
|
}
|
|
|
|
if( CLogger::m_pInstance != NULL )
|
|
{
|
|
delete CLogger::m_pInstance;
|
|
CLogger::m_pInstance = NULL;
|
|
}
|
|
|
|
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
|
|
m_bIsInitialized = true;
|
|
|
|
pthread_mutex_unlock( &m_mutex );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CLogger::IsValidLogLevel( int logLevel )
|
|
{
|
|
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
|
|
}
|
|
|
|
bool CLogger::SetLogLevel( int logLevel )
|
|
{
|
|
if( IsValidLogLevel( logLevel ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_nLogLevel = logLevel;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CLogger::MakeLogDir( string programName, string logDir )
|
|
{
|
|
string path = logDir + "/" + programName;
|
|
|
|
struct stat dirStat;
|
|
|
|
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
|
if( lstat( path.c_str(), &dirStat ) == 0 )
|
|
{
|
|
// 해당 이름이 디렉토리인 경우
|
|
if( S_ISDIR( dirStat.st_mode ) == true )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
cerr << "exist file with the same name as log path(" << path << ")." << endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
|
|
string cmd = "mkdir -p " + path;
|
|
system( cmd.c_str() );
|
|
|
|
if( IsDirectory( path ) == false )
|
|
{
|
|
cerr << "can't make log directory. path=" << path << "." << endl;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CLogger::IsDirectory( string path )
|
|
{
|
|
struct stat dirStat;
|
|
|
|
if( lstat ( path.c_str(), &dirStat ) != 0 )
|
|
{
|
|
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
|
return false;
|
|
}
|
|
|
|
// 해당 정보가 Directory 가 아닌 경우
|
|
if( S_ISDIR( dirStat.st_mode ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CLogger::Exit()
|
|
{
|
|
if( m_bIsInitialized == false )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock( &m_mutex );
|
|
|
|
if( CLogger::m_pInstance != NULL )
|
|
{
|
|
delete CLogger::m_pInstance;
|
|
CLogger::m_pInstance = NULL;
|
|
m_bIsInitialized = false;
|
|
}
|
|
|
|
pthread_mutex_unlock( &m_mutex );
|
|
}
|
|
|
|
CLogger* CLogger::GetInstance()
|
|
{
|
|
return CLogger::m_pInstance;
|
|
}
|
|
|
|
string CLogger::GetLogFilename( struct tm &timeNow )
|
|
{
|
|
char timeStr[256];
|
|
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
|
|
|
// Make File Name
|
|
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
|
|
|
|
return filename;
|
|
}
|
|
|
|
bool CLogger::Write( int logLevel, const char * fmt, ...)
|
|
{
|
|
// 가변 인자 처리
|
|
va_list args;
|
|
char buffer[MAX_BUFFER_SIZE];
|
|
va_start( args, fmt );
|
|
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
|
{
|
|
va_end( args );
|
|
return false;
|
|
}
|
|
va_end(args);
|
|
|
|
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
|
|
}
|
|
|
|
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
|
|
{
|
|
// 가변 인자 처리
|
|
va_list args;
|
|
char buffer[MAX_BUFFER_SIZE];
|
|
va_start( args, fmt );
|
|
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
|
{
|
|
va_end( args );
|
|
return false;
|
|
}
|
|
va_end(args);
|
|
|
|
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
|
|
|
|
return false;
|
|
}
|
|
|
|
// CHG 2012-08-16 huibong
|
|
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
|
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
|
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
|
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
|
|
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
|
{
|
|
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// 가변 인자 처리
|
|
va_list args;
|
|
char buffer[MAX_BUFFER_SIZE];
|
|
va_start( args, fmt );
|
|
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
|
{
|
|
va_end( args );
|
|
return false;
|
|
}
|
|
va_end(args);
|
|
|
|
|
|
char className[255];
|
|
string funcPrefix = GetClassName( className, filename );
|
|
funcPrefix += "::" + string( funcname ) + "()";
|
|
|
|
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
|
|
}
|
|
|
|
const char* CLogger::GetClassName( char* className, const char* filename )
|
|
{
|
|
if( className == NULL )
|
|
{
|
|
cout << "[ERROR] The input argument 'className' is NULL." << endl;
|
|
return NULL;
|
|
}
|
|
|
|
if( filename == NULL )
|
|
{
|
|
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
|
|
strcpy( className, "NULL" );
|
|
return className;
|
|
}
|
|
|
|
int filenameSize = strlen( filename );
|
|
memcpy( className, filename, filenameSize );
|
|
|
|
// find end position
|
|
char endCharacter = '.';
|
|
int endPos = filenameSize - 1;
|
|
for( ; endPos > 0; --endPos )
|
|
{
|
|
if( className[endPos] == endCharacter )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( endPos == 0 )
|
|
{
|
|
cout << "[ERROR] endPos=0" << endl;
|
|
strcpy( className, "NULL" );
|
|
return className;
|
|
}
|
|
|
|
className[endPos] = '\0';
|
|
|
|
// find start position
|
|
char startCharacter = '/';
|
|
int startPos = endPos - 1;
|
|
for( ; startPos > 0; --startPos )
|
|
{
|
|
if( className[startPos] == startCharacter )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( startPos != 0 )
|
|
{
|
|
startPos += 1;
|
|
}
|
|
|
|
return (className + startPos );
|
|
}
|
|
|
|
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
|
|
{
|
|
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
|
if( IsValidLogLevel( logLevel ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
|
if( logLevel > m_nLogLevel )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( log == NULL )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get Current Data & Time
|
|
time_t now = time( NULL );
|
|
struct tm timeNow;
|
|
localtime_r( &now, &timeNow );
|
|
|
|
string filename = GetLogFilename( timeNow );
|
|
|
|
// Log file open
|
|
FILE* pFile = NULL;
|
|
pFile = fopen( filename.c_str(), "a+" );
|
|
|
|
if( pFile == NULL )
|
|
{
|
|
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
|
return false;
|
|
}
|
|
|
|
// 시간 정보처리
|
|
char timeStr[256];
|
|
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
|
|
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
|
|
|
// Log Level 문자열 검색
|
|
string szLevel = m_vectorLogLevelString[ logLevel ];
|
|
|
|
switch( logPrefix )
|
|
{
|
|
case PREFIX_DATE:
|
|
/// 형식 예: [15:47:41] [DBG] sample log message.
|
|
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
|
|
break;
|
|
|
|
case PREFIX_FUNCTION:
|
|
if( functionName == NULL )
|
|
{
|
|
fclose( pFile );
|
|
return false;
|
|
}
|
|
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
|
|
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
|
|
break;
|
|
|
|
case PREFIX_NONE:
|
|
/// 형식 예: sample log message.
|
|
fprintf( pFile, "%s\n", log );
|
|
break;
|
|
}
|
|
// Write to log file
|
|
fflush( pFile );
|
|
|
|
// 종료 처리.
|
|
fclose( pFile );
|
|
return true;
|
|
}
|
|
|
|
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
|
|
{
|
|
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
|
if( IsValidLogLevel( logLevel ) == false )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
|
if( logLevel > m_nLogLevel )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( data == NULL )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( size <= 0 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Get Current Data & Time
|
|
time_t now = time( NULL );
|
|
struct tm timeNow;
|
|
localtime_r( &now, &timeNow );
|
|
|
|
string filename = GetLogFilename( timeNow );
|
|
|
|
// Log file open
|
|
FILE* pFile = NULL;
|
|
pFile = fopen( filename.c_str(), "a+" );
|
|
|
|
if( pFile == NULL )
|
|
{
|
|
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
|
return false;
|
|
}
|
|
|
|
for( int i = 0; i < size; ++i )
|
|
{
|
|
fprintf( pFile, "%02X", data[i] );
|
|
|
|
if( i == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( ( (i+1) % 8 ) == 0 )
|
|
{
|
|
fprintf( pFile, " " );
|
|
}
|
|
|
|
if( ( (i+1) % 16 ) == 0 )
|
|
{
|
|
fprintf( pFile, " " );
|
|
}
|
|
|
|
if( ( (i+1) % 32 ) == 0 )
|
|
{
|
|
fprintf( pFile, "\n" );
|
|
}
|
|
}
|
|
fprintf( pFile, "\n" );
|
|
fflush( pFile );
|
|
|
|
// 종료 처리.
|
|
fclose( pFile );
|
|
|
|
return false;
|
|
}
|