程序日志模块
将接口封装成函数形式,方便调用
适用于一些简单的程序
#pragma once
#include <iostream>
#include <fstream>
#include <ctime>
#include <mutex>
#include <boost/date_time.hpp>
#include <boost/filesystem.hpp>
#include "boost/format.hpp"
#include "configfile.h" // 自定义的配置文件
using namespace boost::filesystem;
void log_Init();
void log_Info(const std::string& message);
void log_Warn(const std::string& message);
void log_Error(const std::string& message);
class Logger
{
public:
Logger();
~Logger();
void logInit();
void LogInfo(const std::string& message);
void LogWarn(const std::string& message);
void LogError(const std::string& message);
private:
std::string GetFileName() const;
std::string CurrentTime() const;
void ClearLogs();
void Write(const std::string& message);
bool EnableWrite();
private:
std::ofstream file_;
std::mutex mutex_;
boost::gregorian::date today_; // 今天的日期
int keep_days; // 保存的天数
};
#include "Log.h"
static Logger* pLog = new Logger;
void log_Init()
{
pLog->logInit();
}
void log_Info(const std::string& message)
{
pLog->LogInfo(message);
}
void log_Warn(const std::string& message)
{
pLog->LogWarn(message);
}
void log_Error(const std::string& message)
{
pLog->LogError(message);
}
Logger::Logger()
{
today_ = boost::gregorian::day_clock::local_day();
keep_days = configfile::Instance()->GetSaveLogDay();
ClearLogs();
file_.open(GetFileName(), std::ios::app);
}
Logger::~Logger()
{
if(file_.is_open())
file_.close();
}
void Logger::logInit()
{
//用于标识启动日志模块,方便查看
Write("======================[" + CurrentTime() + "]======================\n");
}
// 打印日志信息
void Logger::LogInfo(const std::string& message)
{
Write("[" + CurrentTime() + "] [info] " + message + "\n");
}
void Logger::LogWarn(const std::string& message)
{
Write("[" + CurrentTime() + "] [warn] " + message + "\n");
}
void Logger::LogError(const std::string& message)
{
Write("[" + CurrentTime() + "] [error] " + message + "\n");
}
// 获取文件名全路径
std::string Logger::GetFileName() const
{
// 获取当前时间
boost::posix_time::ptime time_stamp = boost::posix_time::microsec_clock::local_time();
// 获取年月日信息
boost::gregorian::date d = time_stamp.date();
int year = d.year();
int month = d.month().as_number();
int day = d.day();
// 将年月日时分秒拼接为字符串
std::string name = (boost::format("%04d-%02d-%02d_") % year % month % day).str();
name += "sea.log";
return configfile::Instance()->GetLogPath() + "/" + name;
}
// 获取当前时间
std::string Logger::CurrentTime() const
{
// 获取当前时间
boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
// 格式化输出当前时间
std::ostringstream oss;
oss << boost::posix_time::to_iso_extended_string(now) << "."
<< std::setfill('0') << std::setw(3)
<< (now.time_of_day().fractional_seconds() / 1000);
return oss.str();
}
// 清空过期日志
void Logger::ClearLogs()
{
if(file_.is_open())
file_.close();
std::string path = configfile::Instance()->GetLogPath(); // 获取配置文件中的日志路径
// 检查路径,不存在则创建
if (path.empty())
path = current_path().string() + "/log";
if (!exists(path))
{
create_directory(path);
return;
}
boost::filesystem::path folder_path(configfile::Instance()->GetLogPath());
std::time_t current_time = std::time(nullptr);
std::time_t max_age = keep_days * 24 * 60 * 60; // 计算日志文件最大的存储时间
for (boost::filesystem::directory_iterator itr(folder_path); itr != boost::filesystem::directory_iterator(); ++itr)
{
if (boost::filesystem::is_regular_file(itr->status()))
{
boost::filesystem::path file_path = itr->path();
if ("log" == file_path.extension().string())
{
if ((current_time - boost::filesystem::last_write_time(itr->path())) > max_age)
{
boost::filesystem::remove(itr->path());
}
}
}
}
}
// 判断磁盘剩余空间
bool Logger::EnableWrite()
{
space_info disk_space = space("/");
if (disk_space.available < configfile::Instance()->GetMinResRoom())
return false;
return true;
}
// 写入日志
void Logger::Write(const std::string& message)
{
if (!EnableWrite())
return;
// 加入互斥,防止多线程访问造成崩溃问题
std::lock_guard<std::mutex> lock(mutex_);
if (!file_.is_open())
{
file_.open(GetFileName(), std::ios::app);
if (!file_.is_open())
return;
}
try
{
file_.write(message.c_str(), message.size());
file_.close();
}
catch (std::exception &e)
{
std::cerr << "log error:" << e.what()<<std::endl;
}
boost::gregorian::date today = boost::gregorian::day_clock::local_day();
if (today != today_)
{
ClearLogs();
today_ = today;
}
}
崩溃日志模块
主要通过调用windows接口实现对崩溃信息的抓取
发生崩溃时会在当前路径下创建dump文件夹,在该文件夹下生成一个.dmp文件,再使用visual studio打开,查看崩溃信息
#pragma once
#include <windows.h>
#include <iostream>
#include <string>
#include <locale>
#include <codecvt>
#include <boost/filesystem.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
namespace ysm {
class Debug
{
public:
static LONG WINAPI crashStackCallback(struct _EXCEPTION_POINTERS* exceptionInfo);
};
}
#include "Debug.h"
#include <Dbghelp.h>
#pragma comment(lib, "Dbghelp.lib")
namespace ysm
{
LONG WINAPI Debug::crashStackCallback(struct _EXCEPTION_POINTERS* exceptionInfo)
{
std::string savePath = boost::filesystem::current_path().string() + "/dump/";
if (!boost::filesystem::exists(savePath))
{
if (!boost::filesystem::create_directory(savePath))
return EXCEPTION_EXECUTE_HANDLER;
}
boost::posix_time::ptime time_local = boost::posix_time::second_clock::local_time();
std::string time_str = boost::posix_time::to_simple_string(time_local);
time_str = boost::replace_all_copy(time_str, " ", "-"); // 将空格替换为 "-"
time_str = boost::replace_all_copy(time_str, ":", "_"); // 将冒号替换为 "_"
time_str = time_str.substr(0, time_str.length() - 2); // 去掉秒的小数部分
savePath.append("crash_");
savePath.append(time_str);
int id = GetCurrentThreadId();
savePath.append(std::to_string(id));
savePath.append(".dmp");
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring wstr = converter.from_bytes(savePath);
HANDLE dump = CreateFileW(wstr.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == dump)
{
//app->exit(E_UNEXPECTED);
return EXCEPTION_EXECUTE_HANDLER;
}
MINIDUMP_EXCEPTION_INFORMATION miniDumpExceptionInfo;
miniDumpExceptionInfo.ExceptionPointers = exceptionInfo;
miniDumpExceptionInfo.ThreadId = GetCurrentThreadId();
miniDumpExceptionInfo.ClientPointers = TRUE;
DWORD idProcess = GetCurrentProcessId();
MiniDumpWriteDump(GetCurrentProcess(),
idProcess,
dump,
MiniDumpWithFullMemory, &miniDumpExceptionInfo, NULL, NULL);
// 第四个参数 DumpType 指定类型,用于生成包含不同信息、大小的dump日志。
CloseHandle(dump);
//app->exit(E_UNEXPECTED);
return EXCEPTION_EXECUTE_HANDLER;
}
}
一般在主程序中启用
SetUnhandledExceptionFilter(ysm::Debug::crashStackCallback);