日志的實現

目錄

日志與策略模式

Log.hpp

?class LogStrategy基類

class ConsoleLogStrategy派生類

?classFileLogStrategy派生類

日志等級

獲得時間戳

localtime_r函數詳解

函數原型

struct tm結構的指針

Logger類(重點)

class LogMessage 日志信息類

std::stringstream

用法

重載?流輸出運算符和析構函數


日志與策略模式

什么是設計模式?

IT行業這么火,涌入的人很多.俗話說林子大了啥鳥都有,大佬和菜雞們兩極分化的越來越嚴重.為了讓 菜雞們不太拖大佬的后腿,于是大佬們針對?些經典的常見的場景,給定了?些對應的解決方案,這個就是設計模式

日志認識

計算機中的日志是記錄系統和軟件運行中發生事件的文件,主要作用是監控運行狀態、記錄異常信 息,幫助快速定位問題并支持程序員進行問題修復。它是系統維護、故障排查和安全管理的重要工?具。

日志格式以下幾個指標是必須得有的

  • 時間戳
  • 日志等級
  • 日志內容
  • 文件名(可選)
  • 行號(可選)
  • 進程/線程id(可選)

日志有現成的解決方案,如:spdlog、glog、Boost.Log、Log4cxx等等,我們依舊采用自定義日志的方式。 這里我們采用設計模式-策略模式來進行日志的設計

我們想要的日志格式如下:

[ 可讀性很好的時間 ] [ 日志等級 ] [ 進程 pid] [ 打印對應日志的文件名 ][ 行號 ] - 消息內容,支持可 變參數

Log.hpp

首先創建Log.hpp文件,命名一個LogModule的空間域,我們將來的代碼就在這里面實現

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //C++17
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include "Mutex.hpp"  //互斥鎖(自己實現的)namespace LogMudoule
{}#endif

?class LogStrategy基類

策略模式就是利用多態的特性,我們先定義出一個LogStrategy的基類,后面通過我們傳的不同派生類來實現不同的模式,比如向屏幕打印的ConsoleLogStrategy派生類和向文件寫入的FileLogStrategy派生類

using namespace MutexModule;const std::string gsep = "\r\n";// 策略模式,C++多態特性// 2. 刷新策略 a: 顯示器打印 b:向指定的文件寫入//  刷新策略基類class LogStrategy{public:virtual ~LogStrategy(){}virtual void SyncLog(const std::string &message)=0;   //控制臺虛函數};

gsep是換行符,因為要使用鎖來保護臨界資源,這里Sync是同步的意思,基類的析構函數要記得加上virture。SyncLog是純虛函數(強制派生類重寫虛函數,因為不重寫實例化不出對象。)

class ConsoleLogStrategy派生類

下面的ConsoleLogStrategy派生類實現的也十分簡單

 // 顯示器打印日志的策略  子類class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy(){}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << gsep;//向屏幕打印消息}~ConsoleLogStrategy(){}private:Mutex _mutex;};

LockGuard 是我們封裝的互斥鎖的類,通過lockguard對象的創建來保護臨界資源,析構后釋放鎖

不懂互斥鎖的話可以去看看博客,我們的重點是完成日志,后續不再講解鎖

override是檢查派生類虛函數是否重寫了基類的某個虛函數。如果對于多態的知識不怎么了解的話,我十分推薦您去看這篇多態博客,點擊多態就能進去閱讀

?classFileLogStrategy派生類

下面的FileLogStrategy派生類稍微復雜一點但是并不難理解

// 文件打印日志的策略 : 子類const std::string defaultpath = "./log";const std::string defaultfile = "my.log";class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path),_file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)){return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);const std::string &filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message << gsep;out.close();}~FileLogStrategy(){}private:const std::string _path;const std::string _file;Mutex _mutex;};

理解一個類首先要去看它的私有成員,日志要向文件進行打印,首先要知道它的路徑,其次就是它的文件名。所以能理解_path和_file兩個成員變量

