log4go 源码剖析

log4go 源码

下载

https://github.com/alecthomas/log4go.git

源码目录

.
..
config.go
examples 
filelog.go
.git
.gitignore
LICENSE
log4go.go
log4go_test.go
pattlog.go
README
socklog.go
termlog.go
wrapper.go

源码剖析

对问题的抽象和解决方案

这里写图片描述

从源码中一一找到对应代码

写什么 ?

log4- X 系列发迹于log4j , 然后被拓展到各种语言,提供便捷的基于等级制度的日志记录库。
那么写的当然是日志。 在log4go中对日志的描述统一为结构体 LogRecord

log4go.go

type LogRecord struct {
    Level   Level     // The log level // 日志等级
    Created time.Time // The time at which the log message was created (nanoseconds) // 纳秒级别的日期。日志发生的时间
    Source  string    // The message source // 日志的基本信息 ( 行号 , 文件名 ) 
    Message string    // The log message // 日志携带的消息
}

写到哪里 ?

这里首先规定了些日志的基本接口 LogWriter

log4go.go

// This is an interface for anything that should be able to write logs
type LogWriter interface {
    // This will be called to log a LogRecord message.
    LogWrite(rec *LogRecord)

    // This should clean up anything lingering about the LogWriter, as it is called before
    // the LogWriter is removed.  LogWrite should not be called after Close.
    Close()
}

即, 有一个能够接收LogRecord指针来记录对应的日志的 LogWrite接口。 有一个收尾的Close接口即可。

终端

termlog.go

// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
    "fmt"
    "io"
    "os"
    "time"
)

var stdout io.Writer = os.Stdout

// This is the standard writer that prints to standard output.
type ConsoleLogWriter struct {
    format string
    w      chan *LogRecord
}

// This creates a new ConsoleLogWriter
func NewConsoleLogWriter() *ConsoleLogWriter {
    consoleWriter := &ConsoleLogWriter{
        format: "[%T %D] [%L] (%S) %M",
        w:      make(chan *LogRecord, LogBufferLength),
    }
    go consoleWriter.run(stdout)
    return consoleWriter
}
// 自定义格式
func (c *ConsoleLogWriter) SetFormat(format string) {
    c.format = format
}
func (c *ConsoleLogWriter) run(out io.Writer) {
// 持续监听channel , 将数据格式化后打印到终端
    for rec := range c.w {
        fmt.Fprint(out, FormatLogRecord(c.format, rec))
    }
}

// This is the ConsoleLogWriter's output method.  This will block if the output
// buffer is full.
func (c *ConsoleLogWriter) LogWrite(rec *LogRecord) {
    c.w <- rec
}

// Close stops the logger from sending messages to standard output.  Attempts to
// send log messages to this logger after a Close have undefined behavior.
func (c *ConsoleLogWriter) Close() {
    close(c.w)
    time.Sleep(50 * time.Millisecond) // Try to give console I/O time to complete
}

文件

写文件的时候比写终端要复杂写, 主要是设计到一些日志文件的属性, 比如最大文件大小,最大文件行数,定期更换并备份日志文件 。。。。

下面是我将这些属性相关的代码剔除后的代码

filelog.go

// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
    "fmt"
    "os"
    "time"
)

// This log writer sends output to a file
type FileLogWriter struct {
    rec chan *LogRecord
    // The opened file
    filename string
    file     *os.File
    // The logging format
    format string
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
    w.rec <- rec
}

func (w *FileLogWriter) Close() {
    close(w.rec)
    w.file.Sync()
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
    w := &FileLogWriter{
        rec:       make(chan *LogRecord, LogBufferLength),
        filename:  fname,
        format:    "[%D %T] [%L] (%S) %M",
    }

    // open the file for the first time
    if err := w.intRotate(); err != nil {
        fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
        return nil
    }

    go func() {
        defer func() {
            if w.file != nil {
                fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
                w.file.Close()
            }
        }()

        for {
            select {
            // 这里使用select 是因为完整的代码中还有别的channel . 属性相关。
            case rec, ok := <-w.rec:
                if !ok {
                    return
                }
                fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
                    return
                }

                // Perform the write
                n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
                if err != nil {
                    fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
                    return
                }
            }
        }
    }()

    return w
}



// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
    w.format = format
    return w
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
    // Close any log file that may be open
    if w.file != nil {
        fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
        w.file.Close()
    }
    // Open the log file
    fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
    if err != nil {
        return err
    }
    w.file = fd
    return nil
}

