如何在golang中使用并发日志?

时间:2016-07-29 19:45:47

标签: logging go concurrency

我已经搜索过它,但只找到了herehere,但它并没有解决我的问题。

我如何使用标准方法识别分隔JAVA和线程ID的并发日志的日志?因为如果我有一个并发方法,那么日志将在输出中混合打印,所以我认为LOGGER需要有一种方法来识别每个请求"线程"

例如:

package main

import "log"

func main() {

    /* simulating 10000 users requests  */
    i := 1;
    for ;i < 10000; i++ {
        go getHello(i)
    }

}

func getHello(d int){
    log.Printf("m=getHello, i=%d,begin", d)
    log.Println("m=getHello, processing the hello message")
    log.Printf("m=getHello, i=%d, end", d)
}

输出

2016/07/29 15:59:46 m=getHello, i=1017,begin
2016/07/29 15:59:46 m=getHello, processing the hello message
2016/07/29 15:59:46 m=getHello, i=1017, end
2016/07/29 15:59:46 m=getHello, processing the hello message
2016/07/29 15:59:46 m=getHello, i=1038, end
2016/07/29 15:59:46 m=getHello, i=311,begin
2016/07/29 15:59:46 m=getHello, processing the hello message
2016/07/29 15:59:46 m=getHello, i=311, end
2016/07/29 15:59:46 m=getHello, i=1023,begin
2016/07/29 15:59:46 m=getHello, processing the hello message
2016/07/29 15:59:46 m=getHello, i=1023, end
2016/07/29 15:59:46 m=getHello, i=991,begin
2016/07/29 15:59:46 m=getHello, processing the hello message
2016/07/29 15:59:46 m=getHello, i=991, end

正如您所看到的,如果我没有int标志,则无法知道请求记录的内容。在java,c,c#,delphy例如我简单记录了线程id,一切正常。

你能帮我在Golang做些类似的事吗?感谢。

Obs:如果我的问题不好,请在评论中告诉我原因

3 个答案:

答案 0 :(得分:7)

运行时没有可用的goroutine ID。 Goroutines比其他语言中的线程使用得更加自由,因此在更大的程序中,goroutine处理特定请求或项目的想法可能不太明确。

所以你需要自己通过一个ID进行日志记录。您可以自己通过int,但context模块对此非常方便:除了允许用户定义的值之外,它还可以处理项目取消和截止日期,这些也可能对您有用

这是一个如何使用它的粗略例子。我添加了一个简单的日志记录对象,可以用作上下文感知记录器。它记录创建上下文的ID。可能这个记录器会在一个真实的程序中包含它自己的包,并支持更多的日志包功能,而不仅仅是PrintfPrintln

package main

import (
    "fmt"
    "log"
    "sync"
    "context"
)

type logger int
const loggerID = "logger_id"

func (l logger) Printf(s string, args ...interface{}) {
    log.Printf("[id=%d] %s", l, fmt.Sprintf(s, args...))
}

func (l logger) Println(s string) {
    log.Printf("[id=%d] %s\n", l, s)
}

func ctxWithLoggerID(ctx context.Context, id int) context.Context {
    return context.WithValue(ctx, loggerID, id)
}

func getLogger(ctx context.Context) logger {
    return logger(ctx.Value(loggerID).(int))
}

func main() {
    ctx := context.Background()
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go hello(ctxWithLoggerID(ctx, i), &wg)
    }
    wg.Wait()
}

func hello(ctx context.Context, wg *sync.WaitGroup) {
    defer wg.Done()
    log := getLogger(ctx)
    log.Printf("hello begin")
    log.Println("hello, processing the hello message")
    log.Printf("hello, end")
}

这是您的问题的附带条件,但我在您的程序中添加了sync.WaitGroup,以便main等到所有工作人员完成后再退出。这允许使用较少数量的工作程序(10而不是原始10000)来测试代码。

答案 1 :(得分:0)

goroutines与线程甚至进程非常不同。它们由go运行时管理,而不是您的操作系统。如果你做了一些非常繁重的工作,甚至可能在进程之间跳过相同的goroutine,因此很难给它们提供唯一的ID。

对于您使用给定int的示例完全没问题,因为每个goroutine只处理一个数字。

答案 2 :(得分:0)

如果您不想在每个goroutine中传递一些int,那么您可以尝试使用goroutine iself中的math/rand包生成随机数。如果数字足够高(取决于您的负载),那么您将获得某种goroutine ID。

您可以找到生成here的随机数的示例。

您也可以用十六进制打印,因此很容易将该id与您在程序中可能拥有的其他int值区分开来。

这应该可以解决问题:

log.Printf("m=getHello, i=%#x,begin", d)

修改 此外,如果你想确定你的id在每个goroutine中是唯一的,那么你可以添加一些全局计数器并在每个goroutine中增加它。

import sync

var txCounter int
var counterMutex = &sync.Mutex{}

for {
    go func() {
        id := txCounter
        counterMutex.Lock()
        txCounter++
        counterMutex.Unlock()
        log.Printf("m=getHello, i=%#x,begin", id)
    }() 
}