1.easylogging源码学习笔记(6)
2.LevelDB 源码剖析1 -- 原理
3.QT中实现log存储的动态四种方法
4.一键自动修改和翻新OC源码,解决苹果审核4.3和马甲问题
5.go语言的修改日志滚动(rolling)记录器——lumberjack
easylogging源码学习笔记(6)
`LOG` 是默认日志、CLOG自定义日志、码动LOG_IF条件日志
特殊日志
LOG_EVERY_N、态修LOG_AFTER_N、志级LOG_N_TIMES
for (int i = 1; i <= ; ++i) {
LOG_EVERY_N(2,动态各种图形源码 INFO) << "Logged every second iter";
}// 5 logs written; 2, 4, 6, 7,
for (int i = 1; i <= ; ++i) {
LOG_AFTER_N(2, INFO) << "Log after 2 hits; " << i;
}// 8 logs written; 3, 4, 5, 6, 7, 8, 9,
for (int i = 1; i <= ; ++i) {
LOG_N_TIMES(3, INFO) << "Log only 3 times; " << i;
}// 3 logs writter; 1, 2, 3
条件日志和特殊日志可以搭配使用
* `VLOG_IF(condition, verbose-level)`
* `CVLOG_IF(condition, verbose-level, loggerID)`
* `VLOG_EVERY_N(n, verbose-level)`
* `CVLOG_EVERY_N(n, verbose-level, loggerID)`
* `VLOG_AFTER_N(n, verbose-level)`
* `CVLOG_AFTER_N(n, verbose-level, loggerID)`
* `VLOG_N_TIMES(n, verbose-level)`
* `CVLOG_N_TIMES(n, verbose-level, loggerID)`
日志详细等级判定
if (VLOG_IS_ON(2)) {
// Verbosity level 2 is on for this file
}
性能追踪
* `TIMED_FUNC(obj-name)`
* `TIMED_SCOPE(obj-name, block-name)`
* `TIMED_BLOCK(obj-name, block-name)`
这些宏实际上都是关于el::base::type::PerformanceTrackerPtr,一个指向el::base::PerformanceTracker的修改指针
#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING)
PerformanceTracker::PerformanceTracker(const std::string& blockName,
base::TimestampUnit timestampUnit,
const std::string& loggerId,
bool scopedLog, Level level) :
m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
// We store it locally so that if user happen to change configuration by the end of scope
// or before calling checkpoint, we still depend on state of configuration at time of construction
el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
if (m_enabled) {
base::utils::DateTime::gettimeofday(&m_startTime);
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
}
在构造函数中获取一个时间,
PerformanceTracker::~PerformanceTracker(void) {
#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
if (m_enabled) {
base::threading::ScopedLock scopedLock(lock());
if (m_scopedLog) {
base::utils::DateTime::gettimeofday(&m_endTime);
base::type::string_t formattedTime = getFormattedTimeTaken();
PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
data.init(this);
data.m_formattedTimeTaken = formattedTime;
PerformanceTrackingCallback* callback = nullptr;
for (const std::pair& h
: ELPP->m_performanceTrackingCallbacks) {
callback = h.second.get();
if (callback != nullptr && callback->enabled()) {
callback->handle(&data);
}
}
}
}
#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
}
在析构函数中获取一个时间,码动处理时间data,态修使用PerformanceTrackingCallback类型指针callback,志级并在callback->handle(&data)中处理输出。动态
由于定义了ELPP_FEATURE_PERFORMANCE_TRACKING,修改因此在初始化(INITIALIZE_EASYLOGGINGPP)中实际上是码动安装了一个base::DefaultPerformanceTrackingCallback。
在PerformanceTracker类的态修handle函数中,callback是志级一个PerformanceTrackingCallback类型指针,由于安装的是DefaultPerformanceTrackingCallback对象,因此是一个基类指针指向了派生类对象。处理输出的逻辑在DefaultPerformanceTrackingCallback类的handle函数中。
DefaultPerformanceTrackingCallback类的handle函数首先会将数据成员m_data的指针赋值给函数参数,并创建一个base::type::stringstream_t类型的对象ss用于构建输出内容。根据m_data的dataType,输出不同的信息。在输出时,会使用el::base::Writer类构造并输出内容。
LevelDB 源码剖析1 -- 原理
LSM-Tree,全称Log-Structured Merge Tree,被广泛应用于数据库系统中,如HBase、Cassandra、贷牛源码LevelDB和SQLite,甚至MongoDB 3.0也引入了可选的LSM-Tree引擎。这种数据结构旨在提供优于传统B+树或ISAM(Indexed Sequential Access Method)方法的写入吞吐量,通过避免随机的本地更新操作实现。
LSM-Tree的核心思想基于磁盘性能的特性:随机访问速度远低于顺序访问,三个数量级的差距。因此,简单地将数据附加至文件尾部(日志或堆文件策略)可以提供接近理论极限的写入吞吐量。尽管这种方法足够简单且性能良好,但它有一个明显的缺点:从日志中随机读取数据需要花费更多时间,因为需要按时间顺序从近及远扫描日志直至找到所需键。因此,日志策略仅适用于简单的数据访问场景。
为了应对更复杂的读取需求,如基于键的搜索、范围搜索等,LSM-Tree引入了一种改进策略,通过创建一系列排序文件来存储数据,每次写入都会生成一个新的文件,同时保留了日志系统优秀的写性能。在读取数据时,系统会检查所有文件,并定期合并文件以减少文件数量,从而提高读取性能。
在LSM-Tree的基本算法中,写入数据按照顺序保存到一组较小的排序文件中。每个文件代表了一段时间内的数据变更,且在写入前进行排序。emwin 控件源码内存表作为写入数据的缓冲区,用于保持键值的顺序。当内存表填满后,已排序的数据刷新到磁盘上的新文件。系统会周期性地执行合并操作,选择一些文件进行合并,以减少文件数量和删除冗余数据,同时维持读取性能。
读取数据时,系统首先检查内存缓冲区,若未找到目标键,则以反向时间顺序检查各个文件,直到找到目标键。合并操作通过定期将文件合并在一起,控制文件数量和读取性能,即使文件数量增加,读取性能仍可保持在可接受范围内。通过使用内存中保存的页索引,可以优化读取操作,尤其是在文件末尾保留索引块,这通常比直接二进制搜索更高效。
为了减少读取操作时访问的文件数量,新实现采用了分级合并(Leveled Compaction),即基于级别的文件合并策略。这不仅减少了最坏情况下需要访问的文件数量,还减少了单次压缩的副作用,同时提供更好的读取性能。分级合并与基本合并的lua源码手册主要区别在于文件合并的策略,这使得工作负载扩展合并的影响更高效,同时减少总空间需求。
QT中实现log存储的四种方法
在QT开发中,日志管理是程序监控和调试的重要手段。本文将介绍在QT中实现日志存储的四种方法,帮助你根据项目需求选择合适的方式。
首先,日志重定向输出是一个便捷的解决方案,它通过封装的类实现,如gitcode.com/feiyangqing...,Qt的qInstallMsgHandler或qInstallMessageHandler提供了强大的支持。这种方式可以将调试信息重定向到文件、数据库,甚至网络,适用于嵌入式Linux环境。使用教程和详细功能可在gitee.com/feiyangqingyu...找到。
其次,log4qt作为log4j的Qt移植版本,如sourceforge.net/project...,虽然功能强大但可能需要自定义配置。log4qt提供了强大的日志功能,但可能需要对源码进行一定程度的修改。
QsLog是一个轻量级的日志库,github.com/victronenerg...,它的特点包括多种日志级别、运行时可配置、低开销和多目标支持。直接集成在项目中,linux 应用源码非常适合简洁项目。
最后,qclog是一个高效、易用的日志库,gitee.com/umatobu/qclog,它提供了彩色日志输出,是追求美观和快速开发者的理想选择。
综上,对于简单需求,日志重定向输出和QsLog是不错的选择;log4qt适合需要更多定制功能的开发者;而对于寻求高效和美观的日志显示,qclog是个值得尝试的选项。具体选择还需根据项目的具体需求来定。
一键自动修改和翻新OC源码,解决苹果审核4.3和马甲问题
自动修改/翻新/混淆/OC/iOS代码,自动替换类名,方法名
由来
网上有很多关于如何混淆iOS源码的方法,但是都不够智能,生成的方法类名要么千奇百怪,要么aaaabbbxxx这种完全毫无意义的名称,要么只能修改单个文件,多个文件根本无法关联,我就想有什么方法可以像真人一样去修改源码,符合语义,不是胡编烂造的方法名,还可以自动修改相关联的文件, 还能自己自定义单词库,于是就有了这个工具。
演示视频
划重点
1. 该工具可以让你一键翻新代码,但是不是生成完整的xcode项目,需要你自己新建一个xcode项目,然后把翻新的文件拖入到新的项目中。
2. 该工具只要是ipa都可以,不限制OC,Swift,Flutter,React Native,H5类app。
3.目前免费使用,免费使用,免费使用,重要的事情说三遍
几个效果展示
使用说明
下载项目,官网下载:IpaGuard官网--IOS 应用程序ipa文件混淆加密保护工具注意:说明中提到的路径均为绝对路径,提到的逗号,都为英文逗号。使用工具运行成功后,需要自己新建OC项目再将修改后的代码与资源文件拖入新建的工程。功能说明:
未来可能添加的功能:说明文档会不定期更新,如遇到问题先检查是否依照说明文档的定义进行配置。
1. 源文件路径/import_path(必选)
OC项目文件路径包含代码文件与资源文件
如下图
2. 导出路径/export_path(必选)
OC项目导出路径。
注意,本工具并不能帮你生成完整的OC项目,只会生成OC代码文件和复制修改项目内部的资源
3. 直接复制的路径/copy_only_pathes(可选)
直接复制,忽略的文件名,多个路径以,逗号隔开
4. 直接复制的文件名(不包含后缀)/copy_only_names(可选)
直接复制,忽略的文件名,不包含后缀
例:UISheetView.h 只需要输入UISheetView,多个文件用, 逗号隔开(注意全半角,逗号为英文逗号)
5. 不进行修改的文件或文件夹/no_change_pathes(可选)
深度读取,但是不会进行更改,如果引入了其他修改了的类,会相应的修改深度读取(说明):会读取文件内部的类比与项目内其他类进行关联例:xxxx/Classes/Models 不想修改,则输入这个文件夹的绝对路径, 如只输入Models,则所有包含名为Models的文件或文件夹都会标记为只读取不修改,多个文件和类用, 逗号隔开(注意全半角,逗号为英文逗号)
6. 不修改的文件或类的前缀名(区分大小写)/no_change_class_prefix_names(可选)
例:不想所有以MJ开头的类或文件,则输入MJ, 多个文件和类用,逗号隔开(注意全半角,逗号为英文逗号)
7. 不修改的文件后缀名(区分大小写)/no_change_class_prefix_names(可选)
如第六条。Model,Info -->不修改以Model,Info为结尾的文件或类
8. 只修改类名的文件名或类名/only_change_clsname_names(可选)
深度读取,只修改类名,不修改内部属性与方法,这个优先级最低,如果之前的条件包含了本参数中的路径,则不生效
9. 只修改类名的文件夹/文件路径/only_change_clsname_pathes(可选)
深度读取,只修改类名,不修改内部属性与方法的文件名,这个优先级最低,如果之前的条件包含了本参数中的路径 则不生效
. 动词词库路径/verbwords_path(可选)
提供单词数组json文件路径 修改的命名逻辑为ABAB型,A为动词,B为名词,例:getMessage
. 名词词库路径/nounwords_path(可选)
提供单词数组json文件路径 修改的命名逻辑为ABAB型,A为动词,B为名词,例:getMessage
. 类名前缀/class_prefix(可选)
给每个类添加的前缀例:MJExtension-> MJ 为前缀, SD_ScrollView-> SD_ 为前缀
. 属性名前缀/property_prefix(可选)
给每个属性添加的前缀例:property(nonatomic, strong) UIViewMJView-> MJ 为前缀, property(nonatomic, strong) UIView SD_View-> SD_ 为前缀
. 需要过滤的方法路径/filter_methods_path(可选)
把你需要过滤的方法写入一个文本文件,然后将该文本路径填入到此项输入框注意事项:其他说明:本工具已经过滤了大多数常用系统方法,大部分情况下不需要配置此项。
. 类名和属性名后缀路径/property_subfix_path(可选)
给每个属性添加的后缀,需要一个配置json文件路径json格式:注意:key一定要与上面一直,否则系统无法读取,工具会遍历数组,为对应的类匹配后缀,在配置改文件时,包含相同字符串的类,需要将类名更长的类放在前面,不然匹配结果会达不到预期。例如:UITableView与UIView,配置时需要将UITableView放在View之前,如上面的例子。
. 为方法名添加介词/add_preposition(可选)
工具内置了所有介词,可选择性添加例:getMessage添加介词后-> getAMessage or getTheMessage具体介词完全随机添加
. 修改分类/change_category(可选)
工具会自动识别分类,可选择是否修改
. 将原代码行作为注释写入/add_original_comments(可选)
由于本工具不保证修改后百分百不报错,将修改过原属性声明和方法名作为注释写入,方便在重建工程后报错与原工程进行对照,建议设置
. 修改方法内部局部变量名/change_local_property(可选)
定义在方法内部的局部变量,可选择是否修改
. 综合配置路径(可选)
json配置文件路径,内部必须为字典,key为说明条目标题后面的英文请注意:配置文件优先级高于输入框输入的规则,配置文件存在时, 输入框输入的配置不生效例:(可直接复制修改)
关于bug
虽然工具本身经过了完整的商业项目的考验,但由于每个人的代码风格不一样,工具不可避免会出现解析不了的情况,开发者建议过滤C语言的文件,与第三方库,减少出错的可能性,还是无法运行成功,你可以发isssue至客服邮箱或根据报错Log自行删减项目文件,其中利弊,自行斟酌。
go语言的日志滚动(rolling)记录器——lumberjack
Lumberjack是一款用于日志滚动记录的工具,它将日志文件在达到特定条件后进行归档,通常以普通文件或压缩文件形式,随后创建同名新文件继续记录。Lumberjack自身不包含基础的日志功能,如日志等级和格式化,它仅是一个文件记录器,适合与其它日志库配合使用。在Go语言中,通过将Lumberjack与内置的log模块结合,即可实现日志的滚动记录。
查看log.SetOutput接口,可得知Lumberjack需要实现io.Writer接口以与日志模块协同工作。事实是,Lumberjack确实实现了此接口,并进一步提供了io.Closer接口。
Lumberjack的对外接口由Logger结构体提供,它支持json或yaml格式的配置文件,包含配置如日志级别、格式等。Logger结构体不仅实现了io.WriterCloser的接口,还拥有主动滚动日志的Rotate方法。
文件名的备份通过backupName函数实现,它将原始文件名加上时间戳形成备份文件名,支持本地或UTC时间。尽管Lumberjack未提供时间格式化接口,时间格式默认为-xx...,与Go语言的time模块相关。
文件名的自定义修改需要通过下载源代码并进行调整。备份文件名示例为:/abc/def.log -> /abc/def---T--..log。
在启动日志系统时,通过openExistingOrNew方法获取日志文件句柄,这个句柄可能是已存在的文件或新建文件的。
当即将写入的字节数超过当前文件剩余配额时,即触发日志滚动。滚动过程分为三步。
日志滚动后处理在另一个goroutine中进行,不影响当前日志写入。收集旧日志文件,包括.log和.log.gz格式的文件。对文件名列表进行时间排序,依据MaxBackups、MaxAge、Compress参数完成旧日志的删除和压缩。
总结,Lumberjack作为滚动记录器,实现了文件记录与归档功能,尽管自身不包含日志功能,但其强大的文件管理能力使其成为日志滚动记录的理想选择。