C++17 引入了一個重要的新特性:?文件系統庫? (std::filesystem),它提供了處理文件系統和路徑的標準方法。這個庫基于 Boost.Filesystem,并經過了標準化處理。在構造函數中完成_path和_file的初始化,然后利用std::filesystem::exists來檢查路徑是否存在,存在就返回,不存在就創建一個路徑(在當前路徑下的Log目錄下)

SyncLog函數中filename就是我們要創建的文件名,它由_path和_file組合,

中間的三目操作符意思是如果 _path 的最后一個字符是 '/',那么整個三目運算符的結果就是空字符串 "",否則就是字符串 "/"。然后,這個結果被用于與 _path 和 _file 進行字符串拼接。

ofstream是 C++ 標準庫中的一個類,全稱為 ?Output File Stream?(輸出文件流)。它用于將數據從程序寫入到文件中,是文件操作的重要組成部分。

  • std::ofstream: 是C++標準庫中用于文件輸出的流類,定義在頭文件 <fstream>中。
  • out: 是定義的輸出文件流對象的名稱。
  • filename: 是要打開的文件名(可以是字符串、字符數組等)。
  • std::ios::app: 是打開文件的模式標志,表示以追加模式(append)打開。
  • out.close():表示關閉文件

好了,到這里我們日志就實現了大概1/2,我們這里可以去檢驗一下我們寫錯沒有

#include <iostream>
#include "Log.hpp"using namespace LogMudoule;
int main()
{// std::unique_ptr<LogStrategy> strategy = std::make_unique<ConsoleLogStrategy>();std::unique_ptr<LogStrategy> strategy = std::make_unique<FileLogStrategy>();strategy->SyncLog("hello");return 0;
}

strategy是智能指針由派生類?classFileLogStrategy完成賦值(多態)表示我們選擇文件寫入的模式

下面的圖片也表明了我們的代碼無誤

日志等級

利用枚舉實現日志等級

// 1. 形成日志等級
enum class LogLevel
{DEBUG,INFO,WARNING,ERROR,FATAL
};

我們使用的枚舉實際上是0、1、2、3等數字,最終我們的日志是一串字符串所以我們還要類型轉換一下,也是非常簡單的。

std::string LeveltoStr(LogLevel level)
{switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "UNKNOWN";}
}

獲得時間戳

std::string GetTimeStamp()
{time_t curr = time(nullptr);struct tm curr_tm;localtime_r(&curr, &curr_tm);char timebuffer[128];snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year + 1900,curr_tm.tm_mon + 1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return timebuffer;
}

localtime_r函數詳解

localtime_r是一個用于將時間戳轉換為本地時間的線程安全函數。它是 localtime函數的可重入(reentrant)版本,在多線程編程中特別重要。

函數原型

struct tm *localtime_r(const time_t *timep, struct tm *result);

參數說明

  • timep: 指向 time_t類型時間的指針,表示從 1970-01-01 00:00:00 UTC 開始的秒數
  • result: 指向 struct tm結構的指針,用于存儲轉換后的時間信息

返回值

  • 成功時返回指向 result的指針
  • 失敗時返回 NUL

struct tm結構的指針

struct tm {int tm_sec;    // 秒 [0, 59]int tm_min;    // 分 [0, 59]int tm_hour;   // 時 [0, 23]int tm_mday;   // 日 [1, 31]int tm_mon;    // 月 [0, 11] (0 = 一月)int tm_year;   // 年 (從1900開始)int tm_wday;   // 星期 [0, 6] (0 = 周日)int tm_yday;   // 年中的日 [0, 365]int tm_isdst;  // 夏令時標志 (正數: 是, 0: 否, 負數: 未知)
};

snprintf格式化輸入函數,我們期望的年是4位,月是兩位(不足補0——%02d)天、時分秒也是如此

實現效果如下

Logger類(重點)

