主頁 >  其他 > C++還在用printf/cout進行Debug?學習一下如何自己寫日志庫吧(上篇)

C++還在用printf/cout進行Debug?學習一下如何自己寫日志庫吧(上篇)

2021-10-26 07:39:17 其他

文章目錄

        • 一. 前言
        • 二. 基本功能
        • 三. 代碼實作
          • 1. fdoglogger.h
          • 2. fdoglogger.cpp
        • 四. 測驗用例
          • 1. fdoglogger_test.cpp


一. 前言

哈嘍,自從實習以來很久沒有更文了,一是沒有時間,二是實習了之后突然發現自己能寫的東西也沒有多少了,趕上1024有征文活動,就寫一篇吧,在實習的這段時間,我更加認識到日志的重要性,客戶端值沒傳過來?看日志,服務崩潰了?看日志,沒錯,日志是出現例外第一個想到的東西,它記錄了程式運行程序中所呼叫的函式,所接受到的值,所執行的行為等等,大家也都看到這篇的標題了,我這個人有一個缺點,就是不太喜歡用別人的東西,如果有能力,我希望自己造,所以今天我們自己來動手擼一個日志庫,文章重點講實作程序,如果需要原始碼,可以前往github獲取FdogLog,一個輕量級C++日志庫,用于日志服務,

跪求三連!


二. 基本功能

我們先來捋一捋這個日志庫應該實作那些功能,

  1. 日志最最最基本的功能是什么,當然是列印或記錄日志,
  2. 資訊應該包括哪些資訊,時間?運行用戶?所在檔案?想要顯示的資訊?(自定義顯示資訊下篇實作)
  3. 資訊雖然顯示豐富,但是要盡可能讓代碼自己獲取其他資訊,呼叫者只需要設定最主要的資訊,
  4. 資訊有重要等級之分,所以我們需要對資訊做必要分類,提高效率,
  5. 如何實作全域盡可能簡潔的呼叫,
  6. 如果日志庫是運行在多執行緒環境,如何保證執行緒安全,(下篇實作)

這些就是一個日志庫所具備的最基本的功能,接下來繼續思考,還需要什么,

  1. 怎么控制日志的行為,
  2. 如果保存在檔案,如何定義檔案名,
  3. 隨著日志增加,檔案會越來越大,如何解決,(下篇實作)

簡單規劃完一個不那么完美的日志庫所具備的能力,現在我們來對這幾條做更詳細的規劃,

  1. 日志最最最基本的功能是什么,當然是列印或記錄日志,
  2. 資訊應該包括哪些資訊,時間?運行用戶?所在檔案?想要顯示的資訊?

當我在呼叫一個名為function的函式時,

function();

你希望它輸出怎么樣的資訊,

我被呼叫
[2021-10-20 23:27:23] 我被呼叫
[2021-10-20 23:27:23] INFO 我被呼叫
[2021-10-20 23:27:23] INFO root 我被呼叫
[2021-10-20 23:27:23] INFO root 17938 我被呼叫
[2021-10-20 23:27:23] INFO root 17938 [/media/rcl/FdogIM/service.h function:8] 我被呼叫

我想大部分人都會選擇最后一種輸出資訊吧(雖然在這之前,我們都大量使用cout輸出第一種),所以我們的日志應該包括時間,日志等級,運行用戶,行程ID,呼叫函式所在檔案,以及呼叫時所在行數,當然總會有人不想全部輸出,這將在后面給出方案,

  1. 資訊雖然顯示豐富,但是要盡可能讓代碼自己獲取其他資訊,呼叫者只需要設定最主要的資訊,

  2. 資訊有重要等級之分,所以我們需要對資訊做必要分類,提高效率,

  3. 如何實作全域盡可能簡潔的呼叫.

資訊有重要等級之分,要可以對資訊做區分,按照常見的等級之分,有:

ERROR: 此資訊輸出后,主體系統核心模塊不能正常作業,需要修復才能正常作業,
WARN: 此資訊輸出后,系統一般模塊存在問題,不影響系統運行,
INFO: 此資訊輸出后,主要是記錄系統運行狀態等關聯資訊,
DEBUG: 最細粒度的輸出,除去上面各種情況后,你希望輸出的相關資訊,都可以在這里輸出,
TRACE: 最細粒度的輸出,除去上面各種情況后,你希望輸出的相關資訊,都可以在這里輸出,