// 预定义出一种XML格式
// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
    return NewFileLogWriter(fname, rotate).SetFormat(
        `   <record level="%L">
        <timestamp>%D %T</timestamp>
        <source>%S</source>
        <message>%M</message>
    </record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
Socket

socklog.go

// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
    "encoding/json"
    "fmt"
    "net"
    "os"
)

// This log writer sends output to a socket
type SocketLogWriter chan *LogRecord

// This is the SocketLogWriter's output method
func (w SocketLogWriter) LogWrite(rec *LogRecord) {
    w <- rec
}

func (w SocketLogWriter) Close() {
    close(w)
}

func NewSocketLogWriter(proto, hostport string) SocketLogWriter {
    sock, err := net.Dial(proto, hostport)
    if err != nil {
        fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err)
        return nil
    }

    w := SocketLogWriter(make(chan *LogRecord, LogBufferLength))

    go func() {
        defer func() {
            if sock != nil && proto == "tcp" {
                sock.Close()
            }
        }()

        for rec := range w {
            // Marshall into JSON
            js, err := json.Marshal(rec)
            if err != nil {
                fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
                return
            }

            _, err = sock.Write(js)
            if err != nil {
                fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err)
                return
            }
        }
    }()

    return w
}

什么格式写 ?

日志的最终形式必然是字符串。这边需要从LogRecord结构到对应字符串的序列化工具

JSON

使用encoding/json库的支持, 直接对LogRecord结构体进行序列化。在写入socket的时候使用的是这种序列化方式。

自定义格式

pattlog.go

// 目前支持的格式
// Known format codes:
// %T - Time (15:04:05 MST) 
// %t - Time (15:04) 
// %D - Date (2006/01/02)
// %d - Date (01/02/06)
// %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT)
// %S - Source
// %M - Message  // 目前包含文件名和行号
// Ignores unknown formats
// Recommended: "[%D %T] [%L] (%S) %M"
// 下面的接口按照规定的格式, 将LogRecord结构体序列化为字符串。
func FormatLogRecord(format string, rec *LogRecord) string {
    if rec == nil {
        return "<nil>"
    }
    if len(format) == 0 {
        return ""
    }

    out := bytes.NewBuffer(make([]byte, 0, 64))
    secs := rec.Created.UnixNano() / 1e9

    cache := *formatCache // 这里有个本地的时间缓存。秒级别刷新时间和对应的字符串
    if cache.LastUpdateSeconds != secs {
        month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year()
        hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second()
        zone, _ := rec.Created.Zone()
        updated := &formatCacheType{
            LastUpdateSeconds: secs,
            shortTime:         fmt.Sprintf("%02d:%02d", hour, minute),
            shortDate:         fmt.Sprintf("%02d/%02d/%02d", day, month, year%100),
            longTime:          fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone),
            longDate:          fmt.Sprintf("%04d/%02d/%02d", year, month, day),
        }
        cache = *updated
        formatCache = updated
    }

    // Split the string into pieces by % signs
    pieces := bytes.Split([]byte(format), []byte{'%'})

    // Iterate over the pieces, replacing known formats
    // 下面按照指定格式拼接最终的字符串
    for i, piece := range pieces {
        if i > 0 && len(piece) > 0 {
            switch piece[0] {
            case 'T':
                out.WriteString(cache.longTime)
            case 't':
                out.WriteString(cache.shortTime)
            case 'D':
                out.WriteString(cache.longDate)
            case 'd':
                out.WriteString(cache.shortDate)
            case 'L':
                out.WriteString(levelStrings[rec.Level])
            case 'S':
                out.WriteString(rec.Source)
            case 's':
                slice := strings.Split(rec.Source, "/")
                out.WriteString(slice[len(slice)-1])
            case 'M':
                out.WriteString(rec.Message)
            }
            if len(piece) > 1 {
                out.Write(piece[1:])
            }
        } else if len(piece) > 0 {
            out.Write(piece)
        }
    }
    out.WriteByte('\n')

    return out.String()
}
XML

基于自定义格式,定义出符合XML规范的输出

filelog.go 这里定义了一种XML的输出格式, 用于写入文件。

    <record level="%L">
        <timestamp>%D %T</timestamp>
        <source>%S</source>
        <message>%M</message>
    </record>`

日志的等级

log4go.go

const (
    FINEST Level = iota
    FINE
    DEBUG
    TRACE
    INFO
    WARNING
    ERROR
    CRITICAL
)

仅仅接收自己关心的日志

// log4go.go

// A Filter represents the log level below which no log records are written to
// the associated LogWriter.
type Filter struct {
    Level Level //关心的等级
    LogWriter   // 对应的Writer
}

// A Logger represents a collection of Filters through which log messages are
// written.
type Logger map[string]*Filter  

// 下面提取关键代码 Log 接口
// Send a log message with manual level, source, and message.
func (log Logger) Log(lvl Level, source, message string) {
    skip := true

    //查看是否有接收的着。 只要有一个即可
    for _, filt := range log {
        if lvl >= filt.Level {
            skip = false
            break
        }
    }
    if skip {
        return
    }
    // 利用上下文组装LogRecord
    // Make the log record
    rec := &LogRecord{
        Level:   lvl,
        Created: time.Now(),
        Source:  source,
        Message: message,
    }
    // 发送给所有希望接收这个日志的Filter
    // Dispatch the logs
    for _, filt := range log {
        if lvl < filt.Level {
            continue
        }
        filt.LogWrite(rec)
    }
}

使用配置文件配置一个Logger

config.go 核心代码解析

package log4go
// xml 对应的结构体
type xmlProperty struct {
    Name  string `xml:"name,attr"`
    Value string `xml:",chardata"`
}
// xml 对应的结构体
type xmlFilter struct {
    Enabled  string        `xml:"enabled,attr"`
    Tag      string        `xml:"tag"`
    Level    string        `xml:"level"`
    Type     string        `xml:"type"`
    Property []xmlProperty `xml:"property"`
}

type xmlLoggerConfig struct {
    Filter []xmlFilter `xml:"filter"`
}
// Load XML configuration; see examples/example.xml for documentation
func (log Logger) LoadConfiguration(filename string) {
    log.Close()
    // 打开,解析文件
    // Open the configuration file
    fd, err := os.Open(filename)
    if err != nil {
        fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err)
        os.Exit(1)
    }

    contents, err := ioutil.ReadAll(fd)
    if err != nil {
        fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err)
        os.Exit(1)
    }

    xc := new(xmlLoggerConfig)
    if err := xml.Unmarshal(contents, xc); err != nil {
        fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err)
        os.Exit(1)
    }
    // 依次解析每个Filter
    for _, xmlfilt := range xc.Filter {
        var filt LogWriter
        var lvl Level
        bad, good, enabled := false, true, false
        // 检查基本属性是否完整
        // Check required children
        if len(xmlfilt.Enabled) == 0 {
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename)
            bad = true
        } else {
            enabled = xmlfilt.Enabled != "false"
        }
        if len(xmlfilt.Tag) == 0 {
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename)
            bad = true
        }
        if len(xmlfilt.Type) == 0 {
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename)
            bad = true
        }
        if len(xmlfilt.Level) == 0 {
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename)
            bad = true
        }
        // 确定关心的最低等级
        switch xmlfilt.Level {
        case "FINEST":
            lvl = FINEST
        case "FINE":
            lvl = FINE
        case "DEBUG":
            lvl = DEBUG
        case "TRACE":
            lvl = TRACE
        case "INFO":
            lvl = INFO
        case "WARNING":
            lvl = WARNING
        case "ERROR":
            lvl = ERROR
        case "CRITICAL":
            lvl = CRITICAL
        default:
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level)
            bad = true
        }

        // Just so all of the required attributes are errored at the same time if missing
        if bad {
            os.Exit(1)
        }
        // 利用额外属性创建并初始化对应的Filter
        switch xmlfilt.Type {
        case "console":
            filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled)
        case "file":
            filt, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled)
        case "xml":
            filt, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled)
        case "socket":
            filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled)
        default:
            fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type)
            os.Exit(1)
        }

        // Just so all of the required params are errored at the same time if wrong
        if !good {
            os.Exit(1)
        }

        // If we're disabled (syntax and correctness checks only), don't add to logger
        if !enabled {
            continue
        }
        // 保存Filter
        log[xmlfilt.Tag] = &Filter{lvl, filt}
    }
}

