【负载均衡在线OJ项目日记】编译与日志功能开发

目录

日志功能开发

常见的日志等级

日志功能代码

编译功能开发

创建子进程和程序替换

重定向

编译功能代码


日志功能开发

日志在软件开发和运维中起着至关重要的作用,目前我们不谈运维只谈软件开发;日志最大的作用就是用于故障排查和调试;

当系统出现问题时,日志记录可以帮助开发人员追踪问题的根源。通过查看日志文件,开发人员可以了解系统在发生故障之前的行为,识别错误发生的时间、地点和原因,并快速定位到错误的代码或功能模块。

因此我们要为目前这个项目编写一个简单的日志模块,这个模块可能被项目中的任何一个部分使用,我们将其放在公共模块的一个文件中。

常见的日志等级

  1. DEBUG(调试): 用于记录程序的详细运行信息,通常用于开发和调试阶段,帮助开发人员定位问题和追踪程序流程。

  2. INFO(信息): 用于记录程序正常运行时的重要信息,例如启动信息、关键操作记录等,可用于了解系统的基本运行情况。

  3. WARNING(警告): 用于记录一些潜在的问题或异常情况,虽然不会导致系统崩溃或功能失效,但需要开发人员注意和处理,以避免可能的错误。

  4. ERROR(错误): 用于记录错误事件,表示程序发生了一些可恢复的错误,但并未导致程序完全失败,通常需要开发人员及时处理以保证系统的正常运行。

  5. FATAL(致命错误): 用于记录严重错误事件,表示程序发生了无法恢复的错误,导致程序崩溃或功能失效,需要立即进行修复和处理,以保证系统的稳定性和可靠性。

基于这个项目,目前我们实现的时文件版本;因此我们需要打印出日志等级、文件名称、报错行、时间戳。

日志功能代码

Log.hpp

复制代码
#pragma once
#include <iostream>
#include <string>
#include "util.hpp"
using namespace std;
namespace ns_log
{
    using namespace ns_util;
    // 日志等级
    enum
    {
        INFO,    // 就是整数0
        DEBUG,   // 1
        WARNING, // 2
        ERROR,   // 3
        FATAL    // 4
    };
    // 参数
    // 日志等级
    // 文件名
    // 行数
    // 标准输出流返回给用户
    inline ostream &Log(const string &level, const string &file_name, const int line)
    {
        // 添加日志等级
        string message = "[";
        message += level;
        message += "]";
        // 添加报错文件名称
        message += "[";
        message += file_name;
        message += "]";
        // 添加报错行
        message += "[";
        message += to_string(line);
        message += "]";

        // 日志时间戳
        message += "[";
        message += TimeUtil::GetTimeStamp();
        message += "]";

        // cout 本质内部是包含缓冲区的
        //
        cout << message; // 不要endl刷新
        // 此时message 就在缓冲区中
        return cout;
    }
// LOG(INFO)<<"message"<<endl;
// 开放日志
#define LOG(level) Log(#level, __FILE__, __LINE__)
}

最后我们宏定义这个函数调用,参数#level 用于将参数 level 转换成一个字符串,参数__FILE____LINE__ 是预定义的宏,在编译时由编译器自动替换为当前源文件的文件名和代码行号。

时间信息也是一个公用的信息我们会在另一个文件中实现。


编译功能开发

首先我们要明白这个模块只负责进行代码编译,那么意味着我们目前默认是能够接收远端提交的代码文件,并且这个文件对于我们编译模块来说是一个临时文件,如果编译成功后也会形成一个临时的可执行文件,因此在这个模块中我们还需要一个临时文件的文件夹。但是对于这个模块中目前的进程是用来接收代码文件的,对于提交的代码文件我们如何处理呢?

创建子进程和程序替换

因为编译对于操作系统来也是执行了一个程序,只不过这个程序很小,过程快而已;因此我们可以创建一个子进程,让子进程进行程序替换,替换我们的编译指令程序。

重定向

编译代码就两种结果:编译成功或者编译失败;编译成功就是我们想要的结果,可以通过判断是否生成可执行文件判断这个结果;对于编译失败,编译失败的信息会向我们的显示器打印,但是我们是要将信息返回给使用者的;因此我们就需要将错误信息重定向到一个错误文件中。

编译功能代码

compiler.hpp

复制代码
// 主要进行编译服务
#pragma once

// 编译服务器
#include <iostream>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>
#include"../comm/util.hpp"
#include"../comm/Log.hpp"
using namespace std;

// 只负责进行代码编译

// 默认代码能够接收

// 远端提交代码

// 一定要能够形成临时文件

// 第一种编译通过
// 第二种编译出错
// 本质是像stderr错误中打印---->需要形成临时文件 ,帮助我们保存编译出错的结果的