有了等級之分,如何實作全域盡可能簡潔的呼叫,通俗的說就是去掉一切不必要的呼叫,只留下最主要的呼叫,

例如:

#include<iostream>
#include"fdoglogger.h"  //添加日志庫頭檔案

using namespace fdog;   //日志庫的命名空間

int main(){
    FdogError("錯誤");
    FdogWarn("警告");
    FdogInfo("資訊");
    FdogDebug("除錯");
    FdogTrace("追蹤");
    return 0;
}

你不必初始化什么資訊,呼叫什么多余的初始化函式,只需要用這五個類似函式的東西來輸出即可,同樣,如果是另一個源檔案,依舊是這樣的呼叫方式(這里可以使用單一模式來實作,其意圖是保證一個類僅有一個實列,并提供一個訪問它的全域訪問點,該實體被所有程式模塊共享,就比如日志的輸出,),

  1. 如果日志庫是運行在多執行緒環境,如何保證執行緒安全,

到目前,一個基本的日志庫的呼叫基本成形,如果在單執行緒,它可以很好的作業,但是到了多執行緒環境下,就不能保證了,第一點就是單例模式的創建,當兩個執行緒同時去初始化時,無法保證單一實體被成功創建,第二,日志既然是輸出到檔案,不同執行緒寫入檔案時,如何保證寫入資料不會錯亂,既然寫的是C++的日志輸出,必然用到了cout ,cout 不是原子性的操作,所以在多執行緒下是不安全的,這些都是我們需要考慮到的,

  1. 怎么控制日志的行為,

這里使用組態檔進行日志的行為規定,包括列印什么日志,輸入到檔案,還是終端,輸出的等級,以及日志開關,等等,組態檔將在程式啟動時被讀取,(提醒各位千萬不要寫死代碼,后患無窮!!!)

  1. 如果保存在檔案,如何定義檔案名,

  2. 隨著日志增加,檔案會越來越大,如何解決,

日志的檔案名由組態檔指定,但是創建時會在后面加上創建日期后綴,并且可以在組態檔中配置每隔多少天創建一個新的日志檔案,如果配置中心有設定日志檔案大小,則會優先大小判斷,超過便創建一個新檔案,


三. 代碼實作

1. fdoglogger.h
#ifndef FDOGLOGGER_H
#define FDOGLOGGER_H
#include<iostream>
#include<fstream>
#include<map>
#include<mutex>
#ifndef linux
#include<unistd.h>
#include<sys/syscall.h>
#include<sys/stat.h>
#include<sys/types.h>
#include <pwd.h>
#endif
#ifndef WIN32
//TODO
#endif
using namespace std;

namespace fdog {

#define RED   "\e[1;31m"
#define BLUE  "\e[1;34m"
#define GREEN "\e[1;32m"
#define WHITE "\e[1;37m"
#define DEFA  "\e[0m"

enum class coutType: int {Error, Warn, Info, Debug, Trace};
enum class fileType: int {Error, Warn, Info, Debug, Trace};
enum class terminalType: int {Error, Warn, Info, Debug, Trace};

struct Logger {
    string logSwitch;           //日志開關
    string logFileSwitch;       //是否寫入檔案
    string logTerminalSwitch;   //是否列印到終端
    string logName;             //日志檔案名字
    string logFilePath;         //日志檔案保存路徑
    string logMixSize;          //日志檔案最大大小
    string logBehavior;         //日志檔案達到最大大小行為
    string logOverlay;          //日志檔案覆寫時間
    string logOutputLevelFile;  //日志輸出等級(file)
    string logOutputLevelTerminal;//日志輸出等級
};

class FdogLogger {
public:
    void initLogConfig();

    void releaseConfig();

    static FdogLogger* getInstance();

    string getCoutType(coutType coutType);

    bool getFileType(fileType fileCoutBool);

    bool getTerminalType(terminalType terminalCoutTyle);

    string getLogCoutTime();

    string getLogNameTime();

    string getFilePash();

    string getLogCoutProcessId();

    string getLogCoutThreadId();

    string getLogCoutUserName();

    bool createFile(string filePash);

    bool logFileWrite(string messages);

