之前看到有个方法是在项目属性设置里实现的

以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的更多相关文章

  1. C++ gui程序附加dos输出窗口

    C++ gui程序附加console qtcreator 1:在.pro文件中加入一句: CONFIG+= console 2:在运行设置里勾选在终端运行的选项 vs 1.新建gui项目 2.连接器( ...

  2. 最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示。 HardInfo 显示硬件具体信息,甚至包括一组八个的流行的性能基准程序,你可以用它们评估你的系统性能。 KInfoCenter 和 Lshw 也能够显示硬件的详细信息,并且可以从许多软件仓库中获取。

    最简单的方法是使用标准的 Linux GUI 程序之一: i-nex 收集硬件信息,并且类似于 Windows 下流行的 CPU-Z 的显示. HardInfo 显示硬件具体信息,甚至包括一组八个的流 ...

  3. log4net--帮助程序员将日志信息输出到各种目标(控制台、文件、数据库等)的工具

    1. log4net库是Apache log4j框架在Microsoft .NET平台的实现,是一个帮助程序员将日志信息输出到各种目标(控制台.文件.数据库等)的工具. 2. Log4net的结构如下 ...

  4. linux脚本: 后台启动程序并重定向输出信息脚本

    后台启动程序并重定向输出信息脚本 新建文件mstart, 写入下面代码. #!/bin/bash $1 1>/etc/null 2>&1 & 说明 1>/etc/nu ...

  5. 在cmd启动一个win32程序,printf把信息输出到启运它的那个CMD窗口

    #define ProcessBasicInformation 0 typedef struct { DWORD ExitStatus; DWORD PebBaseAddress; DWORD Aff ...

  6. java程序运行中如果出现异常未被处理,将会被抛到java虚拟机进行处理,程序中断运行后被挂起,在页面输出错误信息(不会输出到console)

    下面的代码中,因为我是使用 for (Iterator<Element> i = el.elements().iterator(); i.hasNext(); ) 迭代器遍历根节点的所有子 ...

  7. 使用PyQt来编写第一个Python GUI程序

    原文:使用PyQt来编写第一个Python GUI程序 本文由 伯乐在线 - Lane 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonforengineers.com.欢迎加 ...

  8. Qt5+MSVC2015环境将VS2015编写的控制台程序转化为GUI程序

    如题所述,如何将VS2015编写的控制台程序转化为Qt5+MSVC2015环境编译的GUI程序? 最近想到这个操作,类似于Linux下使用的命令行操作转到Windows下使用GUI操作,看了控制台的命 ...

  9. 给Win32 GUI程序增加控制台窗口的方法

    给Win32 GUI程序增加控制台窗口的方法 2008年10月11日 星期六 下午 04:43 在Win32的GUI程序中,没有控制台窗口,我们输出调试信息时有些不方便,以往我的做法是使用Messag ...

随机推荐

  1. OpenStack最新版本Folsom架构解析

    OpenStack最新版本Folsom架构解析摘要:OpenStack的第6版,版本代号为Folsom的最新版于今年九月底正式发布,Folsom将支持下一代软件定义网络(SDN)作为其核心组成部分.F ...

  2. 重构 ORM 中的 Sql 生成

    Rafy 领域实体框架设计 - 重构 ORM 中的 Sql 生成   前言 Rafy 领域实体框架作为一个使用领域驱动设计作为指导思想的开发框架,必然要处理领域实体到数据库表之间的映射,即包含了 OR ...

  3. ActionInvoker

    ActionInvoker 还是这张图: 当ControllerFactory生成Controller实例后,这时就需要使用ActionInvoker来选择调用一个合适的Action执行.ASP.NE ...

  4. Power BI移动端应用

    随笔- 420  文章- 6  评论- 1927  无处不在的商业智能---Power BI移动端应用   此篇来自于微软商业智能网站的官方博客团队发布的Power BI在线资料其中的一部分,完整版地 ...

  5. javascript生成自定义的arcgis simpletoolbar

    javascript生成自定义的arcgis simpletoolbar 最近在学习ARCGIS for Javascript过程中,在ESRI的在线帮助上看见了这样一个示例,查看源码后,觉得左侧工具 ...

  6. 解决MSSQL 2008不能用IP登录的问题

        解决MSSQL 2008不能用IP登录的问题   前提准备:mssql 2008已安装好了一个实例(我按默认情况下安装的实例是:SQLEXPRESS),并安装了SQL Server Manag ...

  7. 在C#中模拟Javascript的setTimeout方法

    在C#中模拟Javascript的setTimeout方法 背景 每种语言都有自己的定时器(Timer),很多人熟悉Javascript中的setInterval和setTimeout,在Javasc ...

  8. webservice的调用方法

    一.WebService在cs后台程序中的调用 A.通过命名空间和类名直接调用 示例: WebService ws = new WebService(); string s = ws.HelloWor ...

  9. JavaScript中变量声明有var和没var的区别

    JavaScript中变量声明有var和没var的区别 JavaScript中有var和没var的区别 Js中的变量声明的作用域是以函数为单位,所以我们经常见到避免全局变量污染的方法是 (functi ...

  10. Machine Learning/Random Projection

    这次突然打算写点dimension reduction的东西, 虽然可以从PCA, manifold learning之类的东西开始, 但很难用那些东西说出好玩的东西. 这次选择的是一个不太出名但很有 ...