使用log4cxx在GUI 程序中将信息输出到Console
之前看到有个方法是在项目属性设置里实现的
以VS2010为例:
右键Project选择Properties->Configuration Properties->Build Events->Post-Build Event,在Command Line后面添加
editbin /SUBSYSTEM:CONSOLE $(OUTDIR)\$(TargetName).exe
该文同时指出“使用AllocConsole()的方法,对printf和cout有效,而对log4cxx无效”。
此法虽然可行,但是无论是否有信息输出到Console,程序启动就会开启一个Console窗口。不灵活。
我要补充的是:使用AllocConsole()方法也是可以的,只是调用的位置不在主程序中,而是对log4cxx的源码稍做修改即可。
因为是控制台log相关的,所以我决定修改consoleappender.h和consoleappender.cpp,要做的就是为程序分配一个Console窗口,红色部分为添加的代码
consoleappender.h
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ #ifndef _LOG4CXX_CONSOLE_APPENDER_H
#define _LOG4CXX_CONSOLE_APPENDER_H #include <windows.h>
#include <tchar.h>
#include <log4cxx/writerappender.h> namespace log4cxx
{ /**
* ConsoleAppender appends log events to <code>stdout</code> or
* <code>stderr</code> using a layout specified by the user. The
* default target is <code>stdout</code>.
*/
class LOG4CXX_EXPORT ConsoleAppender : public WriterAppender
{
private:
void AllocConsole();
LogString target;
HWND m_wnd_console; public:
DECLARE_LOG4CXX_OBJECT(ConsoleAppender)
BEGIN_LOG4CXX_CAST_MAP()
LOG4CXX_CAST_ENTRY(ConsoleAppender)
LOG4CXX_CAST_ENTRY_CHAIN(AppenderSkeleton)
END_LOG4CXX_CAST_MAP() ConsoleAppender();
ConsoleAppender(const LayoutPtr& layout);
ConsoleAppender(const LayoutPtr& layout, const LogString& target);
~ConsoleAppender(); /**
* Sets the value of the <b>target</b> property. Recognized values
* are "System.out" and "System.err". Any other value will be
* ignored.
* */
void setTarget(const LogString& value); /**
* Returns the current value of the <b>target</b> property. The
* default value of the option is "System.out".
*
* See also #setTarget.
* */
LogString getTarget() const; void activateOptions(log4cxx::helpers::Pool& p);
void setOption(const LogString& option, const LogString& value);
static const LogString& getSystemOut();
static const LogString& getSystemErr(); private:
void targetWarn(const LogString& val);
static log4cxx::helpers::WriterPtr createWriter(const LogString& target); };
LOG4CXX_PTR_DEF(ConsoleAppender);
} //namespace log4cxx #endif //_LOG4CXX_CONSOLE_APPENDER_H
consoleappender.cpp
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <log4cxx/logstring.h>
#include <log4cxx/consoleappender.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/systemoutwriter.h>
#include <log4cxx/helpers/systemerrwriter.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/layout.h> using namespace log4cxx;
using namespace log4cxx::helpers; IMPLEMENT_LOG4CXX_OBJECT(ConsoleAppender) ConsoleAppender::ConsoleAppender()
: target(getSystemOut()), m_wnd_console(GetConsoleWindow())
{
AllocConsole();
} ConsoleAppender::ConsoleAppender(const LayoutPtr& layout1)
:target(getSystemOut()), m_wnd_console(GetConsoleWindow())
{
AllocConsole();
setLayout(layout1);
WriterPtr wr(createWriter(getSystemOut()));
setWriter(wr);
Pool p;
WriterAppender::activateOptions(p);
} ConsoleAppender::ConsoleAppender(const LayoutPtr& layout1, const LogString& target1)
:m_wnd_console(GetConsoleWindow()), target(target1)
{
AllocConsole();
setLayout(layout1);
WriterPtr wr(createWriter(target1));
setWriter(wr);
Pool p;
WriterAppender::activateOptions(p);
} ConsoleAppender::~ConsoleAppender()
{
finalize();
} const LogString& ConsoleAppender::getSystemOut() {
static const LogString name(LOG4CXX_STR("System.out"));
return name;
} const LogString& ConsoleAppender::getSystemErr() {
static const LogString name(LOG4CXX_STR("System.err"));
return name;
} WriterPtr ConsoleAppender::createWriter(const LogString& value) {
LogString v = StringHelper::trim(value); if (StringHelper::equalsIgnoreCase(v,
LOG4CXX_STR("SYSTEM.ERR"), LOG4CXX_STR("system.err"))) {
return new SystemErrWriter();
}
return new SystemOutWriter();
} void ConsoleAppender::setTarget(const LogString& value)
{
LogString v = StringHelper::trim(value); if (StringHelper::equalsIgnoreCase(v,
LOG4CXX_STR("SYSTEM.OUT"), LOG4CXX_STR("system.out")))
{
target = getSystemOut();
}
else if (StringHelper::equalsIgnoreCase(v,
LOG4CXX_STR("SYSTEM.ERR"), LOG4CXX_STR("system.err")))
{
target = getSystemErr();
}
else
{
targetWarn(value);
}
} LogString ConsoleAppender::getTarget() const
{
return target;
} void ConsoleAppender::targetWarn(const LogString& val)
{
LogLog::warn(((LogString) LOG4CXX_STR("["))
+ val + LOG4CXX_STR("] should be system.out or system.err."));
LogLog::warn(LOG4CXX_STR("Using previously set target, System.out by default."));
} void ConsoleAppender::activateOptions(Pool& p)
{
if(StringHelper::equalsIgnoreCase(target,
LOG4CXX_STR("SYSTEM.OUT"), LOG4CXX_STR("system.out")))
{
WriterPtr writer1(new SystemOutWriter());
setWriter(writer1);
}
else if (StringHelper::equalsIgnoreCase(target,
LOG4CXX_STR("SYSTEM.ERR"), LOG4CXX_STR("system.err")))
{
WriterPtr writer1(new SystemErrWriter());
setWriter(writer1);
}
WriterAppender::activateOptions(p);
} void ConsoleAppender::setOption(const LogString& option, const LogString& value)
{
if (StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("TARGET"), LOG4CXX_STR("target")))
{
setTarget(value);
}
else
{
WriterAppender::setOption(option, value);
}
} void ConsoleAppender::AllocConsole() {
if(NULL == m_wnd_console) {
::AllocConsole();
m_wnd_console = GetConsoleWindow();
FILE* stream = NULL;
_tfreopen_s(&stream, _T("CONOUT$"), _T("w"), stdout);
}
}
propertyconfigurator.cpp中修改如下
void PropertyConfigurator::doConfigure(const File& configFileName,
spi::LoggerRepositoryPtr& hierarchy)
{
std::locale::global(std::locale(""));
hierarchy->setConfigured(true); Properties props;
try {
InputStreamPtr inputStream = new FileInputStream(configFileName);
props.load(inputStream);
} catch(const IOException& ie) {
LogLog::error(((LogString) LOG4CXX_STR("Could not read configuration file ["))
+ configFileName.getPath() + LOG4CXX_STR("]."));
return;
} try {
doConfigure(props, hierarchy);
} catch(const std::exception& ex) {
LogLog::error(((LogString) LOG4CXX_STR("Could not parse configuration file ["))
+ configFileName.getPath() + LOG4CXX_STR("]."), ex);
}
}
在程序中写了一个对log4cxx的封装类CLoggerFramework,主要是为了修改控制台文本颜色和分离log(TRACE信息只输出到控制台,不输出到文件)
LoggerFramework.h
#pragma once
// log4cxx
#include "log4cxx/logger.h"
#include "log4cxx/propertyconfigurator.h"
using namespace log4cxx; #define _TRACE CLoggerFramework::LoggerInstance()->LoggerTrace
#define _INFO CLoggerFramework::LoggerInstance()->LoggerInfo
#define _WARN CLoggerFramework::LoggerInstance()->LoggerWarn
#define _ERROR CLoggerFramework::LoggerInstance()->LoggerError class CLoggerFramework {
public:
// TODO: add your methods here.
void LoggerTrace(const TCHAR* msg, ...);
void LoggerInfo (const TCHAR* msg, ...);
void LoggerWarn (const TCHAR* msg, ...);
void LoggerError(const TCHAR* msg, ...);
static CLoggerFramework* LoggerInstance(); private:
CLoggerFramework(void);
~CLoggerFramework(void);
void AttachConsoleAndSetTextColor(unsigned int color); unsigned int m_buf_len;
static CLoggerFramework* m_pLoggerFramework;
bool m_console_attached;
// 之前字体颜色,如果相同则不用再设置
unsigned int m_prev_color;
// 控制台句柄,用来设置字体颜色
HANDLE m_console_handle;
CRITICAL_SECTION m_criticalSection;
static LoggerPtr m_pFileLogger;
static LoggerPtr m_pConsoleLogger;
};
LoggerFramework.cpp
#include "stdafx.h"
#include "LoggerFramework.h" CLoggerFramework* CLoggerFramework::m_pLoggerFramework = NULL;
LoggerPtr CLoggerFramework::m_pFileLogger = NULL;
LoggerPtr CLoggerFramework::m_pConsoleLogger = NULL; // This is the constructor of a class that has been exported.
// see LoggerFramework.h for the class definition
CLoggerFramework::CLoggerFramework() : m_buf_len() {
m_console_attached = false;
m_console_handle = NULL;
m_prev_color = ;
InitializeCriticalSectionAndSpinCount(&m_criticalSection, );
// get logger
PropertyConfigurator::configure(g_moduleDirectory + _T("log4cxx.properties"));
m_pFileLogger = Logger::getLogger(_T("PMLog"));
m_pConsoleLogger = Logger::getRootLogger();
} CLoggerFramework::~CLoggerFramework() {
DeleteCriticalSection(&m_criticalSection);
SetConsoleTextAttribute(m_console_handle, FOREGROUND_RED | FOREGROUND_GREEN
| FOREGROUND_BLUE);
} CLoggerFramework* CLoggerFramework::LoggerInstance() {
if(NULL == m_pLoggerFramework) {
m_pLoggerFramework = new CLoggerFramework();
}
return m_pLoggerFramework;
} void CLoggerFramework::LoggerTrace(const TCHAR* msg, ...) {
EnterCriticalSection(&m_criticalSection);
AttachConsoleAndSetTextColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
va_list argList;
va_start(argList, msg);
m_buf_len = _vsctprintf(msg, argList) + ;
TCHAR* buffer = new TCHAR[m_buf_len]();
_vstprintf_s(buffer, m_buf_len, msg, argList);
va_end(argList);
std::tstring message(buffer);
delete[] buffer;
LOG4CXX_TRACE(m_pConsoleLogger, message.c_str());
LeaveCriticalSection(&m_criticalSection);
} void CLoggerFramework::LoggerInfo(const TCHAR* msg, ...) {
EnterCriticalSection(&m_criticalSection);
AttachConsoleAndSetTextColor(FOREGROUND_GREEN);
va_list argList;
va_start(argList, msg);
m_buf_len = _vsctprintf(msg, argList) + ;
TCHAR* buffer = new TCHAR[m_buf_len]();
_vstprintf_s(buffer, m_buf_len, msg, argList);
va_end(argList);
std::tstring message(buffer);
delete[] buffer;
LOG4CXX_INFO(m_pFileLogger, message.c_str());
LeaveCriticalSection(&m_criticalSection);
} void CLoggerFramework::LoggerWarn(const TCHAR* msg, ...) {
EnterCriticalSection(&m_criticalSection);
AttachConsoleAndSetTextColor(FOREGROUND_RED | FOREGROUND_GREEN);
va_list argList;
va_start(argList, msg);
m_buf_len = _vsctprintf(msg, argList) + ;
TCHAR* buffer = new TCHAR[m_buf_len]();
_vstprintf_s(buffer, m_buf_len, msg, argList);
va_end(argList);
std::tstring message(buffer);
delete[] buffer;
LOG4CXX_WARN(m_pFileLogger, message.c_str());
LeaveCriticalSection(&m_criticalSection);
} void CLoggerFramework::LoggerError(const TCHAR* msg, ...) {
EnterCriticalSection(&m_criticalSection);
AttachConsoleAndSetTextColor(FOREGROUND_RED);
va_list argList;
va_start(argList, msg);
m_buf_len = _vsctprintf(msg, argList) + ;
TCHAR* buffer = new TCHAR[m_buf_len]();
_vstprintf_s(buffer, m_buf_len, msg, argList);
va_end(argList);
std::tstring message(buffer);
delete[] buffer;
LOG4CXX_ERROR(m_pFileLogger, message.c_str());
LeaveCriticalSection(&m_criticalSection);
} void CLoggerFramework::AttachConsoleAndSetTextColor(unsigned int color) {
if(!m_console_attached) {
// 获取控制台句柄用来设置字体颜色
m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTitle(_T("日志监控"));
m_console_attached = true;
}
if(color != m_prev_color) {
SetConsoleTextAttribute(m_console_handle, color);
m_prev_color = color;
}
}
配置文件如下
#设置root logger为TRACE级别,使用了ca和fa两个Appender
log4j.rootLogger = TRACE, ca
log4j.logger.PMLog = INFO, fa, ha #对Appender ca进行设置
log4j.appender.ca = org.apache.log4j.ConsoleAppender
log4j.appender.ca.ImmediateFlush = true
log4j.appender.ca.Append = false
log4j.appender.ca.layout = org.apache.log4j.PatternLayout
log4j.appender.ca.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss,SSS}[%-5p]: %m%n #对Appender fa进行设置
log4j.appender.fa = org.apache.log4j.FileAppender
log4j.appender.fa.ImmediateFlush = true
log4j.appender.fa.File = output.log
log4j.appender.fa.Append = false
log4j.appender.fa.MaxFileSize = 2MB
log4j.appender.fa.MaxBackupIndex =
log4j.appender.fa.layout = org.apache.log4j.PatternLayout
log4j.appender.fa.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss,SSS}[%t][%c][%-5p]: %m%n #对Appender ha进行配置
log4j.appender.ha = org.apache.log4j.FileAppender
log4j.appender.ha.ImmediateFlush = true
log4j.appender.ha.File = output.htm
log4j.appender.ha.Append = false
log4j.appender.ha.MaxFileSize = 10MB
log4j.appender.ha.MaxBackupIndex =
log4j.appender.ha.layout = org.apache.log4j.HTMLLayout
log4j.appender.ha.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss,SSS}[%t][%c][%-5p]: %m%n
得到如下log效果
控制台:

