咋们不是个成熟的程序员那不得学会打印日志和捕抓出错?

15 篇文章 2 订阅
7 篇文章 0 订阅

杂谈

刚刚入行不久,想得太简单,出错了也知道哪里的问题,思维不够严密。加班熬夜少不了,写代码两小时,调试两整天。怀疑别人写的代码有问题,怀疑别人写的借口有问题,甚至于怀疑编译器。因为没有打印日志而选择弹框报错,报错信息不详尽,哪里出问题都不知道;没有catch错误,挨批了。我还以为catch C#才有的。

如何打印日志

  1. 在接口调用位置打日志
  2. 在关键函数位置打日志
  3. 在线程进程位置打日志
  4. 在句柄位置打日志
  5. 在关键信息的位置打日志
  6. 在数据库调用的位置打日志
  7. 在你怀疑的地方打日志
    总之我们的程序运行情况也全都依赖日志 打日志也是门学问。前期可能会抓不住重点需要加很多日志。

我是搞C++MFC的我下面提供我写代码的日志函数,这个日志函数是有多个模式可以选择的

.cpp文件

/* 包含头文件 */
/*
日志文件的相关操作
*/
//#include "stdafx.h"
#include "pch.h"
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include "log.h"
#include <CONIO.H>

HANDLE hOutputHandle;
DWORD nRet;//用来记录实际输出的字符个数

// 内部常量定义
#define LOG_MODE_DISABLE  0
#define LOG_MODE_FILE     1
#define LOG_MODE_DEBUGER  2
#define LOG_MODE_CONSOLE  3

// 内部全部变量定义
static FILE *s_log_fp   = NULL;
static DWORD s_log_mode = LOG_MODE_DISABLE;

/* 函数实现 */
void log_init(const char *file)
{
    if (!s_log_fp) {
        if (strcmp(file, "DEBUGER") == 0) {
            s_log_mode = LOG_MODE_DEBUGER;//2
        } 
		else if (strcmp(file, "CONSOLE") == 0) {
			s_log_mode = LOG_MODE_CONSOLE;//3
			AllocConsole();   //打开控制台
			hOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
		}
		else if (strcmp(file, "NULL") == 0) {
			s_log_mode = LOG_MODE_DISABLE;//0
		}
		else {
            s_log_fp = fopen(file, "w");
            if (s_log_fp) {
                s_log_mode = LOG_MODE_FILE;//1
            }
        }
    }
}

void log_done(void)
{
    if (s_log_fp) {
        fflush(s_log_fp); 
        fclose(s_log_fp);
        s_log_fp   = NULL;
        s_log_mode = 0;
    }
	if(s_log_mode = LOG_MODE_CONSOLE) FreeConsole(); //关闭后台
}

#define MAX_LOG_BUF 1024

//定义一个使用省略号的函数原型
//打印信息加日期
void log_printf(const char *format, ...)
{
	// if debug log is not enable, directly return
	//模式0返回
	if (s_log_mode == LOG_MODE_DISABLE) return; 

    char buf[MAX_LOG_BUF];
	char OutBuf[MAX_LOG_BUF+32];
	//声明一个va_list类型的变量valist
    va_list valist;
	//使用va_star把变量ap初始化为参数列表,指向参数列表的第一个可选参数
    va_start(valist, format);
	//第一个参数缓冲区
	//第二个参数缓冲区大小
	//第三个参数是参数列表
	//第四个参数valist是指向可选参数的指针
	//format内容合并到buf中
    vsnprintf(buf, MAX_LOG_BUF, format, valist);
	//清空参数列表,并使valist无效
    va_end(valist);

	SYSTEMTIME st;
	CString strDate;
	GetLocalTime(&st);
	//格式化日期
	strDate.Format("[%4d-%02d-%02d %02d:%02d:%02d:%03d]",st.wYear,st.wMonth,st.wDay, st.wHour,st.wMinute,st.wSecond,st.wMilliseconds);
	//格式化日期和变参内容到OutBuf中
	sprintf(OutBuf, "%s %s", (char *)(LPCTSTR)strDate, buf);

    switch (s_log_mode)//模式选择
    {
    case LOG_MODE_FILE: //模式1
		//不加换行符
		//缓冲区
		//文件指针
        fputs(OutBuf, s_log_fp); 
		//刷新缓冲区
        fflush(s_log_fp);
        break;

    case LOG_MODE_DEBUGER: //模式2
		//输出调试信息
        OutputDebugStringA(OutBuf);
        break;
	case LOG_MODE_CONSOLE: //模式3
		//句柄
		//缓冲区
		//大小in
		//输出的实际大小out
		//保留位
		WriteConsole(hOutputHandle, OutBuf, strlen(OutBuf), &nRet, NULL);
		break;
    }
}


