服务器程序日志介绍以及实现封装

        日志的一般有诊断日志和交易日志,诊断日志简单说就是以文本形式供人阅读的日志。而交易日志是数据库的write-ahead log、文件系统的journaing等,通过回放日志可以恢复每一次修改后的状态。这里说的日志是诊断日志。

日志的作用:

1.诊断定位程序执行问题
2.追踪(trace)程序执行过程和数据变化。
3.程序的性能分析
4.记录数据采集、分析和统计

        在服务器编程中日志必不可少,对于程序问题的诊断和分析,GDB不一定是好的方法,通过日志定位问题是必要的。日志不光是给程序看的,也是给运维人员看的,有时候需要用grep、awk、sed命令来查询、筛选分析日志,所以内容格式要清晰明了。一条完整的日志要包含:日志级别、时间、源文件名、具体函数和行数以及线程ID等数据和日志的具体内容。

日志的级别分为6级:

enum LOG_LEVEL
{
	FATAL = 1,
	ERROR,
	WARN,
	INFO,
	DEBUG,
	TRACE,
};

TRACE: 调试追踪记录日志,默认情况不会打印输出到日志文件。

DEBUG:理论上“不属于错误”。记录开发调试的信息,正式上线后需要屏蔽。

INFO:理论上“不属于错误”在粗粒度级别上突出强调应用程序的运行过程,一般是一次性的,不会反复输出的。如程序启动,关闭,请求
事件等用于生产环境中输出程序运行的一些重要信息,不能滥用避免打印过多的日志。

WARN:属于轻微的“警告”,程序中出现了一些异常情况,但是影响不大,还可以正常使用。

ERROR:属于“普通的错误”,在程序可以控制的范围内,不会造成连锁影响或巨大影响,
日志发生之后其实不会导致系统运行出现异常的,可能是对某些数据的初始化深入验证出现的问题。

FATAL:属于“致命错误”,造成服务中断的错误。级别最高必须中断程序。比如:空指针异常,数据库不可用,如硬盘空间满等,关键业务流程中断等等。

        日志要将程序中执行的信息写入到日志文件中,期间涉及写硬盘的IO操作,需要用到fopen()、fwrite()、fflush()等函数。如果程序执行过程中直接调用这些系统函数写日志,会导致线程陷入内核,线程会阻塞的等待日志信息写入硬盘,这样线程会挂起,导致频繁的上下文切换,过多的系统调用会影响程序的执行效率。

        这不是我们希望看到的,而我们需要的写日志要低CPU开销,无阻塞的,而且多个线程同时写硬盘也不会提高写入硬盘的效率,它们会在一个队列中等待写入硬盘。
        所以我们需要一个日志线程负责将其他线程的产生的日志信息写入硬盘中,这是一个典型的多生产者-单消费者模型,多个线程往日志缓冲区写数据,一个线程从缓冲区读数据并写入硬盘。另外我们还需要整个程序的日志输出格式一致,这样要求日志库是一个单例模式的。

        C++日志库大致有两种API风格,C/Java的printf(fmt,...)风格,C++的steam<<风格。我在工作中用的比较多的是第一种,下面实现的日志库也是第一种风格。另外为了防止日志文件过大,有时候需要滚动写入。我这里没有处理,在我经历的游戏服务器的日志一般没有滚动写入。在上线之后一般只输出ERROR以上级别的日志,日志量不会太大。

        还有就是对于程序崩溃没有日志处理,这样会导致最后几条没有来及写入日志文件的日志丢失,往往最后几条才是最重要的。muduo中的日志库处理是写入core文件中,以后待改正。

禁止类拷贝和赋值 : NoneCopyCtorAssign.h

#ifndef NONE_COPY_ABLE_H_
#define NONE_COPY_ABLE_H_

class NoneCopyCtorAssign
{
protected:
	NoneCopyCtorAssign() {}
	~NoneCopyCtorAssign() {}
private:
	NoneCopyCtorAssign(const NoneCopyCtorAssign&);
	NoneCopyCtorAssign& operator=(const NoneCopyCtorAssign&);
};

/******************example**********************/
//class Test :private NoneCopyCtorAssign
//{
//	... //class不再声明copy构造函数或copy assignment操作符
//};
/****************************************/
/*
进行类体设计时,会发现某个类的对象是独一无二的,没有完全相同的对象,也就是对该类对象做副本没有任何意义.
因此,需要限制编译器自动生动的拷贝构造函数和赋值构造函数,面对在成员函数和友元函数企图拷贝对象时,
会产生连接器错误.遵循错误发现越早越好的原则,我们希望将连接期错误移至编译期.
解决思路是:设计一个专门为了阻止copying动作(包含copy和assign)而设计的基类,boost中的noncopyable就是这样实现
*/
#endif





缓冲区实现封装: Buffer.h 


#ifndef BUFFER_H_
#define BUFFER_H_

#include <algorithm>
#include <vector>
#include <assert.h>
#include <string.h>
#include "NoneCopyCtorAssign.h"

class Buffer : public NoneCopyCtorAssign
{
public:
	Buffer(size_t buffSize = 64 * 1024)
		:_initBuffSize(buffSize),
		_rdIndex(0),
		_wrIndex(0),
		_buffer(_initBuffSize, 0)
	{ 
		assert(rdBytes() == 0);
		assert(wrBytes() == _initBuffSize);
	};
	inline char *readBegin() { return &_buffer[_rdIndex]; }

	inline char* writeBegin() { return &_buffer[_wrIndex]; }

	inline size_t rdBytes() const { return _wrIndex - _rdIndex; }

	inline size_t wrBytes() const { return _buffer.size() - _wrIndex; }

	inline void afterWrite(const size_t len) { _wrIndex += len; }

	void append(const char*  data, size_t len)
	{
		ensureWrite(len);
		bcopy(data, &_buffer[_wrIndex], len);
		afterWrite(len);
	}

	void append(const void* data, size_t len)
	{
		append(static_cast<const char*>(data), len);
	}

	void ensureWrite(const size_t len)
	{
		if (wrBytes() < len)
		{
			size_t buffSize = ((len + (_initBuffSize - 1)) / _initBuffSize)*_initBuffSize + _buffer.size();
			_buffer.resize(buffSize, 0);
		}
		assert(wrBytes() >= len);
	}

	inline bool rdReady() const { return _wrIndex > _rdIndex; }

	//读到最后一个包时调用
	inline void afterRead(unsigned int len)
	{
		_rdIndex += len;
		if (_wrIndex > _rdIndex)
		{
			unsigned int tmp = _wrIndex - _rdIndex;
			if (_rdIndex >= tmp)
			{
				memmove(&_buffer[0], &_buffer[_rdIndex], tmp);
				_rdIndex = 0;
				_wrIndex = tmp;
			}
		}
		else
		{
			resetBuf();
		}
	}

	inline void resetBuf() {
		_rdIndex = 0;
		_wrIndex = 0;
	}

	void swap(Buffer& rhs)
	{
		_buffer.swap(rhs._buffer);
		std::swap(_rdIndex, rhs._rdIndex);
		std::swap(_wrIndex, rhs._wrIndex);
	}

	void shrink()
	{
		Buffer other(_initBuffSize);
		other.ensureWrite(rdBytes());
		swap(other);
	}

	inline size_t buffSize() const { return _buffer.size(); }

private:
	size_t _initBuffSize;
	size_t _rdIndex;
	size_t _wrIndex;
	std::vector<char> _buffer;
};

#endif //BUFFER_H_

时间函数封装

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值