zoukankan      html  css  js  c++  java
  • 使用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
  • 相关阅读:
    小熊派4G开发板初体验
    空间换时间,查表法的经典例子
    C语言、嵌入式应用:TCP通信实践
    【实践】基于RT-Thread的智慧路灯案例实验分享
    STM32串口打印的那些知识
    【RT-Thread笔记】BH1750软件包的使用
    【RT-Thread笔记】OneNet软件包的使用
    串口通讯你真的会了吗?不妨看看这些经验
    三小记(2)
    audio标签实现简单的自定义播放器
  • 原文地址:https://www.cnblogs.com/mforestlaw/p/3501369.html
Copyright © 2011-2022 走看看