1.Golang 结构化日志包 log/slog 详解(一):简单使用
2.常见log日志的日志使用方法详细解析
3.日志(Log)是什么?
4.Golang日志库 log
5.简单到傻瓜都会用的日志库:log.c
6.easylogging源码学习笔记(6)
Golang 结构化日志包 log/slog 详解(一):简单使用
在 Go 1.前,日志处理的码日官方包 log 广泛使用,但存在明显缺点,日志如社区不断需求改进。码日随着 Go 1.发布,日志log/slog 包应运而生,码日悍将传世 源码完美解决了原包缺陷,日志更实用的码日新特性随之到来。
log/slog 包以结构化形式提供日志记录功能,日志涵盖时间、码日级别、日志消息及键值对属性。码日包含时间、日志级别、码日信息及各类键值对,日志键为字符串,值为任意类型。使用 log/slog 包内的 Logger 结构体,提供 Info、Warn、Error 等方法用于打印不同等级的日志。
Logger 与 Handler 相关联,打印日志过程由 Logger 的方法调用并传递给 Handler,Handler 决定日志处理路径。默认Logger中包含Info、Warn、Error等常用打印日志函数,通过调用这些方法实现日志打印功能。例如,android 下拉刷新 源码使用 slog.Info 创建包含“hello”信息及键“count”值3键值对的日志。
Info函数通过调用Default函数返回的Logger实例的log方法实现日志打印。Default函数初始化defaultLogger,即默认Logger实例。默认Logger中的打印日志函数如Debug、Warn、Error。额外Log函数提供参数化日志级别,实现更灵活的日志记录。
Logger类型定义包含Log、Debug、Info、Warn、Error等日志打印方法。实现高效且灵活的日志记录体验。
总结log/slog包改进日志记录效率与体验,通过结构化方式与实用特性满足开发者需求,提供更高级的日志管理功能。
常见log日志的使用方法详细解析
日志在程序开发中起着至关重要的作用,它能帮助我们调试错误并记录关键信息。常见的日志框架包括Java自带的原生日志、log4j以及Slf4j等。
日志级别是控制输出信息的关键,通常分为:OFF、FATAL、ERROR、WARN、INFO、版本更新e源码DEBUG、TRACE和ALL,级别越高,信息越详细。Java.util.Logger,如在源代码中使用`Logger`,可以通过设置级别来调整输出,例如,`logger.setLevel(Level.ALL)`会显示所有级别的信息,而`logger.setLevel(Level.WARNING)`则只会显示严重和警告级别的信息。
log4j则通常在XML配置文件中配置,比如导入依赖和配置``元素。通过这种方式,可以精细地控制每个日志条目的格式和输出位置。log4j2的配置文件如`log4j2.xml`,提供了丰富的自定义选项。
Slf4j作为当前主流的日志框架,支持参数化输出,只需在类头引入`import org.slf4j.Logger`和`LoggerFactory.getLogger`,并在代码中调用相关方法。在引入依赖时,可能出现`SLF4J: Failed to load class`的问题,解决方法通常是检查类路径和slf4j的绑定。Slf4j的输出信息中包含类路径信息,便于追踪问题来源。
总结来说,log日志的使用方法因框架而异,但核心在于控制输出级别和配置细节,eclipse调试android源码以满足不同开发阶段的需求。通过灵活运用这些工具,可以提高程序的可维护性和可读性。
日志(Log)是什么?
日志信息过于简化,乃至于没有日志,则用户无法找到解决问题所需的信息,进而妨碍问题的解决;然而日志信息过于详细不仅会降低系统的性能而且会使真正有用的信息淹没在文字的海洋中。
为此JDK给出了建议的日志分级标准。将不同的信息根据其重要性分级。与此同时可以根据实际需要在JRE中设置需要记录的日志级别--级别高于此值的日志才被记录。
依照JDK提供的标准(java。util。logging。Level)将日志划分为OFF、SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST、ALL等从高到低九个级别。他们都分别对应着唯一的整数值,即OFF=Integer。
MAX_VALUE、SEVERE=、WARNING=、INFO=、zxing android 源码简化CONFIG=、FINE=、FINER=、FINEST=、ALL=Integer。MIN_VALUE。通过对java。util。logging。
Level的泛化(扩展),开发人员可以在JDK提供的标准基础之上定义自己的日志分级标准。
在这九个级别中OFF、SEVERE、WARNING、INFO、CONFIG、ALL比较容易理解。
OFF级别主要用于JRE日志输出控制,表示不输出任何信息。
SEVERE(严重)级别描述组织程序正常运行的重大事件。这些事件的表述必须能够让最终用户和系统管理员清晰地了解到底发生了什么事情。
WARNING(警告)级别描述了最终用户或系统管理员维护时比较感兴趣的事件,或指示系统存在潜在问题的事件。
这些事件都需要特别提醒最终用户或系统管理员注意。
INFO(信息)级别主要用于描述输出到控制台或其替代品的,具有相当程度重大意义的事件。譬如系统的心跳信息,以及其他系统希望告知最终用户或系统管理员的信息等。
CONFIG(配置)级别主要用于描述可以辅助调试解决问题的静态配置信息。
譬如CPU类型、操作系统类型、内存容量、系统语言等等。
ALL级别也是主要用于JRE日志输出控制,表示输出所有日志信息。
FINE、FINER、FINEST等三个级别被用于描述不同程度的跟踪信息。这三个级别被sun分别翻译为"良好","较好"和"最好",但是笔者认为翻译为"略细","较细","最细"更合适。
这三个级别比较容易使人难于区分。到底什么样的信息应该以哪个级别输出呢?
一般说来,FINE级别用于输出开发人员广泛关注的信息。包括小的可恢复的故障,潜在的性能问题、数据源连接不足、服务超时等。
FINER级别描述比FINE级别更详细的信息。
包括进入/返回方法调用,抛出了一个异常等信息。
Golang日志库 log
日志库的正确使用姿势和Golang日志库Glog源码分析
日志,顾名思义,就是记录系统操作和结果的文件。日志文件记录了系统与用户交互的信息,是分析系统运行状态和解决线上问题的重要手段。良好的日志规范对于系统运维至关重要。
在开发中,正确使用日志库能帮助我们追踪程序运行过程中的状态,定位问题,提高代码的可维护性。Golang标准库提供的`log`库就是常用的日志工具之一。
使用`log`库,首先需要明确的是,这个库不需要额外安装,直接使用即可。默认情况下,日志输出到标准错误,并且每条日志前自动包含日期和时间戳。`log`库提供了丰富的功能,包括格式化输出、添加前缀、设置选项等。
例如,使用`log.Prefix`方法可以为日志添加一个前缀,增强日志的可读性。同时,通过`log.SetFlag`可以定制日志输出的格式选项,比如日期、时间、文件名等。
为了实现更灵活的日志输出,可以创建自定义的Logger。`log.New`函数提供了创建自定义日志器的接口,通过设置Writer参数,可以将日志输出到不同的目的地,如标准输出、文件、甚至网络。
核心功能`Output`方法处理了日志的格式化、前缀添加和输出流程。通过设置`calldepth`参数,可以获取调用栈的信息,增加日志的上下文信息。
总之,Golang的`log`库提供了一套简洁且强大的日志处理方案,不仅能够满足基本的日志需求,还允许开发者根据项目需要进行高度定制,是开发中不可或缺的工具。
简单到傻瓜都会用的日志库:log.c
log.c 是一个专为C语言设计的简单易用的日志处理模块,仅包含一个.c和.h文件,总共行代码,体现了其简洁的设计。它的核心功能是提供一个单一的API,log_log(),用于打印日志,其使用方法与printf类似。通过API,用户可以将日志记录到不同的目标,如文件,同时考虑到线程安全,允许用户自定义锁机制。
在内部实现上,log.c使用全局变量L来管理信息,udata用于存储用户自定义数据,而level和quiet分别控制日志等级和输出开关。一个名为callbacks的数组用于存储多种输出方式,当前支持标准输出和文件,未来可扩展到syslog和网络。log_Event是私有数据结构,用于表示每条日志信息,用户可以自定义输出函数log_LogFn。
log.c的公共接口非常直观,仅有一个核心打印函数log_log(),其他如log_trace()等宏则是其扩展。这个设计让使用者和开发者都感到方便。整个库的实现流程是:首先根据输入的日志信息构造log_Event,然后输出到标准输出,最后遍历并调用所有回调函数进行具体的打印操作。
该库由吴伟东Jack(公众号老吴嵌入式)原创,可以在其公众号文章《简单到傻瓜都会用的日志库:log.c》中获取更多详细信息和学习资源。点击相关链接深入了解这个易用的日志库。
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类构造并输出内容。