// 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) }