    bool bindFileCoutMap(string value1, fileType value2);

    bool bindTerminalCoutMap(string value1, terminalType value2);

private:
    char szbuf[128];
    Logger logger;
    static FdogLogger * singleObject;
    static mutex * mutex_new;
    map<coutType, string> coutTypeMap;
    map<fileType, bool> fileCoutMap;
    map<terminalType, bool> terminalCoutMap;

private:
    FdogLogger();
    ~FdogLogger();
};

#define Error1 __FDOGNAME__(Error)
#define Warn1 __FDOGNAME__(Warn)
#define Info1 __FDOGNAME__(Info)
#define Debug1 __FDOGNAME__(Debug)
#define Trace1 __FDOGNAME__(Trace)


#define SQUARE_BRACKETS_LEFT " ["
#define SQUARE_BRACKETS_RIGHT "] "
#define SPACE " "
#define LINE_FEED "\n"
#define COLON ":"
#define SLASH "/"

#define __FDOGTIME__  FdogLogger::getInstance()->getLogCoutTime()          //時間宏
#define __FDOGPID__   FdogLogger::getInstance()->getLogCoutProcessId()     //行程宏
#define __FDOGTID__   FdogLogger::getInstance()->getLogCoutThreadId()      //執行緒宏
#define __FDOGFILE__  __FILE__        //檔案名宏
#define __FDOGPASH__  FdogLogger::getInstance()->getFilePash() + __FDOGFILE__ //檔案路徑
#define __FDOGFUNC__   __func__        //函式名宏
#define __FDOGLINE__  __LINE__        //行數宏
#define __USERNAME__  FdogLogger::getInstance()->getLogCoutUserName()     //獲取呼叫用戶名字
#define __FDOGNAME__(name) #name        //名字宏


#define COMBINATION_INFO_FILE(coutTypeInfo, message) \
    do{\
        string messagesAll = __FDOGTIME__ + coutTypeInfo + __USERNAME__ + __FDOGTID__ + SQUARE_BRACKETS_LEFT + \
        __FDOGPASH__  + SPACE +__FDOGFUNC__ + COLON + to_string(__FDOGLINE__) + SQUARE_BRACKETS_RIGHT + message + LINE_FEED;\
        FdogLogger::getInstance()->logFileWrite(messagesAll); \
    }while(0);

#define COMBINATION_INFO_TERMINAL(coutTypeInfo, message) \
    do{\
        string messagesAll = __FDOGTIME__ + WHITE + coutTypeInfo + DEFA + __USERNAME__ + __FDOGTID__ + SQUARE_BRACKETS_LEFT + \
        __FDOGPASH__  + SPACE +__FDOGFUNC__ + COLON + to_string(__FDOGLINE__) + SQUARE_BRACKETS_RIGHT + message + LINE_FEED;\
        cout << messagesAll;\
    }while(0);

#define LoggerCout(coutTyle, coutTypeInfo, fileCoutBool, terminalCoutBool, message) \
    do {\
        string coutType = FdogLogger::getInstance()->getCoutType(coutTyle);\
        if (FdogLogger::getInstance()->getFileType(fileCoutBool)) {\
            COMBINATION_INFO_FILE(coutTypeInfo, message)\
        }\
        if (FdogLogger::getInstance()->getTerminalType(terminalCoutBool)) {\
            COMBINATION_INFO_TERMINAL(coutTypeInfo, message)\
        }\
    }while(0);

#define FdogError(message) \
    do{\
        LoggerCout(fdog::coutType::Error, Error1, fdog::fileType::Error, fdog::terminalType::Error, message)\
    }while(0);

#define FdogWarn(message)  \
    do{\
        LoggerCout(fdog::coutType::Warn, Warn1, fdog::fileType::Warn, fdog::terminalType::Warn, message)\
    }while(0);

#define FdogInfo(message)  \
    do{\
        LoggerCout(fdog::coutType::Info, Info1, fdog::fileType::Info, fdog::terminalType::Info, message)\
    }while(0);

#define FdogDebug(message) \
    do{\
        LoggerCout(fdog::coutType::Debug, Debug1, fdog::fileType::Debug, fdog::terminalType::Debug, message)\
    }while(0);

#define FdogTrace(message) \
    do{\
        LoggerCout(fdog::coutType::Trace, Trace1, fdog::fileType::Trace, fdog::terminalType::Trace, message)\
    }while(0);

}