// 不能让这个进程编译
// 核心思路:创建子进程(fork())---->子进程完成编译代码 编译错误向stderr中打印(默认是向显示屏打印) 需要重定向到stderr中
// 父进程继续执行

namespace ns_compiler
{
    //引入路径拼接功能
    using namespace ns_util;
    using namespace ns_log;
    class Compiler
    {
    public:
        Compiler()
        {
        }
        ~Compiler()
        {
        }
        //编译
        //返回值:编译成功:true 编译失败:false
        //参数:编译代码的文件名
        //1234
        //./temp/1234.cpp
        //./tmep/1234.exe
        //./temp/1234.stderr//编译错误临时文件
        static bool Compile(const std::string &file_name)//temp文件夹保存临时文件
        {
            pid_t pid = fork();
            if(pid<0)
            {
                //创建子进程失败
                LOG(ERROR)<<"内部错误,创建子进程失败"<<"\n";
                return false;
            }
            else if(pid==0)
            {
                //需要一个错误文件
                int _stderr = open(PathUtil::Stderr(file_name).c_str(),O_CREAT|O_WRONLY,644);//
                if(_stderr<0)
                {
                    //打开文件失败
                    LOG(WARNING)<<"没有成功形成stderr文件"<<"\n";

                    exit(1);
                }
                //编译错误时才会向文件中写入
                //重定向
                dup2(_stderr,2);
                //子进程:调用编译器完成对代码的编译工作
                //程序替换
                //不需要冗余的路径
                //g++ -o target src -std=c++11
                //程序替换并不影响文件描述符表

                //我想执行谁,怎么执行
                execlp("g++","g++","-o",PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(),"-std=c++11",nullptr/*不要忘记*/);
                LOG(ERROR) <<"启动编译器g++失败,可能是参数错误"<<"\n";
                exit(2);
            }
            else 
            {
                //父进程
                waitpid(pid,nullptr,0);
                //编译是否成功---->是否形成可执行程序
                if(FileUtil::IsFileExists(PathUtil::Exe(file_name)))
                {
                    LOG(INFO)<<PathUtil::Src(file_name)<<" 编译成功!"<<"\n";
                    return true;
                }
            }
            LOG(ERROR)<<"编译失败,没有形成可执行程序"<<"\n";
            return false;

        }
    };
}

until.hpp

复制代码
#pragma once
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
using namespace std;
namespace ns_util
{
    const std::string temp_path = "./temp/";
    // 路径
    class PathUtil
    {
    public:
        static std::string AddSuffix(const std::string &file_name, const std::string suffix)
        {
            std::string path_name = temp_path;
            path_name += file_name;
            path_name += suffix;
            return path_name;
        }
        // 构建源文件路径+后缀的完整文件名
        // 1234->./temp/1234.cpp
        static std::string Src(const std::string &file_name)
        {
            return AddSuffix(file_name, ".cpp");
        }
        // 构建可执行程序的完整路径+后缀
        static std::string Exe(const std::string &file_name)
        {
            return AddSuffix(file_name, ".exe");
        }
        // 构建该程序对应的标准错误完整的的路径+后缀名
        static std::string Stderr(const std::string &file_name)
        {
            return AddSuffix(file_name, ".stderr");
        }
    };
    class FileUtil
    {
    public:
        static bool IsFileExists(const std::string path_name)
        {
            // 通过判断获取文件属性判断文件是否存在
            struct stat st;
            if (stat(path_name.c_str(), &st) == 0)
            {
                // 获取文件属性成功代表文件存在
                return true;
            }
            return false;
        }
    };
    class TimeUtil
    {
    public:
        static string GetTimeStamp()
        {
            struct timeval _time;
            gettimeofday(&_time, nullptr);
            return to_string(_time.tv_sec);
        }
    };
}

今天对项目编译和日志功能开发的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!

相关推荐
花嫁代二娃5 小时前
Linux:环境变量
linux
乌托邦的逃亡者6 小时前
Docker的/var/lib/docker/目录占用100%的处理方法
运维·docker·容器
数字芯片实验室6 小时前
分享一个可以学习正则表达式的网址:Pythex.org
学习·正则表达式
ldj20206 小时前
Jenkins 流水线配置
运维·jenkins
陈洪奇6 小时前
注册中心学习笔记整理
笔记·学习
光影少年6 小时前
从前端转go开发的学习路线
前端·学习·golang
古希腊数通小白(ip在学)8 小时前
stp拓扑变化分类
运维·服务器·网络·智能路由器
Muxiyale9 小时前
使用spring发送邮件,部署ECS服务器
java·服务器·spring
l1x1n010 小时前
Vim 编辑器常用操作详解(新手快速上手指南)
linux·编辑器·vim
12点一刻10 小时前
搭建自动化工作流:探寻解放双手的有效方案(2)
运维·人工智能·自动化·deepseek