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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
// Package tracerx implements a simple tracer function that uses environment
// variables to control the output. It is a generalized package inspired by
// git's GIT_TRACE mechanism.
//
// By default, tracerx will look for the TRACERX_TRACE environment variable.
// The default can by changed by setting the DefaultKey.
//
// The values control where the tracing is output as follows:
// unset, 0, or "false": no output
// 1, 2: stderr
// absolute path: output will be written to the file
// 3 - 10: output will be written to that file descriptor
//
// By default, messages will be prefixed with "trace: ". This prefix can be
// modified by setting Prefix.
//
// Each key can have an associated performance key, e.g. TRACERX_TRACE_PERFORMANCE.
// If this key is 1 or "true" performance output will be written to the same output
// as the tracing output.
package tracerx
import (
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
)
var (
DefaultKey = "TRACERX"
Prefix = "trace: "
tracers map[string]*tracer
tracerLock sync.Mutex
)
type tracer struct {
enabled bool
performance bool
w io.Writer
}
// Printf writes a trace message for the DefaultKey
func Printf(format string, args ...interface{}) {
PrintfKey(DefaultKey, format, args...)
}
// PrintfKey writes a trace message for the given key
func PrintfKey(key, format string, args ...interface{}) {
tracer := getTracer(key)
if tracer.enabled {
fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+Prefix+format+"\n", args...)
return
}
}
// PerformanceSince writes out the time since the given time, if
// tracing for the default key is enabled and the performance key is set
func PerformanceSince(what string, t time.Time) {
PerformanceSinceKey(DefaultKey, what, t)
}
// PerformanceSince writes out the time since the given time, if
// tracing for the given key is enabled and the performance key is set
func PerformanceSinceKey(key, what string, t time.Time) {
tracer := getTracer(key)
if tracer.performance {
since := time.Since(t)
fmt.Fprintf(tracer.w, time.Now().Format("15:04:05.000000 ")+"performance %s: %.9f s\n", what, since.Seconds())
}
}
// Disable will disable tracing for the given key, regardless of
// the environment variable
func Disable(key string) {
uppedKey := strings.ToUpper(key)
if tracer, ok := tracers[uppedKey]; ok {
tracer.enabled = false
}
}
// Enable will enable tracing for the given key, regardless of
// the environment variable
func Enable(key string) {
uppedKey := strings.ToUpper(key)
if tracer, ok := tracers[uppedKey]; ok {
tracer.enabled = true
}
}
func getTracer(key string) *tracer {
uppedKey := strings.ToUpper(key)
tracerLock.Lock()
tracer, ok := tracers[uppedKey]
if !ok {
tracer = initializeTracer(uppedKey)
}
tracerLock.Unlock()
return tracer
}
func initializeTracer(key string) *tracer {
if tracer, ok := tracers[key]; ok {
return tracer // Someone else initialized while we were blocked
}
tracer := &tracer{false, false, os.Stderr}
tracers[key] = tracer
perf := os.Getenv(fmt.Sprintf("%s_TRACE_PERFORMANCE", key))
if perf == "1" || strings.ToLower(perf) == "true" {
tracer.performance = true
}
trace := os.Getenv(fmt.Sprintf("%s_TRACE", key))
fd, err := strconv.Atoi(trace)
if err != nil {
// Not a number, it could be a path for a log file
if filepath.IsAbs(trace) {
tracerOut, err := os.OpenFile(trace, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not open '%s' for tracing: %s\nDefaulting to tracing on stderr...\n", trace, err)
tracerOut = os.Stderr
}
tracer.w = tracerOut
tracer.enabled = true
} else if strings.ToLower(trace) == "true" {
tracer.enabled = true
}
} else {
switch fd {
case 0:
case 1, 2:
tracer.enabled = true
default:
tracer.w = os.NewFile(uintptr(fd), "trace")
tracer.enabled = true
}
}
return tracer
}
func init() {
tracers = make(map[string]*tracer, 0)
}
|