C++类设计:一个不同版本的日志类(完整源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。

 


        如何设计日志类请看:C++类设计:设计一个日志类(源码)_初级代码游戏的博客-CSDN博客        

        这个版本有些不同,支持输出到编辑框、文件和VS的输出窗口,基本结构完全相同。

目录

一、效果

二、完整源码

三、详解

3.1 输出到VS输出窗口

3.2 输出到编辑框

3.3 输出到文件


一、效果

        VS2017,MFC,UNICODE字符集。

        输出到编辑框:

        自动向下滚动,编辑框必须是多行的。

         输出到VS输出窗口(调试模式):

二、完整源码

        头文件:

//common.h

#pragma once
#include <sstream>
#include <map>
using namespace std;

extern CEdit* pGlobalLogWnd;

class CStringConvert
{
public:
	static string WStringToString(wstring const& wstr)
	{
		size_t buflen = wstr.size() * sizeof(wchar_t) + 1;
		char* buf = new char[buflen];
		WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, buf, (int)buflen, NULL, 0);
		string ret = buf;
		delete[] buf;
		return ret;
	}
	static wstring StringToWString(string const& str)
	{
		//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
		int iSize = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
		wchar_t* buf = new wchar_t[iSize * sizeof(wchar_t)];
		MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buf, iSize);
		wstring ret = buf;
		delete[] buf;
		return ret;
	}
};

class CLog
{
private:
	HANDLE hFile = 0;//输出文件

	stringstream m_buf;//一条信息
	CString m_file;//产生日志的文件名
	int m_line;//产生日志的行号

	int m_limit_text = UINT_MAX;//最大长度,编辑框的默认值大概是几十K,需要设置得更大
	
	void end(char const* logtype)
	{
		CString str;

		//格式化信息
		str += logtype;
		str += ":";
		str += m_buf.str().c_str();
		if (m_file.GetLength() > 0)
		{
			CString line;
			line.Format(TEXT("%d"), m_line);
			str += TEXT(" [From ") + m_file + TEXT(":") + line + TEXT("]");
		}
		str += "\r\n";

		//输出到VS信息窗口
		OutputDebugString(str);

		//输出到文件
		if (hFile)
		{
			DWORD dwCount;
			string mbcs_string = CStringConvert::WStringToString(str.GetString());
			WriteFile(hFile, mbcs_string.c_str(), (DWORD)mbcs_string.size(), &dwCount, 0);
		}
		
		//输出到编辑框
		if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
		{
			pGlobalLogWnd->SetLimitText(m_limit_text);
			while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
			{
				//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
				//pGlobalLogWnd->ReplaceSel(TEXT(""));
				//mag_size.erase(mag_size.begin());
				pGlobalLogWnd->SetWindowText(TEXT(""));
			}

			int nLength = pGlobalLogWnd->GetWindowTextLength();
			pGlobalLogWnd->SetSel(nLength, nLength);
			pGlobalLogWnd->ReplaceSel(str);
			pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
		}
		else
		{
			OutputDebugString(TEXT("日志窗口失效\r\n"));
		}
		
		//清除缓存的信息
		m_buf.str("");
		m_file = "";
		m_line = 0;
	}
public:
	CLog()
	{
		wchar_t pszPathName[2048];
		GetModuleFileName(NULL, pszPathName, 2048);
		StrCat(pszPathName, TEXT(".log"));
		hFile = CreateFile(pszPathName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL
			, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
		if (INVALID_HANDLE_VALUE == hFile)
		{
			MessageBox(NULL,TEXT("创建或打开日志文件失败"),TEXT("出错"),0);
		}
	}
	~CLog()
	{
		if (hFile)CloseHandle(hFile);
	}
	void set_LimitText(int n) { m_limit_text = n; }
	struct CLog_ende
	{};
	struct CLog_endi
	{};
	struct CLog_endd
	{};
	CLog& SetPos(char const * file, int line)
	{
		m_file = file;
		m_line = line;
		return *this;
	}
	template<typename T>
	CLog& operator<<(T const& data)
	{
		m_buf << data;
		return *this;
	}
	CLog& operator<<(CLog_ende const& data)
	{
		end("错误");
		return *this;
	}
	CLog& operator<<(CLog_endi const& data)
	{
		end("信息");
		return *this;
	}
	CLog& operator<<(CLog_endd const& data)
	{
		end("调试");
		return *this;
	}
	CLog& operator<<(CString const& data)
	{
		return operator<<(data.GetString());
	}
	CLog& operator<<(wchar_t const* data)
	{
		return operator<<(CStringConvert::WStringToString(wstring(data)));
	}
	CLog& operator<<(RECT const& data)
	{
		return *this << "{" << data.left << "," << data.top << "," << data.right << "," << data.bottom << "}";
	}
};

extern bool G_IS_DEBUG;
extern CLog gloablLog;
#define thelog (gloablLog.SetPos( __FILE__ , __LINE__))
#define debuglog if(G_IS_DEBUG)gloablLog.SetPos( __FILE__ , __LINE__)
extern CLog::CLog_ende ende;
extern CLog::CLog_endi endi;
extern CLog::CLog_endd endd;

