aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/log/log.go
blob: b21ced9a6382070b3bf60454b186bfb1dfbdef27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright 2016 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package log provides functionality similar to standard log package with some extensions:
//  - verbosity levels
//  - global verbosity setting that can be used by multiple packages
//  - ability to disable all output
//  - ability to cache recent output in memory
package log

import (
	"bytes"
	"flag"
	"fmt"
	golog "log"
	"sync"
	"time"
)

var (
	flagV        = flag.Int("vv", 0, "verbosity")
	mu           sync.Mutex
	cacheMem     int
	cacheMaxMem  int
	cachePos     int
	cacheEntries []string
	prependTime  = true // for testing
)

// EnableCaching enables in memory caching of log output.
// Caches up to maxLines, but no more than maxMem bytes.
// Cached output can later be queried with CachedOutput.
func EnableLogCaching(maxLines, maxMem int) {
	mu.Lock()
	defer mu.Unlock()
	if cacheEntries != nil {
		Fatalf("log caching is already enabled")
	}
	if maxLines < 1 || maxMem < 1 {
		panic("invalid maxLines/maxMem")
	}
	cacheMaxMem = maxMem
	cacheEntries = make([]string, maxLines)
}

// Retrieves cached log output.
func CachedLogOutput() string {
	mu.Lock()
	defer mu.Unlock()
	buf := new(bytes.Buffer)
	for i := range cacheEntries {
		pos := (cachePos + i) % len(cacheEntries)
		if cacheEntries[pos] == "" {
			continue
		}
		buf.WriteString(cacheEntries[pos])
		buf.Write([]byte{'\n'})
	}
	return buf.String()
}

func Logf(v int, msg string, args ...interface{}) {
	mu.Lock()
	doLog := v <= *flagV
	if cacheEntries != nil && v <= 1 {
		cacheMem -= len(cacheEntries[cachePos])
		if cacheMem < 0 {
			panic("log cache size underflow")
		}
		timeStr := ""
		if prependTime {
			timeStr = time.Now().Format("2006/01/02 15:04:05 ")
		}
		cacheEntries[cachePos] = fmt.Sprintf(timeStr+msg, args...)
		cacheMem += len(cacheEntries[cachePos])
		cachePos++
		if cachePos == len(cacheEntries) {
			cachePos = 0
		}
		for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ {
			pos := (cachePos + i) % len(cacheEntries)
			cacheMem -= len(cacheEntries[pos])
			cacheEntries[pos] = ""
		}
		if cacheMem < 0 {
			panic("log cache size underflow")
		}
	}
	mu.Unlock()

	if doLog {
		golog.Printf(msg, args...)
	}
}

func Fatal(err error) {
	golog.Fatal(err)
}

func Fatalf(msg string, args ...interface{}) {
	golog.Fatalf(msg, args...)
}

type VerboseWriter int

func (w VerboseWriter) Write(data []byte) (int, error) {
	Logf(int(w), "%s", data)
	return len(data), nil
}