c++ builder > "my" ErrorLoggerUnit / Usage

This article is for 
http://stackoverflow.com/questions/35251103/how-to-capture-suddenly-destructed-tthread?noredirect=1#comment58234908_35251103

## Environment

```txt:implemented_on
RadStudio XE4 using C++ on  Windows 7pro
```

```txt: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();
```