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

以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. Bootstrap导航悬浮顶部,stickUp

    stickUp 一个 jQuery 插件 这是一个简单的jQuery插件,它能让页面目标元素 “固定” 在浏览器窗口的顶部,即便页面在滚动,目标元素仍然能出现在设定的位置.此插件可以在多页面的网站上工 ...

  2. IDE编程环境

    Vim配置及说明——IDE编程环境 目录 Vim配置及说明——IDE编程环境 1.基本及字体 2.插件管理 3.主题风格 4.窗口设置 5.目录树导航 6.标签导航 7.taglist 8.多文档编辑 ...

  3. .NET面向对象特性之封装

    .NET面向对象特性之封装 面向对象的基本内容由:类.对象.属性.方法.字段构成. 面向对象的三大特性:继承.多态.封装. 关于面向对象的特性很多人都把目光转向了继承.多态和接口,却很少有人提及过封装 ...

  4. PureMVC(JS版)源码解析

    PureMVC(JS版)源码解析:总结   PureMVC源码中设计到的11个类已经全部解析完了,回首想想,花了一周的时间做的这点事情还是挺值得的,自己的文字组织表达能力和对pureMVC的理解也在写 ...

  5. 反向代理-- WEB服务的加速器[转]

    昨天j.L问我http cache怎么设置,当时脑子有点糊涂,一时没想到其实他问的就是反向代理如何设置. 首发:PHP CUP xiaobao 什么是反向代理?反 向代理就是代理服务器(如ISA.Sq ...

  6. [转]浅谈PCA的适用范围

    线性代数主要讲矩阵,矩阵就是线性变换,也就是把直线变成直线的几何变换,包括过原点的旋转.镜射.伸缩.推移及其组合.特征向量是对一个线性变换很特殊的向量:只有他们在此变换下可保持方向不变,而对应的特征值 ...

  7. IOS开发之路三(XML解析之GDataXML的使用)

    最近再做一个项目需要用到xml的解析.今天查了一些资料自己做了一个小demo.纯OC没有界面.. 在IOS平台上进行XML文档的解析有很多种方法,在SDK里面有自带的解析方法,但是大多情况下都倾向于用 ...

  8. 利用微信公众平台实现自动回复消息—java版

    最近公司需要拿微信公众平台做个东西,所以就开始了最基本学习,网上很多是php版的,对于我这个只会java,不会php的就只能在网上找点只言片语来一点一点学习了.不费话了直接贴图看效果(很简单的). 不 ...

  9. Using CrunchBase API

    Let us have fun with CrunchBase API. What can CrunchBase API give us? They said: https://developer.c ...

  10. CentOS 下搭建部署独立SVN服务器全程详解(5.5)

    SVN服务器有2种运行方式: 1.独立服务器(例如:svn://xxx.com/xxx): 2.借助apache   (例如:http://svn.xxx.com/xxx): 为了不依赖apache, ...