.h文件

#pragma once

void log_init  (const char *file);
void log_done  (void);
void log_printf(const char *format, ...);


举一个例子在配置文件里面选择日志输出的方式:

配置文件中选择日志模式需添加的函数

/静态函数--得到了文件所在的路径
static void get_app_dir(char *path, int size)
{
	HMODULE handle = GetModuleHandle(NULL);  //获取进程空间的句柄
	GetModuleFileNameA(handle, path, size); // path得到文件的全路径
	// ps:D:\base\file\fliename.txt ->D:\base\file
	char  *str = path + strlen(path);
	while (*--str != '\\');
	*str = '\0';
}

//静态函数————得到了赋予给关键字的值
static void parse_params(const char *str, const char *key, char *val)
{
	char *p = (char*)strstr(str, key); //判断key是否是子串,返回子串以及后面的全部字符。
	int   i;

	if (!p) return;
	p += strlen(key); //跳过关键字
	if (*p == '\0') return;

	while (1) {
		if (*p != ' ' && *p != '=' && *p != ':') break; //空格等于冒号就继续下移,否则跳出
		else p++;
	}

	for (i = 0; i < MAX_PATH; i++) {
		if (*p == ',' || *p == ';' || *p == '\r' || *p == '\n' || *p == '\0') {//找到完了跳出
			val[i] = '\0';
			break;
		}
		else {
			val[i] = *p++; //值放到了val数组中
		}
	}
}

//静态函数------给关键字初始化,值从配置文件里面读出来的
static int load_config_from_file(char *mode)
{
	char  file[MAX_PATH];
	FILE *fp = NULL;
	char *buf = NULL;
	int   len = 0;

	// open params file
	get_app_dir(file, MAX_PATH); //路径放到了file中
	strcat(file, "\\mymode.ini"); //路径+文件名字
	fp = fopen(file, "rb"); //二进制打开文本只读

	if (fp) {
		fseek(fp, 0, SEEK_END);  //定位文件指针到文本末尾
		len = ftell(fp);  //返回文件指针的位置,相当于开头到这里的偏移量
		buf = (char*)malloc(len);
		if (buf) {
			fseek(fp, 0, SEEK_SET); //定位文件指针到开头
			fread(buf, len, 1, fp); //读全文
			//给关键字初始化,值从配置文件里面读出来的
			parse_params(buf, "loginmode", mode);

			free(buf);
		}
		fclose(fp);
		return 0;
	}

	return -1;
}

调用方式

strcpy(m_strLogFile, "CONSOLE"); //m_strLogFile 是char*类型的,默认模式控制台
	int ret = load_config_from_file(m_strLogFile);  //从配置文件里面读取信息
	if (ret != 0) {
		AfxMessageBox(TEXT("无法打开测试配置文件!"), MB_OK);
	}
	log_init(m_strLogFile);//配置模式
	log_printf("logfile  = %s\n", m_strLogFile); //函数于printf是使用方法一样
	log_printf("InitDlg----------\n");

运行结果:
配置了后台打印
在这里插入图片描述
配置了打印到文本,配置文件中的字符,就是日志文件名。
在这里插入图片描述

C++捕抓异常try catch

下面是一个捕获异常的模板,可以也推荐和打印日志使用,在容易出错的地方加上try catch,很省事情。
往往对数据库函数的调用应该用try catch 要进行容错处理。

#include <iostream>
#include <stdexcept>  //捕获异常的头文件

using namespace std;

int main()
{
	try
	{
		//...操作         
	}
	catch (exception e)
	{
		log_printf("....操作捕获到异常:%s", e.what()); //捕获到的异常打印到日志中

	}
}

局限性:一般崩溃无非就是数组越界或者空指针 这两种是catch不到的。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

困了就喝白茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值