Files
opentf/cmd/tofu/version.go
Martin Atkins f21bd00a1b main: Log about reliance on GODEBUG settings
The Go runtime provides a number of configuration knobs that subtly change
its behavior, and we cannot control which of these is available over time
as we change which version of Go we're building with.

It's therefore possible that an OpenTofu user might be intentionally or
unintentionally relying on one of these settings for OpenTofu to work on
their system, in which case they would be broken if they upgraded to a
newer version of OpenTofu which uses a different Go version that no longer
supports that setting.

These log lines are intended to help us more quickly notice that
possibility if someone opens a bug report describing an unexpected behavior
change after upgrading to a new OpenTofu minor release series. We can ask
the reporter to share "TF_LOG=debug" output from both the previous and new
releases to compare, and then these log lines should appear in the older
version's output so we can search the Go codebase and issue tracker for
each of the mentioned names to learn if the handling of that setting has
changed between Go versions.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-12-18 12:33:55 -08:00

68 lines
2.3 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package main
import (
"log"
"runtime/metrics"
"strings"
"github.com/opentofu/opentofu/internal/logging"
"github.com/opentofu/opentofu/version"
)
var Version = version.Version
var VersionPrerelease = version.Prerelease
// logGodebugUsage produces extra DEBUG log lines if the Go runtime's metrics
// suggest that code that was run so far relied on any non-default "GODEBUG"
// settings, which could be helpful in reproducing a bug report if the
// behavior differs based on such a setting.
//
// For this to be useful it must be run just before we're about to exit, after
// we've already performed all of the requested work.
func logGodebugUsage() {
// These constants reflect the documented conventions from the
// runtime/metrics package, so we can filter for only the metrics that
// are relevant to this function.
const godebugMetricPrefix = "/godebug/non-default-behavior/"
const godebugMetricSuffix = ":events"
if !logging.IsDebugOrHigher() {
// No point in doing any of this work if the log lines are going to
// be filtered out anyway.
return
}
metricDescs := metrics.All()
for _, metric := range metricDescs {
if !metric.Cumulative || metric.Kind != metrics.KindUint64 {
// godebug counters are always cumulative uint64, so this quickly
// filters some irrelevant metrics before we do any string
// comparisons.
continue
}
if !strings.HasPrefix(metric.Name, godebugMetricPrefix) {
continue // irrelevant metric
}
if !strings.HasSuffix(metric.Name, godebugMetricSuffix) {
continue // irrelevant metric
}
// The metrics API is designed for applications that want to periodically
// re-extract the same set of metrics in a long-running application by
// reusing a preallocated buffer, but we don't have that need here and so
// we'll just read one metric at a time into a single-element array.
var samples [1]metrics.Sample
samples[0].Name = metric.Name
metrics.Read(samples[:])
if count := samples[0].Value.Uint64(); count != 0 {
name := metric.Name[len(godebugMetricPrefix) : len(metric.Name)-len(godebugMetricSuffix)]
log.Printf("[DEBUG] Relied on GODEBUG %q %d times during this execution; behavior might change in future OpenTofu versions", name, count)
}
}
}