我們在Logger類中完成整個日志的實現它的結構如下

class Logger
{
public:Logger(){SelectConsoleLogStrategy();}void SelectFileLogStrategy(){_fflush_strategy = std::make_unique<FileLogStrategy>();}void SelectConsoleLogStrategy(){_fflush_strategy = std::make_unique<ConsoleLogStrategy>();}class LogMessage{public:private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一條完整的信息Logger &_logger;};LogMessage operator()(LogLevel level, std::string name, int line){return LogMessage(level, name, line, *this);}~Logger(){}private:std::unique_ptr<LogStrategy> _fflush_strategy;
};

為什么要在Logger類里實現一個內部類LogMessage最后再談

這里Logger的構造函數是選擇一種模式,我們這里設置的是向屏幕打印

Logger的私有成員變量是指向基類LogStrategy的智能指針(多態行為)在SelectConsoleLogStrategy()函數中完成賦值就是選擇對應模式

我們在Logger類中還完成了一個仿函數-故意沒寫引用&,這個仿函數的作用是體現在接下來的LogMessage類里重載<<中

LogMessage operator()實際使用方式

// 傳統方式可能這樣寫:
logger.log(DEBUG, "main.cpp", 42) << "This is a debug message";// 使用 operator() 可以這樣寫:
logger(DEBUG, "main.cpp", 42) << "This is a debug message";

class LogMessage 日志信息類

class LogMessage
{
public:LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()),_level(level),_pid(getpid()),_src_name(src_name),_line_number(line_number),_logger(logger){// 日志的左邊部分,合并起來std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << LeveltoStr(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage &operator<<(const T &info){// 日志的右半部分,可變的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if (_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一條完整的信息Logger &_logger;
};

老樣子先看私有成員前五個對應開頭提到的我們希望日志中包含的信息即

  • 時間戳
  • 日志等級
  • 日志內容
  • 文件名(可選)
  • 行號(可選)
  • 進程/線程id(可選)

我們需要把這五個信息合并成一條信息,這就是_loginfo的作用

我們希望logger(DEBUG, "main.cpp", 42) << "This is a debug message";時通過_logger來實現在屏幕上的打印,具體后面詳說,所以在LogMessage中我們還定義Logger &_logger成員

LogMessage的構造函數就是輸出一條完整信息,這里我們利用std::stringstream

std::stringstream

std::stringstream是 C++ 標準庫中的一個類,它提供了內存中的字符串流處理功能,結合了字符串的靈活性和流的操作接口。它是 <sstream>頭文件的一部分。

用法

std::stringstream ss;// 向流中插入數據
ss << "Hello, " << 42 << " " << 3.14 << " " << std::boolalpha << true;// 獲取完整的字符串
std::string result = ss.str();
std::cout << result; // 輸出: Hello, 42 3.14 true

在我們LogMessage中它的作用有兩個

//1_loginfo = ss.str(); // 獲取格式化后的字符串//2ss << info;          // 將任意類型轉換為字符串_loginfo += ss.str(); // 追加到日志信息

重載?流輸出運算符和析構函數