辅助接口

以上的接口已经足够配置出强大灵活的日志系统了。不过很多情景下我其实并不需要过度的定制化。为了能够简化代码,log4go 提供了一系列的傻瓜接口。

wrapper.go

import (
    "errors"
    "fmt"
    "os"
    "strings"
)

var (
    Global Logger //提供一个默认的logger , 所有的操作都是对这个logger操作的 
)

func init() {
    Global = NewDefaultLogger(DEBUG)
}

// Wrapper for (*Logger).LoadConfiguration
func LoadConfiguration(filename string) {
    Global.LoadConfiguration(filename)
}

// Wrapper for (*Logger).AddFilter
func AddFilter(name string, lvl Level, writer LogWriter) {
    Global.AddFilter(name, lvl, writer)
}

// Wrapper for (*Logger).Close (closes and removes all logwriters)
func Close() {
    Global.Close()
}
// 比如 : 
func Crash(args ...interface{}) {
    if len(args) > 0 {
        Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...)
    }
    panic(args)
}
// 比如 : 
// Compatibility with `log`
func Exit(args ...interface{}) {
    if len(args) > 0 {
        Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...)
    }
    Global.Close() // so that hopefully the messages get logged
    os.Exit(0)
}

// 比如 : 
// Utility for finest log messages (see Debug() for parameter explanation)
// Wrapper for (*Logger).Finest
func Finest(arg0 interface{}, args ...interface{}) {
    const (
        lvl = FINEST
    )
    switch first := arg0.(type) {
    case string:
        // Use the string as a format string
        Global.intLogf(lvl, first, args...)
    case func() string:
        // Log the closure (no other arguments used)
        Global.intLogc(lvl, first)
    default:
        // Build a format string so that it will be similar to Sprint
        Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...)
    }
}

