目录
打印日志
日志的作用
什么是日志呢,简单来说就是反映项目中的执行过程阶段性结果的一种信息获取。
我们之前是通过自己定义直接输出打印某步骤中执行的结果,如下面的代码:
if (_sock < 0){
std::cerr << "socket error" << std::endl;
exit(2);
}
这是我们自己"socket error" 输出的,而了解过网络编程的类似绑定,监听,以及各种执行下来,这种结构不在少数,难道都我们自己来一个一个把他们写出来吗,肯定不是的,我们可以设置一个日志信息,当执行到某一部的时候,把相关性质的参数传给它,让他帮我们快捷性的打印当前的执行结果状态,这就是日志的作用。
一个简单的测试
在之前的C语言中,我们想要打印对应的行列信息,我们可以用到C语言给我们的预定义的宏,而在这里我们其实就可以用到它,我们先来做一个简单测测试。
编译链接通过,我们可以看到
就成功打印了我们的文件名和行号
日志的结构
【说明】:
日志级别 :
· 公有四种级别,分别是 INFO(执行正常)、 WARNING (有警告) 、ERROR (执行中遇到错误)FATAL (执行极端),注意INFO是正常执行,而FATAL级别一般是表示程序几乎已经崩溃,这个时候不能再让程序执行下去了,必须终止。
日志信息:详细说明这一步的情况内容。
错误行:这里表示当前错误的行数,上面的测试验证了我们可以直接用C中的宏。
错误文件信息:表示文件出错内容的详细信息。
时间戳:
我们在刚刚开始学习Linux时间戳的时候,学习一些常见的命令,但是我们这里暂时不用,我们还是用time的时间戳。
我们不需要知道实际的时间,我们只要让其起到时间戳最原本的目的,数值随着时间的变化而变化就可以了,我们可以来一个简单的时间戳的测试
我们编译链接,查看一下时间戳的具体情况:
我们可以看到,这里起到了我们想要查看的时间戳的效果。
日志编写
#pragma once
#include <iostream>
#include <string>
#include <ctime>
#define INFO 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
#define LOG(level, message) Log(#level, message, __FILE__, __LINE__)
void Log(std::string level, std::string message, std::string file_name, int line)
{
std::cout << "[" << level << "]" << "[" << time(nullptr) << "]" << "[" << message << "]" << "[" << file_name << "]" << "[" << line << "]" << std::endl;
}
说明:我们把level级别定义成了宏,但是我们传入级别的时候,却是以字符串的形式传入的,这个时候我们应该再定义一个宏,把参数设置成#level可以把整数转化成字符串的形式。
添加日志信息
tcp文件添加日志
#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#define BACKLOG 5
class TcpServer{
private:
int port;
int listen_sock;
static TcpServer *svr;
private:
TcpServer(int _port):port(_port),listen_sock(-1)
{}
TcpServer(const TcpServer &s){}
public:
static TcpServer *getinstance(int port)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
if(nullptr == svr){
pthread_mutex_lock(&lock);
if(nullptr == svr){
svr = new TcpServer(port);
svr->InitServer();
}
pthread_mutex_unlock(&lock);
}
return svr;
}
void InitServer()
{
Socket();
Bind();
Listen();
LOG(INFO, "tcp_server init ... success");
}
void Socket()
{
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock < 0){
LOG(FATAL, "socket error!");
exit(1);
}
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
LOG(INFO, "create socket ... success");
}
void Bind()
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY; //云服务器不能直接绑定公网IP
if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
LOG(FATAL, "bind error!");
exit(2);
}
LOG(INFO, "bind socket ... success");
}
void Listen()
{
if(listen(listen_sock, BACKLOG) < 0){
LOG(FATAL, "listen socket error!");
exit(3);
}
LOG(INFO, "listen socket ... success");
}
int Sock()
{
return listen_sock;
}
~TcpServer()
{
if(listen_sock >= 0) close(listen_sock);
}
};
TcpServer* TcpServer::svr = nullptr;
【说明】:我们是根据所处的状态进行等级的划分,像是创建套件字失败,绑定失败,监听失败等,需要立即停止进程,这个时候应当把等级设置为极限FATAL,程序正常情况下设置为INFO即可
http文件添加日志
#pragma once
#include <iostream>
#include <pthread.h>
#include"Protocol.hpp"
#include "TcpServer.hpp"
#include"Log.hpp"
#define PORT 8081
class HttpServer{
private:
int port;
TcpServer *tcp_server;
bool stop;
public:
HttpServer(int _port = PORT):port(_port),tcp_server(nullptr),stop(false)
{}
void InitServer()
{
tcp_server =TcpServer::getinstance(port);
}
void Loop()
{
LOG(INFO,"Loop,begin!");
int listen_sock = tcp_server->Sock();
while(!stop){
struct sockaddr_in peer;
socklen_t len =sizeof(peer);
int sock =accept(listen_sock,(struct sockaddr*)&peer,&len);
if(sock < 0 ){
continue;
}
LOG(INFO,"Get a new link");
int *_sock = new int(sock);
pthread_t tid;
pthread_create(&tid,nullptr,Entrance:: HandlerRequest,_sock);
pthread_detach(tid);
}
}
~HttpServer()
{}
};
Protocol文件添加日志
class Entrance{
public:
static void *HandlerRequest(void *_sock)
{
LOG(INFO,"Hander Requeset Begin!");
int sock =*(int*)_sock;
delete (int*)_sock;
#ifdef DEBUG
//For Test
char buffer[4096];
recv(sock,buffer,sizeof(buffer),0);
std::cout<<"-------------------begin--------------------"<<std::endl;
std::cout<<buffer<<std::endl;
std::cout<<"-------------------end--------------------"<<std::endl;
#else
EndPoint *ep = new EndPoint(sock);
ep->RecvHttpPoint();
ep->ParseHttpRequest();
ep->BuildHttpResponse();
ep->SendHttpResponse();
delete ep;
#endif
LOG(INFO,"Hander Request End!");
return nullptr;
}
};
日志测试
我们把日志添加到文件当中之后,我们就可以对日志进行简单的测试,编译完成通过,我们运行一下,如下图所示:
这个时候我们看到日志信息打印了我们一步一步的正常流程,我们现在通过浏览器,对其进行一下访问,我们可以看到的是如下:
我们看到了链接的日志信息,以及其处理方法的日志信息,并打印到了我们的显示器上。