template <typename T>
LogMessage &operator<<(const T &info)
{// 日志的右半部分,可變的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;
}
~LogMessage()
{if (_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}

日志的右半部分是可變的,也許是整數,也許是字符串還可能是其他類型,這里我們要設置一個模板

通過stringstream類的ss對象來實現類型轉換并且追加到字符串_loginfo中,返回類型是LogMessage是因為我們期望<<能實現連續使用即下面這樣

logger(LogLevel::DEBUG, "main.cc", 10) << "hello world" << 3.143;

我們在上面實現的仿函數沒有使用引用,當我們向上面代碼這樣使用時,logger返回的是一份臨時對象(類型是LogMessage包含_loginfo的信息)臨時對象在下一行代碼就會被析構,析構時就會調用SyncLog函數打印對應的信息在屏幕上

最后收尾工作

// 全局日志對象
Logger logger;// 使用宏,簡化用戶操作,獲取文件名和行號
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Select_Console_Log_Strategy() logger.SelectConsoleLogStrategy()
#define Select_File_Log_Strategy() logger.SelectFileLogStrategy()

定義全局對象這樣我們就能直接選擇模式了

Select_Console_Log_Strategy();
LOG(LogLevel::DEBUG) << "hello world" << 3.141;
LOG(LogLevel::DEBUG) << "hello world" << 3.142;Select_File_Log_Strategy();
LOG(LogLevel::DEBUG) << "hello world" << 3.143;
LOG(LogLevel::DEBUG) << "hello world" << 3.144;

這就是日志代碼的講解,下面是完整代碼

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //C++17
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include "Mutex.hpp"namespace LogMudoule
{using namespace MutexModule;const std::string gsep = "\r\n";// 策略模式,C++多態特性// 2. 刷新策略 a: 顯示器打印 b:向指定的文件寫入//  刷新策略基類class LogStrategy{public:virtual ~LogStrategy(){}virtual void SyncLog(const std::string &message)=0;   };// 顯示器打印日志的策略  子類class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy(){}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << gsep;}~ConsoleLogStrategy(){}private:Mutex _mutex;};// 文件打印日志的策略 : 子類const std::string defaultpath = "./log";const std::string defaultfile = "my.log";class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path),_file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)){return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);const std::string &filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message << gsep;out.close();}~FileLogStrategy(){}private:const std::string _path;const std::string _file;Mutex _mutex;};// 1. 形成日志等級enum class LogLevel{DEBUG,INFO,WARNING,ERROR,FATAL};std::string LeveltoStr(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetTimeStamp(){time_t curr = time(nullptr);struct tm curr_tm;localtime_r(&curr, &curr_tm);char timebuffer[128];snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year + 1900,curr_tm.tm_mon + 1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return timebuffer;}class Logger{public:Logger(){SelectFileLogStrategy();}void SelectFileLogStrategy(){_fflush_strategy = std::make_unique<FileLogStrategy>();}void SelectConsoleLogStrategy(){_fflush_strategy = std::make_unique<ConsoleLogStrategy>();}class LogMessage{public:LogMessage(LogLevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()),_level(level),_pid(getpid()),_src_name(src_name),_line_number(line_number),_logger(logger){// 日志的左邊部分,合并起來std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << LeveltoStr(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage& operator <<(const T &info){// 日志的右半部分,可變的std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if(_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 合并之后,一條完整的信息Logger &_logger;};LogMessage operator()(LogLevel level, std::string name, int line){return LogMessage(level, name, line, *this);}~Logger(){}private:std::unique_ptr<LogStrategy> _fflush_strategy;};// 全局日志對象Logger logger;// 使用宏,簡化用戶操作,獲取文件名和行號#define LOG(level) logger(level, __FILE__, __LINE__)#define Select_Console_Log_Strategy() logger.SelectConsoleLogStrategy()#define Select_File_Log_Strategy() logger.SelectFileLogStrategy()
}
#endif

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94855.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【論文閱讀】Sparse4D v2:Recurrent Temporal Fusion with Sparse Model

標題&#xff1a; Sparse4D v2&#xff1a;Recurrent Temporal Fusion with Sparse Model 作者&#xff1a; Xuewu Lin, Tianwei Lin, Zixiang Pei, Lichao Huang, Zhizhong Su motivation 在v1的基礎上&#xff0c;作者發現長時序有更好的效果&#xff0c;但v1的計算量太大&am…

構建免費的音視頻轉文字工具:支持多語言的語音識別項目

在當今數字時代&#xff0c;音視頻內容越來越多&#xff0c;但如何快速將其轉換為文字一直是一個挑戰。本項目提供了一個免費的解決方案&#xff0c;支持將視頻和音頻文件轉換為文字&#xff0c;并且支持多語言識別。 一個支持中英文的音視頻轉文字工具&#xff0c;集成了 Vos…

【開題答辯全過程】以 基于SpringBootVue的智能敬老院管理系統為例,包含答辯的問題和答案

個人簡介一名14年經驗的資深畢設內行人&#xff0c;語言擅長Java、php、微信小程序、Python、Golang、安卓Android等開發項目包括大數據、深度學習、網站、小程序、安卓、算法。平常會做一些項目定制化開發、代碼講解、答辯教學、文檔編寫、也懂一些降重方面的技巧。感謝大家的…

Linux 830 shell:expect,ss -ant ,while IFS=read -r line,

[rootsamba caozx26]# scp /home/caozx26/pub root192.168.235.3:~/ root192.168.235.3s password: /home/caozx26/pub: not a regular file [rootsamba caozx26]# ls app km nntp.sh ntp.sh until1.sh 公共 圖片 音樂 find.sh l2 ntp1.sh pub u…

???????GPT-5發布引爆爭議,奧特曼連夜回應!付費充值的Plus用戶成最大贏家?

摘要&#xff1a; GPT-5發布后&#xff0c;社區口碑兩極分化&#xff0c;從“強無敵”到“還我4o”的呼聲并存。面對技術故障和用戶質疑&#xff0c;OpenAI CEO薩姆奧爾特曼及團隊火速回應&#xff0c;公布了一系列補救措施和未來計劃。本文將帶你速覽這場風波始末&#xff0c;…

Python 操作 Redis 的客戶端 - Redis Stream

Python 操作 Redis 的客戶端 - Redis Stream1. Redis Stream2. Redis Commands2.1. CoreCommands.xadd() (生產端)2.2. CoreCommands.xlen() (生產端)2.3. CoreCommands.xdel() (生產端)2.4. CoreCommands.xrange() (生產端)2.5. RedisClusterCommands.delete()3. Redis Stream…

【Qt開發】按鈕類控件(一)-> QPushButton

目錄 1 -> 什么是 PushButton&#xff1f; 2 -> 相關屬性 3 -> 代碼示例 3.1 -> 帶有圖標的按鈕 3.2 -> 帶有快捷鍵的按鈕 4 -> 總結 1 -> 什么是 PushButton&#xff1f; 在 Qt 框架中&#xff0c;QPushButton 是最基礎且最常用的按鈕控件之一&am…

Citrix 零日漏洞自五月起遭積極利用

安全研究員 Kevin Beaumont 披露了有關 CVE-2025-6543 的驚人細節&#xff0c;這是一個嚴重的 Citrix NetScaler 漏洞&#xff0c;在該公司發布補丁之前的幾個月里&#xff0c;該漏洞被積極利用作為零日攻擊。 Citrix 最初將其輕描淡寫為簡單的“拒絕服務”漏洞&#xff0c;但…

【系列08】端側AI:構建與部署高效的本地化AI模型 第7章:架構設計與高效算子

第7章&#xff1a;架構設計與高效算子 要將AI模型成功部署到端側&#xff0c;除了對現有模型進行壓縮和優化&#xff0c;更根本的方法是在設計之初就考慮其在資源受限環境下的運行效率。本章將深入探討如何設計高效的網絡架構&#xff0c;以及如何理解并優化常用的核心算子。高…

42-Ansible-Inventory

文章目錄Ansible基本概述手動運維時代&#xff08;原始社會&#xff09;自動化運維時代自動化運維工具的優勢Ansible的功能及優點Ansible的架構Ansible的執行流程安裝AnsibleAnsible配置文件生效順序Ansible inventory主機清單Ansible基于免秘鑰方式管理客戶端小結Ansible-Adho…

Go語言runtime/trace工具全面解析

基本概念與功能 Go語言的runtime/trace是Go標準庫中內置的性能分析工具,主要用于追蹤和可視化Go程序的運行時行為。它能夠記錄程序執行期間的各種事件,包括goroutine調度、系統調用、垃圾回收(GC)、網絡I/O、鎖等待等關鍵信息。 trace工具的核心功能包括: goroutine生命周期…

Docker(自寫)

Docker程序是跑在操作系統上的&#xff0c;而操作系統上又裝了各種不同版本的依賴庫和配置程序依賴環境&#xff0c;環境不同&#xff0c;程序就可能跑不起來&#xff0c;如果我們能將環境和程序一起打包docker就是可以將程序和環境一起打包并運行的工具軟件基礎鏡像DockerFile…

深度拆解 OpenHarmony 位置服務子系統:從 GNSS 到分布式協同定位的全鏈路實戰

1. 系統概述 OpenHarmony 的“定位子系統”就是硬件服務子系統集里的 “位置服務子系統”(Location SubSystem)。它向下對接 GNSS/GPS、基站、Wi-Fi 等定位模組,向上以 標準位置 API 形式為應用提供 實時位置、軌跡、地理圍欄 等能力,并可與分布式軟總線聯動,實現 跨設備…

React Native基本用法

1&#xff0c;index調用registerComponent,把appName注入到React Native的根節點。 2&#xff0c;package.json是全局大管家&#xff0c;package-lock.json鎖定版本&#xff0c;不會手動編輯&#xff0c;通過install安裝 3&#xff0c; bebal.config.json bebal.config.json是翻…

LoraConfig target modules加入embed_tokens(64)

LoraConfig target modules加入embed_tokens 更好且成本更低的方法 嵌入層(embedding layer)的 lora_embedding_A 和 lora_embedding_B 頭部(head)是否需加入目標模塊列表 用戶警告 解除權重綁定 解綁以后是隨機權重,怎么辦 更好且成本更低的方法 “有沒有一種更好且成本…

筆記共享平臺|基于Java+vue的讀書筆記共享平臺系統(源碼+數據庫+文檔)

筆記共享平臺|讀書筆記共享平臺系統 目錄 基于Javavue的讀書筆記共享平臺系統 一、前言 二、系統設計 三、系統功能設計 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取 博主介紹&#xff1a;??大廠碼農|畢設布道師&#xff…

【VSCode】VSCode為Java C/S項目添加圖形用戶界面

為Java C/S項目添加圖形用戶界面 現在我們來為它添加圖形用戶界面(GUI)。我將使用Java Swing庫創建一個簡單的GUI&#xff0c;因為它內置于Java標準庫中&#xff0c;無需額外依賴。 客戶端GUI實現 首先&#xff0c;我們將修改客戶端代碼&#xff0c;添加一個Swing GUI界面&…

【云原生】Docker 搭建Kafka服務兩種方式實戰操作詳解

目錄 一、前言 二、Docker 搭建kafka介紹 2.1 Docker 命令部署 2.2 使用Docker Compose 部署 2.3 使用 Docker Swarm 2.4 使用 Kubernetes 2.5 部署建議 三、Docker 搭建kafka操作方式一 3.1 前置準備 3.2 完整操作過程 3.2.1 創建docker網絡 3.2.2 啟動zookeeper容…

DBeaver中禁用PostgreSQL SSL的配置指南

在DBeaver中為PostgreSQL連接禁用SSL是一個常見的配置&#xff0c;特別是當你的數據庫服務器未啟用SSL或遇到連接問題時。我來為你詳細講解操作步驟和注意事項。 &#x1f6e0;? DBeaver中禁用PostgreSQL SSL的配置指南 詳細步驟 打開驅動設置&#xff1a;在DBeaver中創建新的…

數組去重【JavaScript】

數組去重&#xff0c;并且key和val相同的對象視為相同的&#xff0c;需要去重。主函數&#xff1a;/*** 數組去重* 兩個屬性相同的對象也認為是相同的* param {Array} arr* return {Array} */ function uniqueArray(arr) {const result []// outer: 標簽&#xff0c;標記外層循…