使用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(0) {
    m_console_attached = false;
    m_console_handle = NULL;
    m_prev_color = 0;
    InitializeCriticalSectionAndSpinCount(&m_criticalSection, 4000);
    // 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) + 1;
    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) + 1;
    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) + 1;
    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) + 1;
    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 = 10
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 = 10
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

 

 

转载于:https://www.cnblogs.com/mforestlaw/p/3501369.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值