This article is for
http://stackoverflow.com/questions/35251103/how-to-capture-suddenly-destructed-tthread?noredirect=1#comment58234908_35251103
Environment
implemented_on
RadStudio XE4 using C++ on Windows 7pro
working_on
Windows 7 Embedded (English)
header
ErrorLoggerUnit.h
//---------------------------------------------------------------------------
/*
2016 Feb. 08
Translated comments from my native languate to English
to share with StackExchange
*/
#ifndef ErrorLoggerUnitH
#define ErrorLoggerUnitH
#include <System.hpp> // for String
#include <System.Classes.hpp> // for TList
class TErrorLogger {
public:
enum ErrorLevel_e {
High = 0, // Critical
Middle, // Such as retry
Low,
Debug, // debug
Info // Not Error, but corresponding to mtInformation
};
private:
struct ErrorInfo_t {
TDateTime datetime;
ErrorLevel_e errorLevel;
String summary;
String detail;
String funcPlace; // function name and index
};
int m_maxNumRecords; // Maximum record number
TList *m_list; // Store ErrorInfo_t
String m_filepath; // Save destination
bool m_debugMode; // false: if(errorLevel==Debug) {will not Add()}
String __fastcall getErrorLevelString(int errorLevel);
String __fastcall getTitleString(); // items of getDataString
String __fastcall getDataString(int idx); // return ErrorInfo_t strings
protected:
public:
void __fastcall Add(int errorLevel_, String summary_, String details_, String funcPlace_);
int __fastcall Save(); // return number of records / -1:error
int __fastcall Save(String filename);
void __fastcall Clear();
// test functions
static int __fastcall Test_run_through();
__fastcall TErrorLogger(String filepath, int maxNum, bool debugMode);
__fastcall ~TErrorLogger();
};
#endif // ErrorLoggerUnitH
source (.cpp)
ErrorLoggerUnit.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include <memory> // for unique_ptr
#include "ErrorLoggerUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
/*
v0.13 2016 Feb. 03
...
*/
//#define DEBUG_DO_PRINT // for debug print
static const String errorLevelStr[] = {
L"High", L"Middle", L"Low", L"Debug", L"Info"
};
int errLevelSize = sizeof(errorLevelStr) / sizeof(errorLevelStr[0]);
static void debug_outputDebugString(String prefix, String msg)
{
// Used to comment out all debug print easily
#ifdef DEBUG_DO_PRINT
String work = L"ErrorLoggerUnit >" + prefix + L": " + msg;
OutputDebugString(work.c_str()); // @ debug_outputDebugString
#endif
}
__fastcall TErrorLogger::TErrorLogger(String filepath, int maxNum, bool debugMode)
{
m_maxNumRecords = maxNum;
m_filepath = filepath;
m_list = new TList();
m_debugMode = debugMode;
}
__fastcall TErrorLogger::~TErrorLogger()
{
if (this == NULL) {
return; // error
}
for(int idx=0; idx < m_list->Count; idx++) {
delete m_list->Items[idx];
}
if (m_list != NULL) {
delete m_list;
m_list = NULL;
}
}
void __fastcall TErrorLogger::Add(int errorLevel_, String summary_, String details_, String funcPlace_)
{
if (this == NULL) {
String msg = L"ERR > TErrorLogger variable not initialized";
debug_outputDebugString(L"Add", msg.c_str());
return; // error
}
if (m_debugMode == false && errorLevel_ == TErrorLogger::Debug) {
return; // will not Add in debug
}
if (m_list == NULL) {
m_list = new TList();
}
if (m_list->Count >= m_maxNumRecords) { // Ring Buffer
m_list->Delete(0);
}
ErrorInfo_t *info = new ErrorInfo_t();
info->datetime = Now();
info->errorLevel = (ErrorLevel_e)errorLevel_;
info->summary = summary_;
info->detail = details_;
info->funcPlace = funcPlace_;
m_list->Add(info);
}
String __fastcall TErrorLogger::getTitleString()
{
if (this == NULL) {
return L""; // error
}
String res =
L"yy/mm/dd,hh:nn:ss"
L",level"
L",summary"
L",detail"
L",function:index";
return res;
}
String __fastcall TErrorLogger::getDataString(int idx)
{
if (this == NULL) {
return L""; // error
}
ErrorInfo_t *info = (ErrorInfo_t *)(m_list->Items[idx]);
if (idx < 0 || idx >= m_list->Count) {
return L""; // error
}
String str;
str = info->datetime.FormatString(L"yy/mm/dd,hh:nn:ss");
str = str + L"," + IntToStr(info->errorLevel)
+ getErrorLevelString(info->errorLevel);
str = str + L"," + info->summary;
str = str + L"," + info->detail;
str = str + L"," + info->funcPlace;
return str;
}
String __fastcall TErrorLogger::getErrorLevelString(int errorLevel)
{
if (this == NULL) {
return L""; // error
}
if (errorLevel < 0 || errorLevel >= errLevelSize) {
return L"";
}
return L"(" + errorLevelStr[errorLevel] + L")";
}
int __fastcall TErrorLogger::Save() {
if (this == NULL) {
return -1;
}
return Save(m_filepath);
}
int __fastcall TErrorLogger::Save(String filename)
{
if (this == NULL) {
return -1; // error
}
if (filename.Length() == 0) {
return -1; // error
}
std::unique_ptr<TStringList> line(new TStringList);
String dataStr;
String title = getTitleString();
line->Add(title);
debug_outputDebugString(L"Save", title.c_str());
for(int idx=0; idx < m_list->Count; idx++) {
dataStr = getDataString(idx);
line->Add(dataStr);
}
try {
line->SaveToFile(filename);
} catch (...) {
}
return line->Count;
}
/* static */int __fastcall TErrorLogger::Test_run_through()
{
TErrorLogger *testError = new TErrorLogger(L"log.txt", /* maxNum=*/ 1000, /*debugMode=*/true);
for(int loop=0; loop<3; loop++) {
testError->Add(TErrorLogger::High, L"UDP:Summary1", L"Detail1", L"funcA:idx1");
testError->Add(TErrorLogger::Middle, L"UDP:Summary2", L"Detail2", L"funcA:idx2");
testError->Add(TErrorLogger::High, L"PC:Summary1", L"Detail3", L"funcB:idx1");
testError->Add(TErrorLogger::Low, L"PC:Summary2", L"Detail4", L"funcAdd:idx2");
testError->Add(TErrorLogger::High, L"PC:Summary3", L"Detail5", L"funcRemove:idx3");
testError->Add(TErrorLogger::Debug, L"Debug:Hello world", L"Detail5", L"");
testError->Add(TErrorLogger::Info, L"Not problem but", L"This is not a problem", L"");
}
int numRecord;
numRecord = testError->Save();
String msg = L"Saved " + IntToStr(numRecord) + L" records";
debug_outputDebugString(L"Test_run_through", msg.c_str() );
testError->Clear();
numRecord = testError->Save(L"log2.txt");
msg = L"Saved " + IntToStr(numRecord) + L" records";
debug_outputDebugString(L"Test_run_through", msg.c_str() );
delete testError;
return numRecord;
}
void __fastcall TErrorLogger::Clear()
{
if (this == NULL) { // new TErrorLogger()してない
return; // error
}
for(int idx=0; idx < m_list->Count; idx++) {
delete m_list->Items[idx];
}
m_list->Clear();
}
Usage
See Test_run_through() for example usage.
instantiate
TErrorLogger *m_errLog;
...
m_errLog = new TErrorLogger(kErrorLogFolder + L"\\" + kErrorLogFileName, kErrorLog_maxNumRecords, /* debugMode=*/false);
...
logging
m_errLog->Add(TErrorLogger::Info, L"ctor", L"instance created", L"");
m_errLog->Save();