- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
C++ 项目,使用了 log4cxx 日志库,版本为:0.10.0 。 项目中需要按照日期备份日志,即一天一个日志目录,目录下是日志文件,即目录层级为:.../log/2022-10-02/log.log,当日期变更后,目录变化:.../log/2022-10-03/log.log 。 使用的 log4cxx 的配置文件如下: 网络上和官方的教程中, DatePattern 都是作为日志备份的策略,当日期变化时,使用 DatePattern 的规则追加到旧日志的文件后缀上,完成日志按照日期回滚,但是这和我们的需求不符合 。 经查询, DatePattern 中的 ' 包含的不会被代码解析,利用此特性,写成上面的格式,可以实现按照日期创建目录 。 问题来了,刚开始程序启动一切正常,但是当日期变化时,新的日期目录并没有如期创建,日志内容还是记录在程序启动时创建的日志文件中 。 刚开始以为是配置文件写错了,在网络上查询并尝试了一天,毫无收获,开始怀疑是不是log4cxx的源码有问题,遂下载源码查看 。 下载编译使用过程略过 。 调试发现,当日期变化时,终端打印了一行错误信息: 从源码中搜索,所幸该错误信息打印只有两处,且出现在一个函数中,文件: rollingfileappender.cpp ,函数: rollover , rollover 方法用在日志的回滚处,比如这里是 DailyRollingFileAppender ,所以当日期变动时,会触发日志回滚,该方法被调用 。 但是这里面有两处一模一样的打印,因为无法单步调试,这里通过修改日志,发现错误打印是在函数最后,但是这里只打印了异常,没有任何错误信息,所以我们稍微修改一下代码,打印一下错误信息: 打印出来的错误信息如下: 没有太多的信息,只有一个错误码,尝试在网络上搜索了一下,没有太大的收货 。 只用使用死方法,加大量的日志,最终找到了错误的那一行代码: 这里是创建了一个文件输出流,用于日志文件的输出 。 rollover1->getActiveFileName() 返回日志文件的绝对路径 。 rollover1->getAppend() 返回一个bool变量,表示日志内容是否追加,值由配置文件中的 log4j.appender.R.Append=true 决定 。 注意,到这里涉及到了文件流,那么结合之前调试现象,程序刚启动的时候会创建目录(甚至时多级目录)和文件,但是日期变化时没有新目录创建 。 猜测,会不会是日期变动的时候, log4cxx 在使用文件流的时候,没有找到对应目录导致的错误 。 针对该猜想,我们手动创建日期目录,再次调试,发现,按照日期备份日志的功能OK了,说明我们的猜测是正确的,那么就是 log4cxx 的代码问题了,后面定位错误就方便了 。 我们继续查看 FileOutputStream 的代码,如下: 我们发现,这里就是个构造函数,唯一有逻辑的就是 open 方法的调用,那就查看一下: 这里发现一个 throw IOException(stat); ,应该就是前面打印错误信息的那个,所以这里的 stat 应该是2,这里先记录下,后面会用到 。 那么错误代码应该就是这一行了: 再进入看代码: 详细阅读代码,可以发现,这里使用了 apr 库进行文件读写 。 上网查询了一圈 apr 的资料,加上自己测试代码: 简单的测试发现,如果文件路径存在,则正常,如果不存在则异常,且异常时,state的值为2 。 这里就和上面的调试(staus code=2)呼应上了,那么这里的2就是apr库的一个错误码,表示目录不存在 。 到此,问题原因基本定位到了 。 综上,定位到了错误代码,找到了错误原因 。 log4cxx 使用 apr 库进行文件的读写操作,但是在日志回滚的时候,没有考虑到目录不存在的情况,导致调用 apr_file_open 方法失败,返回值2,并通过 std::exception 抛出来 。 既然找到了错误原因,那么就好解决了,既然是目录不存在,我们在打开文件之前创建目录不就行了 。 可以创建目录的地方有几处: 我们知道 rollingfileappender.cpp 文件是文件回滚的 appender,不应该有太多的实际操作,加在这里不合适; 。 细看 file.cpp ,我们发现,这就是一个对 apr 文件操作的封装,给外层提供操作文件的简单接口,这里应该正常抛出错误,如果有些地方就需要目录不存在报错呢?所以加在这里也不合适 。 那么应该加在 fileoutputstream.cpp 中,且类名也是文件输出流,它也需要保证文件输出正确,那在这里加目录的创建是对的,且加在 open 方法中最合理,且应该调用 file.cpp 中的方法才不会破坏代码的结构,我们发现 log4cxx::File 有 mkdir 方法的定义,那正好,省的我们自己封转,还需要去熟悉 apr 。 修改 fileoutputstream.cpp 文件的 open() 。 需要新增的代码如下: open() 的整体代码如下: 编译,测试,一切OK 。
1、背景
log4j.rootLogger=INFO,R
# Pattern to output the caller's file name and line number.
log4j.appender.R=org.apache.log4j.DailyRollingFileAppender
log4j.appender.R.ImmediateFlush=true
log4j.appender.R.Append=true
log4j.appender.R.DatePattern='${LOG_HOMR_DIR}/'yyyy-MM-dd'/log.log'
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss.SSS} %-5p [%c] %m %n
2、排查步骤
2.1、错误代码定位
log4cxx: Exception during rollover
try{
...
}catch (std::exception& ex) {
LogLog::warn(LOG4CXX_STR("Exception during rollover"));
//新增下面一行代码
LogLog::warn(LOG4CXX_STR("Exception during rollover, " + std::string(ex.what())));
}
Exception during rollover, status code=2
OutputStreamPtr os(new FileOutputStream(rollover1->getActiveFileName(), rollover1->getAppend()));
2.2、问题猜测
2.3、错误代码分析
FileOutputStream::FileOutputStream(const LogString& filename,bool append) : pool(), fileptr(open(filename, append, pool)) {}
apr_file_t* FileOutputStream::open(const LogString& filename,
bool append, Pool& pool) {
apr_fileperms_t perm = APR_OS_DEFAULT;
apr_int32_t flags = APR_WRITE | APR_CREATE;
if (append) {
flags |= APR_APPEND;
} else {
flags |= APR_TRUNCATE;
}
File fn;
fn.setPath(filename);
apr_file_t* fileptr = 0;
apr_status_t stat = fn.open(&fileptr, flags, perm, pool);
if (stat != APR_SUCCESS) {
throw IOException(stat);
}
return fileptr;
}
apr_status_t stat = fn.open(&fileptr, flags, perm, pool);
log4cxx_status_t File::open(apr_file_t** file, int flags,
int perm, Pool& p) const {
return apr_file_open(file, getPath(p), flags, perm, p.getAPRPool());
}
#include <cstdio>
#include <iostream>
#include <string>
#include <apr-1.0/apr_file_io.h>
#include <apr-1.0/apr_file_info.h>
using namespace std;
void printError(apr_status_t rv)
{
char errbuf[256];
apr_strerror(rv, errbuf, sizeof(errbuf));
cout << errbuf << endl;
}
string FILENAME = "../log/test.log";//日志路径
int main()
{
apr_pool_t *p;
apr_initialize();
apr_pool_create(&p, NULL);
apr_file_t *filetest = NULL;
filetest = NULL;
try
{
printError(stat);
stat = apr_file_open(&filetest, FILENAME.c_str(),
APR_WRITE | APR_CREATE | APR_APPEND,
APR_UREAD | APR_UWRITE | APR_GREAD, p);
cout << stat << endl;
printError(stat);//目录不存在时,这里打印2
apr_size_t byte = 3;
apr_file_write(filetest, "aaa", &byte);
apr_file_close(filetest);
}
catch (std::exception &e)
{
cout << e.what() << endl;
}
apr_pool_destroy(p);
return 0;
}
2.4、错误原因
3、解决方法
File pathFn(filename.substr(0, filename.find_last_of("/")));//linux的路径,windows需要使用\\
pathFn.mkdirs(pool);
apr_file_t* FileOutputStream::open(const LogString& filename,
bool append, Pool& pool) {
//下面两行代码是新增的
File pathFn(filename.substr(0, filename.find_last_of("/")));
pathFn.mkdirs(pool);
apr_fileperms_t perm = APR_OS_DEFAULT;
apr_int32_t flags = APR_WRITE | APR_CREATE;
if (append) {
flags |= APR_APPEND;
} else {
flags |= APR_TRUNCATE;
}
File fn;
fn.setPath(filename);
apr_file_t* fileptr = 0;
apr_status_t stat = fn.open(&fileptr, flags, perm, pool);
if (stat != APR_SUCCESS) {
throw IOException(stat);
}
return fileptr;
}
最后此篇关于记录一次排查log4cxx库按照日期回滚,不创建新目录的BUG的文章就讲到这里了,如果你想了解更多关于记录一次排查log4cxx库按照日期回滚,不创建新目录的BUG的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我想知道是否有办法只在编译时而不是在链接时将一些标志传递给编译器 (g++)。让我用一个例子来解释:我想这样构建我的程序: g++ -c source1.cpp -o source1.o g++ -c
我们在使用 Visual Studio 2012 在 Windows 7 64Bit 下使用 CMake (v2.8.12) 编译项目时遇到问题。CMake 给我们以下错误。我们已经尝试使用管理员权限
我是 Linux 的新用户,正在尝试下载 Avogadro 1.2.0。 我在使用命令 cmake ../ 时遇到问题,当我这样做时 -- The C compiler identification
我使用的是 Julia 1.1.1 和 Cxx 的 0.3.2 版。我发现当我从我定义的模块中调用 icxx""时出现错误,但在 REPL 中调用它时却没有。在我的 REPL 中: using Cxx
./cmake-3.4.1/bin/cmake .. -- The CXX compiler identification is unknown CMake Error at CMakeLists.t
我的代码库中有大量文件。我正在尝试使用具有一个文件 a.h. 的其他库来编译我的代码库。如果我在我的代码库中包含 say a.h 文件,我会遇到编译问题,该文件已经定义了一些具有与 a.h 中定义的枚
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Where does the value of CXX in a makefile come from? 我
我正在尝试使用 Cmake 将外部库安装到我的 C++ 项目中。我希望使用该库生成 Xcode 项目。在我的终端中,我从构建目录运行以下命令: cmake -G Xcode .. 这给了我以下错误:
我正在尝试在ubuntu 16.10上交叉编译ndn-cxx以在arduino yun上使用它。我正在关注steps。 当我尝试执行./waf命令时,出现以下错误: [ 30/141] Com
来自CXX Driver Quickstart guide我完全无法在 Ubuntu 或 CentOS VM 上完成新驱动程序的编译。 我已经按照这封信中的步骤执行了好几次,但我不确定哪里出了问题。
我有一个类,它有一个 bsoncxx::document::view view 作为 private 属性,还有一个从 Mongo 数据库获取文档并保存的方法结果在本地 bsoncxx::docume
这个问题在这里已经有了答案: gitignore does not ignore folder (7 个答案) 关闭 10 个月前。 我的 android 项目中有这个 .gitignore 文件,
我正在尝试在 Ubuntu14.4 上编译 mongo-cxx-driver-r3.1.1 。我已经按照说明从源代码安装了 mongo-c-driver-1.6.3。 pkg-config --cfl
我尝试让我的库目标依赖于 c++14。我怎样才能实现链接到我的库目标的目标也依赖于 c++14? 我尝试使用: add_library(lib SHARED ${FILES}) target_comp
帮助!我在提交我的项目使用 dbus-cxx 时可能犯了一个巨大的错误。该库似乎已被其作者遗弃,并且邮件列表对新成员不开放。这是我的问题: 没有关于使用 dbux-cxx 发送或接收包含非 POD 数
我使用 dbus-cxx通过 dbus 进行方法调用。现在有返回多个参数的方法,我不知道如何接收第一个参数以外的任何参数。 我初始化代理方法: DBus::MethodProxy& info_prox
CXX 测试框架的有效性如何,假设您正在围绕您编写的代码编写单元测试用例。代码中的任何错误也可能会转化为单元测试代码中的错误吗?这不是两个负数组成一个正数吗? 此外,花在 CXX 上的时间和精力至少等
在 cmake 已经预编译失败后,在 99% 编译时链接 CXX 可执行世界服务器。 [ 99%] Linking CXX executable worldserver /usr/bin/ld: ..
代码片段: target_test : test.cc $(CXX) $(CPPFLAGS) $(CFLAGS) test.cc 我知道 CXX 是一个变量(包含要调用的编译器命令),但我想知
我正在使用以下代码查询一个集合: bsoncxx::stdx::optional query_result = collection.find_one(bsoncxx::builder::stream
我是一名优秀的程序员,十分优秀!