Files
interactive/rcts/rc_mngd/lib/Logger.cpp
T
2026-08-07 17:38:18 +09:00

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;
}