        源文件:

#include "pch.h"
#include "common.h"

CEdit* pGlobalLogWnd = nullptr;

bool G_IS_DEBUG = false;
CLog gloablLog;
CLog::CLog_ende ende;
CLog::CLog_endi endi;
CLog::CLog_endd endd;

        使用:

	//设置编辑框最大长度
    gloablLog.set_LimitText(1024 * 1024);
    //设置编辑框指针,不设置则不输出到编辑框
	pGlobalLogWnd = &this->m_LogWnd;
    //打开调试开关,否则debuglog不输出
	G_IS_DEBUG = true;

    //输入出日志:
    thelog<<a<<b<<c<<endi;
    thelog<<a<<b<<c<<ende;
    thelog<<a<<b<<c<<endd;
    debuglog<<a<<b<<c<<endi;
    debuglog<<a<<b<<c<<ende;
    debuglog<<a<<b<<c<<endd;

三、详解

3.1 输出到VS输出窗口

        在调试模式下可以直接输出到VS的输出窗口,使用如下函数即可:

debugapi.h
Kernel32.lib/Kernel32.dll

void OutputDebugStringA(
  [in, optional] LPCSTR lpOutputString
);

        不过如果不是调试模式就没用了。

3.2 输出到编辑框

        图形界面程序完全可以自带日志窗口,这里用了一个编辑框CEdit。用作日志的编辑框需要做一些设置:

  • 多行
  • 滚动条
  • 最大字符数大一些
  • 自动滚动

        前两个在窗体设计器设置即可,后两个靠代码:

		//输出到编辑框
		if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
		{
			pGlobalLogWnd->SetLimitText(m_limit_text);
			while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
			{
				//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
				//pGlobalLogWnd->ReplaceSel(TEXT(""));
				//mag_size.erase(mag_size.begin());
				pGlobalLogWnd->SetWindowText(TEXT(""));
			}

			int nLength = pGlobalLogWnd->GetWindowTextLength();
			pGlobalLogWnd->SetSel(nLength, nLength);
			pGlobalLogWnd->ReplaceSel(str);
			pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
		}

        编辑框的修改要用ReplaceSel,不能用SetWindowText,会严重闪烁。

3.3 输出到文件

        输出到文件没什么好解释了,要注意的就是输出文件的编码,尽量用utf-8,兼容性比较好。如果输出宽字符,用记事本打开可能乱码(或许是没有添加BOM的原因,总之utf-8最简单啦)。


(这里是结束)

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
////////////////////////////////////////////README//////////////////////////////////////////////////// // DebugLog--write some log for debug // //初衷: 版本开发的时候,有些功能点是可以项目无关的,比如是解析某个文件;开发完成之后,测试的话, // // 需要整改项目程序运行、测试,功能点的触发条件比较麻烦。所以,有的时候,对于这样的功能点, // // 我会选择进行本地开发,然后再合到项目中去. // // 但是,每次功能点开发的时候,一般用的是printf直接打屏,但是项目中用的是写文件,所以移植 // // 的时候还需要进行不少改动. // // 于是,我就想自己写个log,可以与项目的日志函数进行适配。那么,上面的问题可以得到比较好的 // // 解决(或者说是规避)。 // //附注:我不知道这样做有没有意义,也就是根据自己的经验,试着做了下。经验不足,水平有限,肯定会 // // 导致设计不足,不对,甚至是整个设计没有意义。如果是这样,希望能不吝交流、赐教. // // EMail: hu__haifeng@163.com // ////////////////////////////////////////////////////////////////////////////////////////////////////// //Version :0.5 // //Description:该版本打造了基本的架构,并且实现了不带参数的打印日志(仅仅是字符串),可以进行监督的测试.// // 由于我项目经验有限,仅仅见过两种不同的项目日志分隔,所以也只能以此为参考,不一定可以 // // 适配其他项目日志函数. // //Adapter :可以用宏定义来进行适配。比如: // // #ifdef __CLOG_H__ // // #define DEBUG(VAR) mylog.Print(__FILE__, __LINE__, VAR, LOG_DEBUG) // // #else // // #define DEBUG(VAR) function //项目用的标准的日志函数. // // #endif // // // //Plan :1.0版本写成可以带参数的日志。2.0版本实现客户端、服务器版本(可以支持不同的进程写日志)。// //////////////////////////////////////////////////////////////////////////////////////////////////////
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值