| // Copyright 2023 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package counter |
| |
| // The implementation of this package and tests are located in |
| // internal/counter, which can be shared with the upload package. |
| // TODO(hyangah): use of type aliases prevents nice documentation |
| // rendering in go doc or pkgsite. Fix this either by avoiding |
| // type aliasing or restructuring the internal/counter package. |
| import ( |
| "flag" |
| "path" |
| "runtime/debug" |
| |
| "golang.org/x/telemetry/internal/counter" |
| "golang.org/x/telemetry/internal/telemetry" |
| ) |
| |
| // Inc increments the counter with the given name. |
| func Inc(name string) { |
| New(name).Inc() |
| } |
| |
| // Add adds n to the counter with the given name. |
| func Add(name string, n int64) { |
| New(name).Add(n) |
| } |
| |
| // New returns a counter with the given name. |
| // New can be called in global initializers and will be compiled down to |
| // linker-initialized data. That is, calling New to initialize a global |
| // has no cost at program startup. |
| // |
| // See "Counter Naming" in the package doc for a description of counter naming |
| // conventions. |
| func New(name string) *Counter { |
| // Note: not calling DefaultFile.New in order to keep this |
| // function something the compiler can inline and convert |
| // into static data initializations, with no init-time footprint. |
| // TODO(hyangah): is it trivial enough for the compiler to inline? |
| return counter.New(name) |
| } |
| |
| // A Counter is a single named event counter. |
| // A Counter is safe for use by multiple goroutines simultaneously. |
| // |
| // Counters should typically be created using New |
| // and stored as global variables, like: |
| // |
| // package mypackage |
| // var errorCount = counter.New("mypackage/errors") |
| // |
| // (The initialization of errorCount in this example is handled |
| // entirely by the compiler and linker; this line executes no code |
| // at program startup.) |
| // |
| // Then code can call Add to increment the counter |
| // each time the corresponding event is observed. |
| // |
| // Although it is possible to use New to create |
| // a Counter each time a particular event needs to be recorded, |
| // that usage fails to amortize the construction cost over |
| // multiple calls to Add, so it is more expensive and not recommended. |
| type Counter = counter.Counter |
| |
| // A StackCounter is the in-memory knowledge about a stack counter. |
| // StackCounters are more expensive to use than regular Counters, |
| // requiring, at a minimum, a call to runtime.Callers. |
| type StackCounter = counter.StackCounter |
| |
| // NewStack returns a new stack counter with the given name and depth. |
| // |
| // See "Counter Naming" in the package doc for a description of counter naming |
| // conventions. |
| func NewStack(name string, depth int) *StackCounter { |
| return counter.NewStack(name, depth) |
| } |
| |
| // Open prepares telemetry counters for recording to the file system. |
| // |
| // If the telemetry mode is "off", Open is a no-op. Otherwise, it opens the |
| // counter file on disk and starts to mmap telemetry counters to the file. |
| // Open also persists any counters already created in the current process. |
| // |
| // Open should only be called from short-lived processes such as command line |
| // tools. If your process is long-running, use [OpenAndRotate]. |
| func Open() { |
| counter.Open(false) |
| } |
| |
| // OpenAndRotate is like [Open], but also schedules a rotation of the counter |
| // file when it expires. |
| // |
| // See golang/go#68497 for background on why [OpenAndRotate] is a separate API. |
| // |
| // TODO(rfindley): refactor Open and OpenAndRotate for Go 1.24. |
| func OpenAndRotate() { |
| counter.Open(true) |
| } |
| |
| // OpenDir prepares telemetry counters for recording to the file system, using |
| // the specified telemetry directory, if it is not the empty string. |
| // |
| // If the telemetry mode is "off", Open is a no-op. Otherwise, it opens the |
| // counter file on disk and starts to mmap telemetry counters to the file. |
| // Open also persists any counters already created in the current process. |
| func OpenDir(telemetryDir string) { |
| if telemetryDir != "" { |
| telemetry.Default = telemetry.NewDir(telemetryDir) |
| } |
| counter.Open(false) |
| } |
| |
| // CountFlags creates a counter for every flag that is set |
| // and increments the counter. The name of the counter is |
| // the concatenation of prefix and the flag name. |
| // |
| // For instance, CountFlags("gopls/flag:", *flag.CommandLine) |
| func CountFlags(prefix string, fs flag.FlagSet) { |
| fs.Visit(func(f *flag.Flag) { |
| New(prefix + f.Name).Inc() |
| }) |
| } |
| |
| // CountCommandLineFlags creates a counter for every flag |
| // that is set in the default flag.CommandLine FlagSet using |
| // the counter name binaryName+"/flag:"+flagName where |
| // binaryName is the base name of the Path embedded in the |
| // binary's build info. If the binary does not have embedded build |
| // info, the "flag:"+flagName counter will be incremented. |
| // |
| // CountCommandLineFlags must be called after flags are parsed |
| // with flag.Parse. |
| // |
| // For instance, if the -S flag is passed to cmd/compile and |
| // CountCommandLineFlags is called after flags are parsed, |
| // the "compile/flag:S" counter will be incremented. |
| func CountCommandLineFlags() { |
| prefix := "flag:" |
| if buildInfo, ok := debug.ReadBuildInfo(); ok && buildInfo.Path != "" { |
| prefix = path.Base(buildInfo.Path) + "/" + prefix |
| } |
| CountFlags(prefix, *flag.CommandLine) |
| } |