github.com/m3db/m3@v1.5.0/src/x/instrument/build.go (about) 1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package instrument 22 23 import ( 24 "errors" 25 "log" 26 "os" 27 "runtime" 28 "strconv" 29 "sync" 30 "time" 31 ) 32 33 var ( 34 // Revision is the VCS revision associated with this build. Overridden using ldflags 35 // at compile time. Example: 36 // $ go build -ldflags "-X github.com/m3db/m3/src/x/instrument.Revision=abcdef" ... 37 // Adapted from: https://www.atatus.com/blog/golang-auto-build-versioning/ 38 Revision = "unknown" 39 40 // Branch is the VCS branch associated with this build. 41 Branch = "unknown" 42 43 // Version is the version associated with this build. 44 Version = "unknown" 45 46 // BuildDate is the date this build was created. 47 BuildDate = "unknown" 48 49 // BuildTimeUnix is the seconds since epoch representing the date this build was created. 50 BuildTimeUnix = "0" 51 52 // LogBuildInfoAtStartup controls whether we log build information at startup. If its 53 // set to a non-empty string, we log the build information at process startup. 54 LogBuildInfoAtStartup string 55 56 // LogBuildInfoToStdout controls whether we log build information to stdout or stderr. 57 // If it is set to a non-empty string then the build info will be logged to stdout, 58 // otherwise it will be logged to stderr (assuming LogBuildInfoAtStartup is also 59 // non-empty). 60 LogBuildInfoToStdout string 61 62 // goVersion is the current runtime version. 63 goVersion = runtime.Version() 64 65 // buildInfoMetricName is the emitted build information metric's name. 66 buildInfoMetricName = "build-information" 67 68 // buildAgeMetricName is the emitted build age metric's name. 69 buildAgeMetricName = "build-age" 70 ) 71 72 var ( 73 errAlreadyStarted = errors.New("reporter already started") 74 errNotStarted = errors.New("reporter not started") 75 errBuildTimeNegative = errors.New("reporter build time must be non-negative") 76 ) 77 78 // LogBuildInfo logs the build information using the default logger. 79 func LogBuildInfo() { 80 LogBuildInfoWithLogger(log.Default()) 81 } 82 83 // LogBuildInfoWithLogger logs the build information using the provided logger. 84 func LogBuildInfoWithLogger(logger *log.Logger) { 85 logger.Printf("Go Runtime version: %s\n", goVersion) 86 logger.Printf("Build Version: %s\n", Version) 87 logger.Printf("Build Revision: %s\n", Revision) 88 logger.Printf("Build Branch: %s\n", Branch) 89 logger.Printf("Build Date: %s\n", BuildDate) 90 logger.Printf("Build TimeUnix: %s\n", BuildTimeUnix) 91 } 92 93 func init() { 94 if LogBuildInfoAtStartup != "" { 95 logger := log.Default() 96 if LogBuildInfoToStdout != "" { 97 logger = log.New(os.Stdout, "", log.LstdFlags) 98 } 99 LogBuildInfoWithLogger(logger) 100 } 101 } 102 103 type buildReporter struct { 104 sync.Mutex 105 106 opts Options 107 buildTime time.Time 108 active bool 109 closeCh chan struct{} 110 doneCh chan struct{} 111 } 112 113 // NewBuildReporter returns a new build version reporter. 114 func NewBuildReporter( 115 opts Options, 116 ) Reporter { 117 return &buildReporter{ 118 opts: opts, 119 } 120 } 121 122 func (b *buildReporter) Start() error { 123 const ( 124 base = 10 125 bitSize = 64 126 ) 127 sec, err := strconv.ParseInt(BuildTimeUnix, base, bitSize) 128 if err != nil { 129 return err 130 } 131 if sec < 0 { 132 return errBuildTimeNegative 133 } 134 buildTime := time.Unix(sec, 0) 135 136 b.Lock() 137 defer b.Unlock() 138 if b.active { 139 return errAlreadyStarted 140 } 141 b.buildTime = buildTime 142 b.active = true 143 b.closeCh = make(chan struct{}) 144 b.doneCh = make(chan struct{}) 145 go b.report() 146 return nil 147 } 148 149 func (b *buildReporter) report() { 150 tags := map[string]string{ 151 "revision": Revision, 152 "branch": Branch, 153 "build-date": BuildDate, 154 "build-version": Version, 155 "go-version": goVersion, 156 } 157 158 for k, v := range b.opts.CustomBuildTags() { 159 tags[k] = v 160 } 161 162 scope := b.opts.MetricsScope().Tagged(tags) 163 buildInfoGauge := scope.Gauge(buildInfoMetricName) 164 buildAgeGauge := scope.Gauge(buildAgeMetricName) 165 buildInfoGauge.Update(1.0) 166 buildAgeGauge.Update(float64(time.Since(b.buildTime))) 167 168 ticker := time.NewTicker(b.opts.ReportInterval()) 169 defer func() { 170 close(b.doneCh) 171 ticker.Stop() 172 }() 173 174 for { 175 select { 176 case <-ticker.C: 177 buildInfoGauge.Update(1.0) 178 buildAgeGauge.Update(float64(time.Since(b.buildTime))) 179 180 case <-b.closeCh: 181 return 182 } 183 } 184 } 185 186 func (b *buildReporter) Stop() error { 187 b.Lock() 188 defer b.Unlock() 189 if !b.active { 190 return errNotStarted 191 } 192 close(b.closeCh) 193 <-b.doneCh 194 b.active = false 195 return nil 196 }