glog.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. // Go support for leveled logs, analogous to https://github.com/google/glog.
  2. //
  3. // Copyright 2023 Google Inc. All Rights Reserved.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. // Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
  17. // It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
  18. // Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
  19. //
  20. // Basic examples:
  21. //
  22. // glog.Info("Prepare to repel boarders")
  23. //
  24. // glog.Fatalf("Initialization failed: %s", err)
  25. //
  26. // See the documentation for the V function for an explanation of these examples:
  27. //
  28. // if glog.V(2) {
  29. // glog.Info("Starting transaction...")
  30. // }
  31. //
  32. // glog.V(2).Infoln("Processed", nItems, "elements")
  33. //
  34. // Log output is buffered and written periodically using Flush. Programs
  35. // should call Flush before exiting to guarantee all log output is written.
  36. //
  37. // By default, all log statements write to files in a temporary directory.
  38. // This package provides several flags that modify this behavior.
  39. // As a result, flag.Parse must be called before any logging is done.
  40. //
  41. // -logtostderr=false
  42. // Logs are written to standard error instead of to files.
  43. // -alsologtostderr=false
  44. // Logs are written to standard error as well as to files.
  45. // -stderrthreshold=ERROR
  46. // Log events at or above this severity are logged to standard
  47. // error as well as to files.
  48. // -log_dir=""
  49. // Log files will be written to this directory instead of the
  50. // default temporary directory.
  51. //
  52. // Other flags provide aids to debugging.
  53. //
  54. // -log_backtrace_at=""
  55. // A comma-separated list of file and line numbers holding a logging
  56. // statement, such as
  57. // -log_backtrace_at=gopherflakes.go:234
  58. // A stack trace will be written to the Info log whenever execution
  59. // hits one of these statements. (Unlike with -vmodule, the ".go"
  60. // must bepresent.)
  61. // -v=0
  62. // Enable V-leveled logging at the specified level.
  63. // -vmodule=""
  64. // The syntax of the argument is a comma-separated list of pattern=N,
  65. // where pattern is a literal file name (minus the ".go" suffix) or
  66. // "glob" pattern and N is a V level. For instance,
  67. // -vmodule=gopher*=3
  68. // sets the V level to 3 in all Go files whose names begin with "gopher",
  69. // and
  70. // -vmodule=/path/to/glog/glog_test=1
  71. // sets the V level to 1 in the Go file /path/to/glog/glog_test.go.
  72. // If a glob pattern contains a slash, it is matched against the full path,
  73. // and the file name. Otherwise, the pattern is
  74. // matched only against the file's basename. When both -vmodule and -v
  75. // are specified, the -vmodule values take precedence for the specified
  76. // modules.
  77. package glog
  78. // This file contains the parts of the log package that are shared among all
  79. // implementations (file, envelope, and appengine).
  80. import (
  81. "bytes"
  82. "errors"
  83. "fmt"
  84. stdLog "log"
  85. "os"
  86. "reflect"
  87. "runtime"
  88. "runtime/pprof"
  89. "strconv"
  90. "sync"
  91. "sync/atomic"
  92. "syscall"
  93. "time"
  94. "github.com/golang/glog/internal/logsink"
  95. "github.com/golang/glog/internal/stackdump"
  96. )
  97. var timeNow = time.Now // Stubbed out for testing.
  98. // MaxSize is the maximum size of a log file in bytes.
  99. var MaxSize uint64 = 1024 * 1024 * 1800
  100. // ErrNoLog is the error we return if no log file has yet been created
  101. // for the specified log type.
  102. var ErrNoLog = errors.New("log file not yet created")
  103. // OutputStats tracks the number of output lines and bytes written.
  104. type OutputStats struct {
  105. lines int64
  106. bytes int64
  107. }
  108. // Lines returns the number of lines written.
  109. func (s *OutputStats) Lines() int64 {
  110. return atomic.LoadInt64(&s.lines)
  111. }
  112. // Bytes returns the number of bytes written.
  113. func (s *OutputStats) Bytes() int64 {
  114. return atomic.LoadInt64(&s.bytes)
  115. }
  116. // Stats tracks the number of lines of output and number of bytes
  117. // per severity level. Values must be read with atomic.LoadInt64.
  118. var Stats struct {
  119. Info, Warning, Error OutputStats
  120. }
  121. var severityStats = [...]*OutputStats{
  122. logsink.Info: &Stats.Info,
  123. logsink.Warning: &Stats.Warning,
  124. logsink.Error: &Stats.Error,
  125. logsink.Fatal: nil,
  126. }
  127. // Level specifies a level of verbosity for V logs. The -v flag is of type
  128. // Level and should be modified only through the flag.Value interface.
  129. type Level int32
  130. var metaPool sync.Pool // Pool of *logsink.Meta.
  131. // metaPoolGet returns a *logsink.Meta from metaPool as both an interface and a
  132. // pointer, allocating a new one if necessary. (Returning the interface value
  133. // directly avoids an allocation if there was an existing pointer in the pool.)
  134. func metaPoolGet() (any, *logsink.Meta) {
  135. if metai := metaPool.Get(); metai != nil {
  136. return metai, metai.(*logsink.Meta)
  137. }
  138. meta := new(logsink.Meta)
  139. return meta, meta
  140. }
  141. type stack bool
  142. const (
  143. noStack = stack(false)
  144. withStack = stack(true)
  145. )
  146. func appendBacktrace(depth int, format string, args []any) (string, []any) {
  147. // Capture a backtrace as a stackdump.Stack (both text and PC slice).
  148. // Structured log sinks can extract the backtrace in whichever format they
  149. // prefer (PCs or text), and Text sinks will include it as just another part
  150. // of the log message.
  151. //
  152. // Use depth instead of depth+1 so that the backtrace always includes the
  153. // log function itself - otherwise the reason for the trace appearing in the
  154. // log may not be obvious to the reader.
  155. dump := stackdump.Caller(depth)
  156. // Add an arg and an entry in the format string for the stack dump.
  157. //
  158. // Copy the "args" slice to avoid a rare but serious aliasing bug
  159. // (corrupting the caller's slice if they passed it to a non-Fatal call
  160. // using "...").
  161. format = format + "\n\n%v\n"
  162. args = append(append([]any(nil), args...), dump)
  163. return format, args
  164. }
  165. // logf writes a log message for a log function call (or log function wrapper)
  166. // at the given depth in the current goroutine's stack.
  167. func logf(depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) {
  168. now := timeNow()
  169. _, file, line, ok := runtime.Caller(depth + 1)
  170. if !ok {
  171. file = "???"
  172. line = 1
  173. }
  174. if stack == withStack || backtraceAt(file, line) {
  175. format, args = appendBacktrace(depth+1, format, args)
  176. }
  177. metai, meta := metaPoolGet()
  178. *meta = logsink.Meta{
  179. Time: now,
  180. File: file,
  181. Line: line,
  182. Depth: depth + 1,
  183. Severity: severity,
  184. Verbose: verbose,
  185. Thread: int64(pid),
  186. }
  187. sinkf(meta, format, args...)
  188. metaPool.Put(metai)
  189. }
  190. func sinkf(meta *logsink.Meta, format string, args ...any) {
  191. meta.Depth++
  192. n, err := logsink.Printf(meta, format, args...)
  193. if stats := severityStats[meta.Severity]; stats != nil {
  194. atomic.AddInt64(&stats.lines, 1)
  195. atomic.AddInt64(&stats.bytes, int64(n))
  196. }
  197. if err != nil {
  198. logsink.Printf(meta, "glog: exiting because of error: %s", err)
  199. sinks.file.Flush()
  200. os.Exit(2)
  201. }
  202. }
  203. // CopyStandardLogTo arranges for messages written to the Go "log" package's
  204. // default logs to also appear in the Google logs for the named and lower
  205. // severities. Subsequent changes to the standard log's default output location
  206. // or format may break this behavior.
  207. //
  208. // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
  209. // recognized, CopyStandardLogTo panics.
  210. func CopyStandardLogTo(name string) {
  211. sev, err := logsink.ParseSeverity(name)
  212. if err != nil {
  213. panic(fmt.Sprintf("log.CopyStandardLogTo(%q): %v", name, err))
  214. }
  215. // Set a log format that captures the user's file and line:
  216. // d.go:23: message
  217. stdLog.SetFlags(stdLog.Lshortfile)
  218. stdLog.SetOutput(logBridge(sev))
  219. }
  220. // NewStandardLogger returns a Logger that writes to the Google logs for the
  221. // named and lower severities.
  222. //
  223. // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
  224. // recognized, NewStandardLogger panics.
  225. func NewStandardLogger(name string) *stdLog.Logger {
  226. sev, err := logsink.ParseSeverity(name)
  227. if err != nil {
  228. panic(fmt.Sprintf("log.NewStandardLogger(%q): %v", name, err))
  229. }
  230. return stdLog.New(logBridge(sev), "", stdLog.Lshortfile)
  231. }
  232. // logBridge provides the Write method that enables CopyStandardLogTo to connect
  233. // Go's standard logs to the logs provided by this package.
  234. type logBridge logsink.Severity
  235. // Write parses the standard logging line and passes its components to the
  236. // logger for severity(lb).
  237. func (lb logBridge) Write(b []byte) (n int, err error) {
  238. var (
  239. file = "???"
  240. line = 1
  241. text string
  242. )
  243. // Split "d.go:23: message" into "d.go", "23", and "message".
  244. if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
  245. text = fmt.Sprintf("bad log format: %s", b)
  246. } else {
  247. file = string(parts[0])
  248. text = string(parts[2][1:]) // skip leading space
  249. line, err = strconv.Atoi(string(parts[1]))
  250. if err != nil {
  251. text = fmt.Sprintf("bad line number: %s", b)
  252. line = 1
  253. }
  254. }
  255. // The depth below hard-codes details of how stdlog gets here. The alternative would be to walk
  256. // up the stack looking for src/log/log.go but that seems like it would be
  257. // unfortunately slow.
  258. const stdLogDepth = 4
  259. metai, meta := metaPoolGet()
  260. *meta = logsink.Meta{
  261. Time: timeNow(),
  262. File: file,
  263. Line: line,
  264. Depth: stdLogDepth,
  265. Severity: logsink.Severity(lb),
  266. Thread: int64(pid),
  267. }
  268. format := "%s"
  269. args := []any{text}
  270. if backtraceAt(file, line) {
  271. format, args = appendBacktrace(meta.Depth, format, args)
  272. }
  273. sinkf(meta, format, args...)
  274. metaPool.Put(metai)
  275. return len(b), nil
  276. }
  277. // defaultFormat returns a fmt.Printf format specifier that formats its
  278. // arguments as if they were passed to fmt.Print.
  279. func defaultFormat(args []any) string {
  280. n := len(args)
  281. switch n {
  282. case 0:
  283. return ""
  284. case 1:
  285. return "%v"
  286. }
  287. b := make([]byte, 0, n*3-1)
  288. wasString := true // Suppress leading space.
  289. for _, arg := range args {
  290. isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
  291. if wasString || isString {
  292. b = append(b, "%v"...)
  293. } else {
  294. b = append(b, " %v"...)
  295. }
  296. wasString = isString
  297. }
  298. return string(b)
  299. }
  300. // lnFormat returns a fmt.Printf format specifier that formats its arguments
  301. // as if they were passed to fmt.Println.
  302. func lnFormat(args []any) string {
  303. if len(args) == 0 {
  304. return "\n"
  305. }
  306. b := make([]byte, 0, len(args)*3)
  307. for range args {
  308. b = append(b, "%v "...)
  309. }
  310. b[len(b)-1] = '\n' // Replace the last space with a newline.
  311. return string(b)
  312. }
  313. // Verbose is a boolean type that implements Infof (like Printf) etc.
  314. // See the documentation of V for more information.
  315. type Verbose bool
  316. // V reports whether verbosity at the call site is at least the requested level.
  317. // The returned value is a boolean of type Verbose, which implements Info, Infoln
  318. // and Infof. These methods will write to the Info log if called.
  319. // Thus, one may write either
  320. //
  321. // if glog.V(2) { glog.Info("log this") }
  322. //
  323. // or
  324. //
  325. // glog.V(2).Info("log this")
  326. //
  327. // The second form is shorter but the first is cheaper if logging is off because it does
  328. // not evaluate its arguments.
  329. //
  330. // Whether an individual call to V generates a log record depends on the setting of
  331. // the -v and --vmodule flags; both are off by default. If the level in the call to
  332. // V is at most the value of -v, or of -vmodule for the source file containing the
  333. // call, the V call will log.
  334. func V(level Level) Verbose {
  335. return VDepth(1, level)
  336. }
  337. // VDepth acts as V but uses depth to determine which call frame to check vmodule for.
  338. // VDepth(0, level) is the same as V(level).
  339. func VDepth(depth int, level Level) Verbose {
  340. return Verbose(verboseEnabled(depth+1, level))
  341. }
  342. // Info is equivalent to the global Info function, guarded by the value of v.
  343. // See the documentation of V for usage.
  344. func (v Verbose) Info(args ...any) {
  345. v.InfoDepth(1, args...)
  346. }
  347. // InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v.
  348. // See the documentation of V for usage.
  349. func (v Verbose) InfoDepth(depth int, args ...any) {
  350. if v {
  351. logf(depth+1, logsink.Info, true, noStack, defaultFormat(args), args...)
  352. }
  353. }
  354. // InfoDepthf is equivalent to the global InfoDepthf function, guarded by the value of v.
  355. // See the documentation of V for usage.
  356. func (v Verbose) InfoDepthf(depth int, format string, args ...any) {
  357. if v {
  358. logf(depth+1, logsink.Info, true, noStack, format, args...)
  359. }
  360. }
  361. // Infoln is equivalent to the global Infoln function, guarded by the value of v.
  362. // See the documentation of V for usage.
  363. func (v Verbose) Infoln(args ...any) {
  364. if v {
  365. logf(1, logsink.Info, true, noStack, lnFormat(args), args...)
  366. }
  367. }
  368. // Infof is equivalent to the global Infof function, guarded by the value of v.
  369. // See the documentation of V for usage.
  370. func (v Verbose) Infof(format string, args ...any) {
  371. if v {
  372. logf(1, logsink.Info, true, noStack, format, args...)
  373. }
  374. }
  375. // Info logs to the INFO log.
  376. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
  377. func Info(args ...any) {
  378. InfoDepth(1, args...)
  379. }
  380. // InfoDepth calls Info from a different depth in the call stack.
  381. // This enables a callee to emit logs that use the callsite information of its caller
  382. // or any other callers in the stack. When depth == 0, the original callee's line
  383. // information is emitted. When depth > 0, depth frames are skipped in the call stack
  384. // and the final frame is treated like the original callee to Info.
  385. func InfoDepth(depth int, args ...any) {
  386. logf(depth+1, logsink.Info, false, noStack, defaultFormat(args), args...)
  387. }
  388. // InfoDepthf acts as InfoDepth but with format string.
  389. func InfoDepthf(depth int, format string, args ...any) {
  390. logf(depth+1, logsink.Info, false, noStack, format, args...)
  391. }
  392. // Infoln logs to the INFO log.
  393. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
  394. func Infoln(args ...any) {
  395. logf(1, logsink.Info, false, noStack, lnFormat(args), args...)
  396. }
  397. // Infof logs to the INFO log.
  398. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
  399. func Infof(format string, args ...any) {
  400. logf(1, logsink.Info, false, noStack, format, args...)
  401. }
  402. // Warning logs to the WARNING and INFO logs.
  403. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
  404. func Warning(args ...any) {
  405. WarningDepth(1, args...)
  406. }
  407. // WarningDepth acts as Warning but uses depth to determine which call frame to log.
  408. // WarningDepth(0, "msg") is the same as Warning("msg").
  409. func WarningDepth(depth int, args ...any) {
  410. logf(depth+1, logsink.Warning, false, noStack, defaultFormat(args), args...)
  411. }
  412. // WarningDepthf acts as Warningf but uses depth to determine which call frame to log.
  413. // WarningDepthf(0, "msg") is the same as Warningf("msg").
  414. func WarningDepthf(depth int, format string, args ...any) {
  415. logf(depth+1, logsink.Warning, false, noStack, format, args...)
  416. }
  417. // Warningln logs to the WARNING and INFO logs.
  418. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
  419. func Warningln(args ...any) {
  420. logf(1, logsink.Warning, false, noStack, lnFormat(args), args...)
  421. }
  422. // Warningf logs to the WARNING and INFO logs.
  423. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
  424. func Warningf(format string, args ...any) {
  425. logf(1, logsink.Warning, false, noStack, format, args...)
  426. }
  427. // Error logs to the ERROR, WARNING, and INFO logs.
  428. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
  429. func Error(args ...any) {
  430. ErrorDepth(1, args...)
  431. }
  432. // ErrorDepth acts as Error but uses depth to determine which call frame to log.
  433. // ErrorDepth(0, "msg") is the same as Error("msg").
  434. func ErrorDepth(depth int, args ...any) {
  435. logf(depth+1, logsink.Error, false, noStack, defaultFormat(args), args...)
  436. }
  437. // ErrorDepthf acts as Errorf but uses depth to determine which call frame to log.
  438. // ErrorDepthf(0, "msg") is the same as Errorf("msg").
  439. func ErrorDepthf(depth int, format string, args ...any) {
  440. logf(depth+1, logsink.Error, false, noStack, format, args...)
  441. }
  442. // Errorln logs to the ERROR, WARNING, and INFO logs.
  443. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
  444. func Errorln(args ...any) {
  445. logf(1, logsink.Error, false, noStack, lnFormat(args), args...)
  446. }
  447. // Errorf logs to the ERROR, WARNING, and INFO logs.
  448. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
  449. func Errorf(format string, args ...any) {
  450. logf(1, logsink.Error, false, noStack, format, args...)
  451. }
  452. func fatalf(depth int, format string, args ...any) {
  453. logf(depth+1, logsink.Fatal, false, withStack, format, args...)
  454. sinks.file.Flush()
  455. err := abortProcess() // Should not return.
  456. // Failed to abort the process using signals. Dump a stack trace and exit.
  457. Errorf("abortProcess returned unexpectedly: %v", err)
  458. sinks.file.Flush()
  459. pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
  460. os.Exit(2) // Exit with the same code as the default SIGABRT handler.
  461. }
  462. // abortProcess attempts to kill the current process in a way that will dump the
  463. // currently-running goroutines someplace useful (Coroner or stderr).
  464. //
  465. // It does this by sending SIGABRT to the current process. Unfortunately, the
  466. // signal may or may not be delivered to the current thread; in order to do that
  467. // portably, we would need to add a cgo dependency and call pthread_kill.
  468. //
  469. // If successful, abortProcess does not return.
  470. func abortProcess() error {
  471. p, err := os.FindProcess(os.Getpid())
  472. if err != nil {
  473. return err
  474. }
  475. if err := p.Signal(syscall.SIGABRT); err != nil {
  476. return err
  477. }
  478. // Sent the signal. Now we wait for it to arrive and any SIGABRT handlers to
  479. // run (and eventually terminate the process themselves).
  480. //
  481. // We could just "select{}" here, but there's an outside chance that would
  482. // trigger the runtime's deadlock detector if there happen not to be any
  483. // background goroutines running. So we'll sleep a while first to give
  484. // the signal some time.
  485. time.Sleep(10 * time.Second)
  486. select {}
  487. }
  488. // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
  489. // including a stack trace of all running goroutines, then calls os.Exit(2).
  490. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
  491. func Fatal(args ...any) {
  492. FatalDepth(1, args...)
  493. }
  494. // FatalDepth acts as Fatal but uses depth to determine which call frame to log.
  495. // FatalDepth(0, "msg") is the same as Fatal("msg").
  496. func FatalDepth(depth int, args ...any) {
  497. fatalf(depth+1, defaultFormat(args), args...)
  498. }
  499. // FatalDepthf acts as Fatalf but uses depth to determine which call frame to log.
  500. // FatalDepthf(0, "msg") is the same as Fatalf("msg").
  501. func FatalDepthf(depth int, format string, args ...any) {
  502. fatalf(depth+1, format, args...)
  503. }
  504. // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
  505. // including a stack trace of all running goroutines, then calls os.Exit(2).
  506. // Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
  507. func Fatalln(args ...any) {
  508. fatalf(1, lnFormat(args), args...)
  509. }
  510. // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
  511. // including a stack trace of all running goroutines, then calls os.Exit(2).
  512. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
  513. func Fatalf(format string, args ...any) {
  514. fatalf(1, format, args...)
  515. }
  516. func exitf(depth int, format string, args ...any) {
  517. logf(depth+1, logsink.Fatal, false, noStack, format, args...)
  518. sinks.file.Flush()
  519. os.Exit(1)
  520. }
  521. // Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
  522. // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
  523. func Exit(args ...any) {
  524. ExitDepth(1, args...)
  525. }
  526. // ExitDepth acts as Exit but uses depth to determine which call frame to log.
  527. // ExitDepth(0, "msg") is the same as Exit("msg").
  528. func ExitDepth(depth int, args ...any) {
  529. exitf(depth+1, defaultFormat(args), args...)
  530. }
  531. // ExitDepthf acts as Exitf but uses depth to determine which call frame to log.
  532. // ExitDepthf(0, "msg") is the same as Exitf("msg").
  533. func ExitDepthf(depth int, format string, args ...any) {
  534. exitf(depth+1, format, args...)
  535. }
  536. // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
  537. func Exitln(args ...any) {
  538. exitf(1, lnFormat(args), args...)
  539. }
  540. // Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
  541. // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
  542. func Exitf(format string, args ...any) {
  543. exitf(1, format, args...)
  544. }