文本log

htm网页log

另外关于每天产生一个日志文件的配置要注意,不要在File属性中指定文件名,否则不会按日期产生文件,要在DatePattern中指定,字符串最好用单引号分隔开,避免出现奇怪的文件后缀.还有,使用html格式输出时将layout指定为org.apache.log4j.HTMLLayout时layout.ConversionPattern指定的格式将失效.修改后的配置文件如下所示
#设置root logger为TRACE级别,使用了ca和fa两个Appender
log4j.rootLogger = TRACE, ca
log4j.logger.PMLog = INFO, fa, ha #对Appender ca进行设置
log4j.appender.ca = org.apache.log4j.ConsoleAppender
log4j.appender.ca.ImmediateFlush = true
log4j.appender.ca.Append = false
log4j.appender.ca.layout = org.apache.log4j.PatternLayout
log4j.appender.ca.layout.ConversionPattern = [%-5p][%d{yyyy-MM-dd HH:mm:ss,SSS}]: %m%n #对Appender fa进行设置
log4j.appender.fa = org.apache.log4j.DailyRollingFileAppender
log4j.appender.fa.ImmediateFlush = true
log4j.appender.fa.Append = true
#log4j.appender.fa.File = ./Logs/Text/ParkingLog.log
log4j.appender.fa.DatePattern = './Logs/Text/ParkingLog_'yyyy-MM-dd'.log'
log4j.appender.fa.layout = org.apache.log4j.PatternLayout
log4j.appender.fa.layout.ConversionPattern = [%-5p][%d{HH:mm:ss,SSS}]: %m%n #对Appender ha进行配置
log4j.appender.ha = org.apache.log4j.DailyRollingFileAppender
log4j.appender.ha.ImmediateFlush = true
log4j.appender.ha.Append = true
#log4j.appender.ha.File = ./Logs/Htm/ParkingLog.htm
log4j.appender.ha.DatePattern = './Logs/Html/ParkingLog_'yyyy-MM-dd'.html'
log4j.appender.ha.layout = org.apache.log4j.HTMLLayout
使用log4cxx在GUI 程序中将信息输出到Console的更多相关文章
- C++ gui程序附加dos输出窗口
C++ gui程序附加console qtcreator 1:在.pro文件中加入一句: CONFIG+= console 2:在运行设置里勾选在终端运行的选项 vs 1.新建gui项目 2.连接器( ...
- 最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示。 HardInfo 显示硬件具体信息,甚至包括一组八个的流行的性能基准程序,你可以用它们评估你的系统性能。 KInfoCenter 和 Lshw 也能够显示硬件的详细信息,并且可以从许多软件仓库中获取。
最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示. HardInfo 显示硬件具体信息,甚至包括一组八个的流 ...
- log4net--帮助程序员将日志信息输出到各种目标(控制台、文件、数据库等)的工具
1. log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. 2. Log4net的结构如下 ...
- linux脚本: 后台启动程序并重定向输出信息脚本
后台启动程序并重定向输出信息脚本 新建文件mstart, 写入下面代码. #!/bin/bash $1 1>/etc/null 2>&1 & 说明 1>/etc/nu ...
- 在cmd启动一个win32程序,printf把信息输出到启运它的那个CMD窗口
#define ProcessBasicInformation 0 typedef struct { DWORD ExitStatus; DWORD PebBaseAddress; DWORD Aff ...
- java程序运行中如果出现异常未被处理,将会被抛到java虚拟机进行处理,程序中断运行后被挂起,在页面输出错误信息(不会输出到console)
下面的代码中,因为我是使用 for (Iterator<Element> i = el.elements().iterator(); i.hasNext(); ) 迭代器遍历根节点的所有子 ...
- 使用PyQt来编写第一个Python GUI程序
原文:使用PyQt来编写第一个Python GUI程序 本文由 伯乐在线 - Lane 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonforengineers.com.欢迎加 ...
- Qt5+MSVC2015环境将VS2015编写的控制台程序转化为GUI程序
如题所述,如何将VS2015编写的控制台程序转化为Qt5+MSVC2015环境编译的GUI程序? 最近想到这个操作,类似于Linux下使用的命令行操作转到Windows下使用GUI操作,看了控制台的命 ...
- 给Win32 GUI程序增加控制台窗口的方法
给Win32 GUI程序增加控制台窗口的方法 2008年10月11日 星期六 下午 04:43 在Win32的GUI程序中,没有控制台窗口,我们输出调试信息时有些不方便,以往我的做法是使用Messag ...
随机推荐
- ASP.NET WebApi 增删改查
本篇是接着上一篇<ASP.NET WebApi 入门>来介绍的. 前言 习惯说 CRUD操作,它的意思是"创建. 读取. 更新和删除"四个基本的数据库操作.许多 HTT ...
- Code reviews and bad habits
图文大话编程语言史 投递人 itwriter 发布于 2014-03-07 17:16 评论(2) 有298人阅读 原文链接 [收藏] « » 英文原文:Code reviews and bad ...
- 开发使用混合式Winform模块
开发使用混合式Winform模块 1.Winform数据访问模式定义 传统的Winform程序模块:用于传统的数据库通讯获取数据,这种方式获取数据,方便快捷,可以用于常规的业务系统的场景,用于单机版软 ...
- iOS基础 - 单元测试
单元测试(unit testing):对软件中最小可测试单元进行检查和验证.一般面向过程的语言中,基本单元为函数,面向对象的语言中,基本单元通常是类,其实对于一个手机上的app来说基本单元也可以是一个 ...
- 列表类型转换(ConvertList<TSource, TResult>)
性能优化-列表类型转换(ConvertList<TSource, TResult>) 2013-12-16 16:55 by stevey, 426 阅读, 7 评论, 收藏, 编辑 之前 ...
- Day2:T1搜索 T2最小生成树
T1:广搜+判断矩形 注:如何判断搜的是否为矩形: 在广搜的时候,记录下边界的坐标,然后枚举一遍过去,如果搜到"."就是牛群,否则就是房间 瞥了一眼ccy的做法,据说是floodf ...
- C#通过接口与线程通信(捕获线程状态)介绍
C#通过接口与线程通信(捕获线程状态)介绍 摘要:本文介绍C#通过接口与线程通信(捕获线程状态),并提供简单的示例代码供参考. 提示:本文所提到的线程状态变化,并不是指线程启动.暂停.停止,而是说线程 ...
- 《cracking the coding intreview》——链表
前言 最近准备暑假回家回家修整一下,所以时间大部分用来完成项目上的工作,同时为了9月份的校招,晚上的时间我还在学习<cracking the coding intreview>,第二章链表 ...
- C#做的颜色工具
常常会用到绘制以及配色,每次看到 framework 里边的 KnowColor.xxx 我就一阵......到底啥颜色啊,干脆做一个一劳永逸的工具吧.功能包含: 可以实现枚举所有系统颜色 圆形.矩形 ...
- 7z文件格式及其源码
7z文件格式及其源码的分析(四) 这是7z文件格式及其源码的分析系列的第四篇. 上一篇讲到了7z文件静态结构的尾header部分.这一篇开始,将从7z实际压缩流程开始详细介绍7z文件尾header的详 ...