#endif

2. fdoglogger.cpp
#include"fdoglogger.h"

using namespace fdog;


FdogLogger * FdogLogger::singleObject = nullptr;
mutex * FdogLogger::mutex_new = new(mutex);

FdogLogger::FdogLogger(){
    initLogConfig();
}
FdogLogger::~FdogLogger(){

}

FdogLogger* FdogLogger::getInstance(){
    mutex_new->lock();
    if (singleObject == nullptr) {
        singleObject = new FdogLogger();
    }
    mutex_new->unlock();
    return singleObject;
}

void FdogLogger::initLogConfig(){

    map<string, string *> flogConfInfo;
    flogConfInfo["logSwitch"] = &this->logger.logSwitch;
    flogConfInfo["logFileSwitch"] = &this->logger.logFileSwitch;
    flogConfInfo["logTerminalSwitch"] = &this->logger.logTerminalSwitch;
    flogConfInfo["logName"] = &this->logger.logName;
    flogConfInfo["logFilePath"] = &this->logger.logFilePath;
    flogConfInfo["logMixSize"] = &this->logger.logMixSize;
    flogConfInfo["logBehavior"] = &this->logger.logBehavior;
    flogConfInfo["logOverlay"] = &this->logger.logOverlay;
    flogConfInfo["logOutputLevelFile"] = &this->logger.logOutputLevelFile;
    flogConfInfo["logOutputLevelTerminal"] = &this->logger.logOutputLevelTerminal;

    string str;
    ifstream file;
    char str_c[100]={0};
    file.open("fdoglogconf.conf");
    if(!file.is_open()){
        cout<<"檔案打開失敗\n";
    }
    while(getline(file, str)){
        if(!str.length()) {
            continue;
        }
        string str_copy = str;
        //cout<<"獲取資料:"<<str_copy<<endl;
        int j = 0;
        for(int i = 0; i < str.length(); i++){
            if(str[i]==' ')continue;
            str_copy[j] = str[i];
            j++;
        }
        str_copy.erase(j);
        if(str_copy[0]!='#'){
            sscanf(str_copy.data(),"%[^=]",str_c);
            auto iter = flogConfInfo.find(str_c);
            if(iter!=flogConfInfo.end()){
                sscanf(str_copy.data(),"%*[^=]=%s",str_c);
                *iter->second = str_c;
            } else {
            }
        }
    }
    logger.logName = logger.logName + getLogNameTime() + ".log";

    bindFileCoutMap("5", fileType::Error);
    bindFileCoutMap("4", fileType::Warn);
    bindFileCoutMap("3", fileType::Info);
    bindFileCoutMap("2", fileType::Debug);
    bindFileCoutMap("1", fileType::Trace);

    bindTerminalCoutMap("5", terminalType::Error);
    bindTerminalCoutMap("4", terminalType::Warn);
    bindTerminalCoutMap("3", terminalType::Info);
    bindTerminalCoutMap("2", terminalType::Debug);
    bindTerminalCoutMap("1", terminalType::Trace);

    if(logger.logFileSwitch == "on"){
        if(!createFile(logger.logFilePath)){
            std::cout<<"Log work path creation failed\n";
        }
    }

    cout << "|========FdogLogger v2.0==========================|" <<endl << endl;
    cout << "  日志開關:" << logger.logSwitch << endl;
    cout << "  檔案輸出:" << logger.logFileSwitch << endl;
    cout << "  終端輸出:" << logger.logTerminalSwitch << endl;
    cout << "  日志輸出等級(檔案):" << logger.logOutputLevelFile << endl;    
    cout << "  日志輸出等級(終端):" << logger.logOutputLevelTerminal << endl;
    cout << "  日志檔案名:" << logger.logName << endl;
    cout << "  日志保存路徑:" << logger.logFilePath << endl;
    cout << "  單檔案最大大小:"<< logger.logMixSize << "M" << endl;
    cout << "  日志保存時間 :" << logger.logOverlay << "天" << endl << endl;
    cout << "|=================================================|" <<endl;
    
    return;
}

