File: tracerx.go

package info (click to toggle)
golang-github-rubyist-tracerx 0.0~git20170927.0.7879593-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, bullseye-backports, buster, buster-backports
  • size: 56 kB
  • ctags: 15
  • sloc: makefile: 2
file content (151 lines) | stat: -rw-r--r-- 4,065 bytes parent folder | download | duplicates (2)
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)
}