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

发布于:2024-05-09 ⋅ 阅读:(28) ⋅ 点赞:(0)

目录

日志功能开发

常见的日志等级

日志功能代码

编译功能开发

创建子进程和程序替换

重定向

编译功能代码


日志功能开发

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

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

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

常见的日志等级

  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);
        }
    };
}

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