log4cplus是C++编写的开源的日志系统,功能非常全面,用到自己开发的工程中会比较专业的,:),本文介绍了log4cplus基本概念,以及如何安装,配置。 ### 简介 ### log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统.受Apache Software License保护。作者是Tad E. Smith。log4cplus具有线程安全、灵活、以及多粒度控制的特点,通过将信息划分优先级使其可以面向程序调试、运行、测试、和维护等全生命周 期; 你可以选择将信息输出到屏幕、文件、 NT event log、甚至是远程服务器;通过指定策略对日志进行定期备份等等。 ### 下载 ### 最新的log4cplus可以从以下网址下载 http://log4cplus.sourceforge.net本文使用的版本为:1.0.2 ### 安装 ### 1. linux下安装 tar xvzf log4cplus-x.x.x.tar.gz cd log4cplus-x.x.x ./configure --prefix=/where/to/install make make install 这里我采用缺省安装路径:/usr/local,下文如无特别说明,均以此路径为准。 2. windows下安装 不需要安装,有一个msvc6存放包括源代码和用例在内的开发工程(for VC6 only),使用之前请先编译 "log4cplus_dll class"工程生成dll,或者编译"log4cplus_static class"工程生成lib. ### 使用前的配置 ### 1. linux下的配置 确保你的Makefile中包含 /usr/local/lib/liblog4cplus.a(静态库)或 -llog4cplus(动态库)即可, 头文件在/usr/local/include/log4cplus目录下。对于动态库,要想正常使用,还得将库安装路径加入到 LD_LIBRARY_PATH 中,我一般是这样做的:以管理员身份登录,在/etc/ld.so.conf中加入安装路径,这里 是/usr/local/lib,然后执行ldconfig使设置生效即可。 2. windows下的配置 将"log4cplus_dll class"工程或"log4cplus_static class"工程的dsp 文件插入到你的工程中,或者直接 把两个工程编译生成的库以及头文件所在目录放到你的工程的搜索路径中,如果你使用静态库,请在你的工程中 "project/setting/C++"的preprocessor definitions中加入LOG4CPLUS_STATIC。 ### 构成要素介绍 ### 虽然功能强大,应该说log4cplus用起来还是比较复杂的,为了更好地使用它,先介绍一下它的基本要素。 Layouts :布局器,控制输出消息的格式. Appenders :挂接器,与布局器紧密配合,将特定格式的消息输出到所挂接的设备终端 (如屏幕,文件等等)。 Logger :记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行 记录时,就需要生成一个logger。 Categories :分类器,层次化(hierarchy)的结构,用于对被记录信息的分类,层次中 每一个节点维护一个logger的所有信息。 Priorities :优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。 本文介绍了log4cplus基本概念,以及如何安装,配置,下一篇将通过例子介绍如何使用log4cplus。 (二) 本文介绍了使用log4cplus有六个步骤,并提供了一些例子引导你了解log4cplus的基本使用。 ### 基本使用 ### 使用log4cplus有六个基本步骤: 1. 实例化一个appender对象 2. 实例化一个layout对象 3. 将layout对象绑定(attach)到appender对象 4. 实例化一个logger对象,调用静态函数:log4cplus::Logger::getInstance("logger_name") 5. 将appender对象绑定(attach)到logger对象,如省略此步骤,标准输出(屏幕)appender对象会绑定到logger 6. 设置logger的优先级,如省略此步骤,各种有限级的消息都将被记录 下面通过一些例子来了解log4cplus的基本使用。 〖例1〗 cpp 代码 /* 严格实现步骤1-6,appender输出到屏幕, 其中的布局格式和LogLevel后面会详细解释。*/ #include #include #include using namespace log4cplus; using namespace log4cplus::helpers; int main(){ /* step 1: Instantiate an appender object */ SharedObjectPtr _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr _layout(new PatternLayout(pattern)); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* step 6: Set a priority for the logger */ _logger.setLogLevel(ALL_LOG_LEVEL); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } 输出结果: 10/14/04 09:06:24 - This is the FIRST log message... [main.cpp:31] 10/14/04 09:06:25 - This is the SECOND log message... [main.cpp:33] 〖例2〗 /* 简洁使用模式,appender输出到屏幕。 */ #include #include using namespace log4cplus; using namespace log4cplus::helpers; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") return 0; } 输出结果: DEBUG - This is the FIRST log message... WARN - This is the SECOND log message... 〖例3〗 /* iostream模式,appender输出到屏幕。 */ #include #include #include /* 其实这个东东还是放到log4cplus头文件中比较合适些,个人意见:) */using namespace log4cplus; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_TRACE(_logger, "This is" << " just a t" << "est." << std::endl) LOG4CPLUS_DEBUG(_logger, "This is a bool: " << true) LOG4CPLUS_INFO(_logger, "This is a char: " << 'x') LOG4CPLUS_WARN(_logger, "This is a int: " << 1000) LOG4CPLUS_ERROR(_logger, "This is a long(hex): " << std::hex << 100000000) LOG4CPLUS_FATAL(_logger, "This is a double: " << std::setprecision(15) << 1.2345234234) return 0; } 输出结果: DEBUG - This is a bool: 1 INFO - This is a char: x WARN - This is a int: 1000 ERROR - This is a long(hex): 5f5e100 FATAL - This is a double: 1.2345234234 〖例4〗 /* 调试模式,通过loglog来控制输出调试、警告或错误信息,appender输出到屏幕。 */ #include #include using namespace log4cplus::helpers; void printMsgs(void) { std::cout << "Entering printMsgs()..." << std::endl; LogLog::getLogLog()->debug("This is a Debug statement..."); LogLog::getLogLog()->warn("This is a Warning..."); LogLog::getLogLog()->error("This is a Error..."); std::cout << "Exiting printMsgs()..." << std::endl << std::endl; } int main() { /* LogLog类实现了debug, warn, error 函数用于输出调试、警告或错误信息, 同时提供了两个方法来进一步控制所输出的信息,其中: setInternalDebugging方法用来控制是否屏蔽输出信息中的调试信息,当输入 参数为false则屏蔽,缺省设置为false。 setQuietMode方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽, 缺省设置为false。 LogLog::getLogLog()->setInternalDebugging(false); */ printMsgs(); std::cout << "Turning on debug..." << std::endl; LogLog::getLogLog()->setInternalDebugging(true); printMsgs(); std::cout << "Turning on quiet mode..." << std::endl; LogLog::getLogLog()->setQuietMode(true); printMsgs(); return 0; } 输出结果: Entering printMsgs()... log4cplus:WARN This is a Warning... log4cplus:ERROR This is a Error... Exiting printMsgs()... Turning on debug... Entering printMsgs()... log4cplus: This is a Debug statement... log4cplus:WARN This is a Warning... log4cplus:ERROR This is a Error... Exiting printMsgs()... Turning on quiet mode... Entering printMsgs()... Exiting printMsgs()... 需要指出的是,输出信息中总是包含"log4cplus:"前缀,有时候会感觉不爽,这是因为LogLog在实现时候死定了要这么写: LogLog::LogLog() : mutex(LOG4CPLUS_MUTEX_CREATE), debugEnabled(false), quietMode(false), PREFIX( LOG4CPLUS_TEXT("log4cplus: ") ), WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN ") ), ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR ") ) { } 你可以把这些前缀换成自己看着爽的提示符号,然后重新编译,hihi。除非万不得已或者实在郁闷的不行,否则还是不要这样干。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值