string FdogLogger::getCoutType(coutType coutType){
    return singleObject->coutTypeMap[coutType];
}

bool FdogLogger::getFileType(fileType fileCoutBool){
    return singleObject->fileCoutMap[fileCoutBool];
}

bool FdogLogger::getTerminalType(terminalType terminalCoutTyle){
    return singleObject->terminalCoutMap[terminalCoutTyle];
}

string FdogLogger::getLogCoutTime(){
    time_t timep;
    time (&timep);
    char tmp[64];
    strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S",localtime(&timep));
    string tmp_str = tmp;

    return SQUARE_BRACKETS_LEFT + tmp_str + SQUARE_BRACKETS_RIGHT;
}

string FdogLogger::getLogNameTime(){
    time_t timep;
    time (&timep);
    char tmp[64];
    strftime(tmp, sizeof(tmp), "%Y-%m-%d-%H:%M:%S",localtime(&timep));
    return tmp;
}

string FdogLogger::getFilePash(){
    getcwd(szbuf, sizeof(szbuf)-1);
    string szbuf_str = szbuf;
    return szbuf_str + SLASH;
}

string FdogLogger::getLogCoutProcessId(){
#ifndef linux
    return to_string(getpid());
#endif
#ifndef WIN32
//  unsigned long GetPid(){
//     return GetCurrentProcessId();
// }
#endif
}

string FdogLogger::getLogCoutThreadId(){
#ifndef linux
    return to_string(syscall(__NR_gettid));
#endif
#ifndef WIN32
//  unsigned long GetTid(){
//     return GetCurrentThreadId();
// }
#endif
}

string FdogLogger::getLogCoutUserName(){
    struct passwd *my_info;
    my_info = getpwuid(getuid());
    string name = my_info->pw_name;
    return SPACE + name + SPACE;
}

bool FdogLogger::createFile(string filePash){
    int len = filePash.length();
    if(!len){
        filePash = "log";
        if (0 != access(filePash.c_str(), 0)){
            if(-1 == mkdir(filePash.c_str(),0)){
                std::cout<<"沒路徑";
                return 0;
            }
        }
    }
    std::string filePash_cy(len,'\0');
    for(int i =0;i<len;i++){
        filePash_cy[i]=filePash[i];
        if(filePash_cy[i]=='/' || filePash_cy[i]=='\\'){
            if (-1 == access(filePash_cy.c_str(), 0)){
                if(0!=mkdir(filePash_cy.c_str(),0)){
                    std::cout<<"有路徑";
                    return 0;
                }
            }
        }
    }
    return 1;
}

bool FdogLogger::logFileWrite(string messages){
    ofstream file;
    file.open(logger.logFilePath + logger.logName, ::ios::app | ios::out);
    if(!file){
        cout<<"寫失敗"<<endl;
        return 0;
    }
    file << messages;
    file.close();
    return 1;
}

bool FdogLogger::bindFileCoutMap(string value1, fileType value2){
    if(logger.logOutputLevelFile.find(value1)!=std::string::npos) {
        fileCoutMap[value2] = true;
    } else {
        fileCoutMap[value2] = false;
    }
}

bool FdogLogger::bindTerminalCoutMap(string value1, terminalType value2){
    if(logger.logOutputLevelTerminal.find(value1)!=std::string::npos) {
        terminalCoutMap[value2] = true;
    } else {
        terminalCoutMap[value2] = false;
    }
}

四. 測驗用例

在這里插入圖片描述

1. fdoglogger_test.cpp
#include<iostream>
#include"fdoglogger.h"  //添加日志庫頭檔案

using namespace fdog;   //日志庫的命名空間

int main(){
    FdogError("錯誤");
    FdogWarn("警告");
    FdogInfo("資訊");
    FdogDebug("除錯");
    FdogTrace("追蹤");
    return 0;
}

在這里插入圖片描述

在這里插入圖片描述

暫時考慮到的就是這些,如有缺陷,歡迎評論區補充,(比如檔案寫入打開就關閉,很浪費資源,如何優化,下篇見),

原始碼已上傳github,還原star! FdogLog,一個輕量級C++日志庫,用于日志服務,


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/336185.html

標籤:其他

上一篇:Python居然開始抄作業了,這次抄的是Rust

下一篇:三、區分存盤物件的不同版本

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more