aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/golang/glog/glog.go
blob: dd0ed101563a2e780c64944e739d21d6d032f047 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
// Go support for leveled logs, analogous to https://github.com/google/glog.
//
// Copyright 2023 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
//
// Basic examples:
//
//	glog.Info("Prepare to repel boarders")
//
//	glog.Fatalf("Initialization failed: %s", err)
//
// See the documentation for the V function for an explanation of these examples:
//
//	if glog.V(2) {
//		glog.Info("Starting transaction...")
//	}
//
//	glog.V(2).Infoln("Processed", nItems, "elements")
//
// Log output is buffered and written periodically using Flush. Programs
// should call Flush before exiting to guarantee all log output is written.
//
// By default, all log statements write to files in a temporary directory.
// This package provides several flags that modify this behavior.
// As a result, flag.Parse must be called before any logging is done.
//
//	-logtostderr=false
//		Logs are written to standard error instead of to files.
//	-alsologtostderr=false
//		Logs are written to standard error as well as to files.
//	-stderrthreshold=ERROR
//		Log events at or above this severity are logged to standard
//		error as well as to files.
//	-log_dir=""
//		Log files will be written to this directory instead of the
//		default temporary directory.
//
// Other flags provide aids to debugging.
//
//	-log_backtrace_at=""
//		A comma-separated list of file and line numbers holding a logging
//		statement, such as
//			-log_backtrace_at=gopherflakes.go:234
//		A stack trace will be written to the Info log whenever execution
//		hits one of these statements. (Unlike with -vmodule, the ".go"
//		must bepresent.)
//	-v=0
//		Enable V-leveled logging at the specified level.
//	-vmodule=""
//		The syntax of the argument is a comma-separated list of pattern=N,
//		where pattern is a literal file name (minus the ".go" suffix) or
//		"glob" pattern and N is a V level. For instance,
//			-vmodule=gopher*=3
//		sets the V level to 3 in all Go files whose names begin with "gopher",
//		and
//			-vmodule=/path/to/glog/glog_test=1
//		sets the V level to 1 in the Go file /path/to/glog/glog_test.go.
//		If a glob pattern contains a slash, it is matched against the full path,
//		and the file name. Otherwise, the pattern is
//		matched only against the file's basename.  When both -vmodule and -v
//		are specified, the -vmodule values take precedence for the specified
//		modules.
package glog

// This file contains the parts of the log package that are shared among all
// implementations (file, envelope, and appengine).

import (
	"bytes"
	"errors"
	"fmt"
	stdLog "log"
	"os"
	"reflect"
	"runtime"
	"runtime/pprof"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/golang/glog/internal/logsink"
	"github.com/golang/glog/internal/stackdump"
)

var timeNow = time.Now // Stubbed out for testing.

// MaxSize is the maximum size of a log file in bytes.
var MaxSize uint64 = 1024 * 1024 * 1800

// ErrNoLog is the error we return if no log file has yet been created
// for the specified log type.
var ErrNoLog = errors.New("log file not yet created")

// OutputStats tracks the number of output lines and bytes written.
type OutputStats struct {
	lines int64
	bytes int64
}

// Lines returns the number of lines written.
func (s *OutputStats) Lines() int64 {
	return atomic.LoadInt64(&s.lines)
}

// Bytes returns the number of bytes written.
func (s *OutputStats) Bytes() int64 {
	return atomic.LoadInt64(&s.bytes)
}

// Stats tracks the number of lines of output and number of bytes
// per severity level. Values must be read with atomic.LoadInt64.
var Stats struct {
	Info, Warning, Error OutputStats
}

var severityStats = [...]*OutputStats{
	logsink.Info:    &Stats.Info,
	logsink.Warning: &Stats.Warning,
	logsink.Error:   &Stats.Error,
	logsink.Fatal:   nil,
}

// Level specifies a level of verbosity for V logs.  The -v flag is of type
// Level and should be modified only through the flag.Value interface.
type Level int32

var metaPool sync.Pool // Pool of *logsink.Meta.

// metaPoolGet returns a *logsink.Meta from metaPool as both an interface and a
// pointer, allocating a new one if necessary.  (Returning the interface value
// directly avoids an allocation if there was an existing pointer in the pool.)
func metaPoolGet() (any, *logsink.Meta) {
	if metai := metaPool.Get(); metai != nil {
		return metai, metai.(*logsink.Meta)
	}
	meta := new(logsink.Meta)
	return meta, meta
}

type stack bool

const (
	noStack   = stack(false)
	withStack = stack(true)
)

func appendBacktrace(depth int, format string, args []any) (string, []any) {
	// Capture a backtrace as a stackdump.Stack (both text and PC slice).
	// Structured log sinks can extract the backtrace in whichever format they
	// prefer (PCs or text), and Text sinks will include it as just another part
	// of the log message.
	//
	// Use depth instead of depth+1 so that the backtrace always includes the
	// log function itself - otherwise the reason for the trace appearing in the
	// log may not be obvious to the reader.
	dump := stackdump.Caller(depth)

	// Add an arg and an entry in the format string for the stack dump.
	//
	// Copy the "args" slice to avoid a rare but serious aliasing bug
	// (corrupting the caller's slice if they passed it to a non-Fatal call
	// using "...").
	format = format + "\n\n%v\n"
	args = append(append([]any(nil), args...), dump)

	return format, args
}

// logf writes a log message for a log function call (or log function wrapper)
// at the given depth in the current goroutine's stack.
func logf(depth int, severity logsink.Severity, verbose bool, stack stack, format string, args ...any) {
	now := timeNow()
	_, file, line, ok := runtime.Caller(depth + 1)
	if !ok {
		file = "???"
		line = 1
	}

	if stack == withStack || backtraceAt(file, line) {
		format, args = appendBacktrace(depth+1, format, args)
	}

	metai, meta := metaPoolGet()
	*meta = logsink.Meta{
		Time:     now,
		File:     file,
		Line:     line,
		Depth:    depth + 1,
		Severity: severity,
		Verbose:  verbose,
		Thread:   int64(pid),
	}
	sinkf(meta, format, args...)
	metaPool.Put(metai)
}

func sinkf(meta *logsink.Meta, format string, args ...any) {
	meta.Depth++
	n, err := logsink.Printf(meta, format, args...)
	if stats := severityStats[meta.Severity]; stats != nil {
		atomic.AddInt64(&stats.lines, 1)
		atomic.AddInt64(&stats.bytes, int64(n))
	}

	if err != nil {
		logsink.Printf(meta, "glog: exiting because of error: %s", err)
		sinks.file.Flush()
		os.Exit(2)
	}
}

// CopyStandardLogTo arranges for messages written to the Go "log" package's
// default logs to also appear in the Google logs for the named and lower
// severities.  Subsequent changes to the standard log's default output location
// or format may break this behavior.
//
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL".  If the name is not
// recognized, CopyStandardLogTo panics.
func CopyStandardLogTo(name string) {
	sev, err := logsink.ParseSeverity(name)
	if err != nil {
		panic(fmt.Sprintf("log.CopyStandardLogTo(%q): %v", name, err))
	}
	// Set a log format that captures the user's file and line:
	//   d.go:23: message
	stdLog.SetFlags(stdLog.Lshortfile)
	stdLog.SetOutput(logBridge(sev))
}

// NewStandardLogger returns a Logger that writes to the Google logs for the
// named and lower severities.
//
// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
// recognized, NewStandardLogger panics.
func NewStandardLogger(name string) *stdLog.Logger {
	sev, err := logsink.ParseSeverity(name)
	if err != nil {
		panic(fmt.Sprintf("log.NewStandardLogger(%q): %v", name, err))
	}
	return stdLog.New(logBridge(sev), "", stdLog.Lshortfile)
}

// logBridge provides the Write method that enables CopyStandardLogTo to connect
// Go's standard logs to the logs provided by this package.
type logBridge logsink.Severity

// Write parses the standard logging line and passes its components to the
// logger for severity(lb).
func (lb logBridge) Write(b []byte) (n int, err error) {
	var (
		file = "???"
		line = 1
		text string
	)
	// Split "d.go:23: message" into "d.go", "23", and "message".
	if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
		text = fmt.Sprintf("bad log format: %s", b)
	} else {
		file = string(parts[0])
		text = string(parts[2][1:]) // skip leading space
		line, err = strconv.Atoi(string(parts[1]))
		if err != nil {
			text = fmt.Sprintf("bad line number: %s", b)
			line = 1
		}
	}

	// The depth below hard-codes details of how stdlog gets here.  The alternative would be to walk
	// up the stack looking for src/log/log.go but that seems like it would be
	// unfortunately slow.
	const stdLogDepth = 4

	metai, meta := metaPoolGet()
	*meta = logsink.Meta{
		Time:     timeNow(),
		File:     file,
		Line:     line,
		Depth:    stdLogDepth,
		Severity: logsink.Severity(lb),
		Thread:   int64(pid),
	}

	format := "%s"
	args := []any{text}
	if backtraceAt(file, line) {
		format, args = appendBacktrace(meta.Depth, format, args)
	}

	sinkf(meta, format, args...)
	metaPool.Put(metai)

	return len(b), nil
}

// defaultFormat returns a fmt.Printf format specifier that formats its
// arguments as if they were passed to fmt.Print.
func defaultFormat(args []any) string {
	n := len(args)
	switch n {
	case 0:
		return ""
	case 1:
		return "%v"
	}

	b := make([]byte, 0, n*3-1)
	wasString := true // Suppress leading space.
	for _, arg := range args {
		isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String
		if wasString || isString {
			b = append(b, "%v"...)
		} else {
			b = append(b, " %v"...)
		}
		wasString = isString
	}
	return string(b)
}

// lnFormat returns a fmt.Printf format specifier that formats its arguments
// as if they were passed to fmt.Println.
func lnFormat(args []any) string {
	if len(args) == 0 {
		return "\n"
	}

	b := make([]byte, 0, len(args)*3)
	for range args {
		b = append(b, "%v "...)
	}
	b[len(b)-1] = '\n' // Replace the last space with a newline.
	return string(b)
}

// Verbose is a boolean type that implements Infof (like Printf) etc.
// See the documentation of V for more information.
type Verbose bool

// V reports whether verbosity at the call site is at least the requested level.
// The returned value is a boolean of type Verbose, which implements Info, Infoln
// and Infof. These methods will write to the Info log if called.
// Thus, one may write either
//
//	if glog.V(2) { glog.Info("log this") }
//
// or
//
//	glog.V(2).Info("log this")
//
// The second form is shorter but the first is cheaper if logging is off because it does
// not evaluate its arguments.
//
// Whether an individual call to V generates a log record depends on the setting of
// the -v and --vmodule flags; both are off by default. If the level in the call to
// V is at most the value of -v, or of -vmodule for the source file containing the
// call, the V call will log.
func V(level Level) Verbose {
	return VDepth(1, level)
}

// VDepth acts as V but uses depth to determine which call frame to check vmodule for.
// VDepth(0, level) is the same as V(level).
func VDepth(depth int, level Level) Verbose {
	return Verbose(verboseEnabled(depth+1, level))
}

// Info is equivalent to the global Info function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Info(args ...any) {
	v.InfoDepth(1, args...)
}

// InfoDepth is equivalent to the global InfoDepth function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) InfoDepth(depth int, args ...any) {
	if v {
		logf(depth+1, logsink.Info, true, noStack, defaultFormat(args), args...)
	}
}

// InfoDepthf is equivalent to the global InfoDepthf function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) InfoDepthf(depth int, format string, args ...any) {
	if v {
		logf(depth+1, logsink.Info, true, noStack, format, args...)
	}
}

// Infoln is equivalent to the global Infoln function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...any) {
	if v {
		logf(1, logsink.Info, true, noStack, lnFormat(args), args...)
	}
}

// Infof is equivalent to the global Infof function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...any) {
	if v {
		logf(1, logsink.Info, true, noStack, format, args...)
	}
}

// Info logs to the INFO log.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Info(args ...any) {
	InfoDepth(1, args...)
}

// InfoDepth calls Info from a different depth in the call stack.
// This enables a callee to emit logs that use the callsite information of its caller
// or any other callers in the stack. When depth == 0, the original callee's line
// information is emitted. When depth > 0, depth frames are skipped in the call stack
// and the final frame is treated like the original callee to Info.
func InfoDepth(depth int, args ...any) {
	logf(depth+1, logsink.Info, false, noStack, defaultFormat(args), args...)
}

// InfoDepthf acts as InfoDepth but with format string.
func InfoDepthf(depth int, format string, args ...any) {
	logf(depth+1, logsink.Info, false, noStack, format, args...)
}

// Infoln logs to the INFO log.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Infoln(args ...any) {
	logf(1, logsink.Info, false, noStack, lnFormat(args), args...)
}

// Infof logs to the INFO log.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Infof(format string, args ...any) {
	logf(1, logsink.Info, false, noStack, format, args...)
}

// Warning logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Warning(args ...any) {
	WarningDepth(1, args...)
}

// WarningDepth acts as Warning but uses depth to determine which call frame to log.
// WarningDepth(0, "msg") is the same as Warning("msg").
func WarningDepth(depth int, args ...any) {
	logf(depth+1, logsink.Warning, false, noStack, defaultFormat(args), args...)
}

// WarningDepthf acts as Warningf but uses depth to determine which call frame to log.
// WarningDepthf(0, "msg") is the same as Warningf("msg").
func WarningDepthf(depth int, format string, args ...any) {
	logf(depth+1, logsink.Warning, false, noStack, format, args...)
}

// Warningln logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Warningln(args ...any) {
	logf(1, logsink.Warning, false, noStack, lnFormat(args), args...)
}

// Warningf logs to the WARNING and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Warningf(format string, args ...any) {
	logf(1, logsink.Warning, false, noStack, format, args...)
}

// Error logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Error(args ...any) {
	ErrorDepth(1, args...)
}

// ErrorDepth acts as Error but uses depth to determine which call frame to log.
// ErrorDepth(0, "msg") is the same as Error("msg").
func ErrorDepth(depth int, args ...any) {
	logf(depth+1, logsink.Error, false, noStack, defaultFormat(args), args...)
}

// ErrorDepthf acts as Errorf but uses depth to determine which call frame to log.
// ErrorDepthf(0, "msg") is the same as Errorf("msg").
func ErrorDepthf(depth int, format string, args ...any) {
	logf(depth+1, logsink.Error, false, noStack, format, args...)
}

// Errorln logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Errorln(args ...any) {
	logf(1, logsink.Error, false, noStack, lnFormat(args), args...)
}

// Errorf logs to the ERROR, WARNING, and INFO logs.
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Errorf(format string, args ...any) {
	logf(1, logsink.Error, false, noStack, format, args...)
}

func fatalf(depth int, format string, args ...any) {
	logf(depth+1, logsink.Fatal, false, withStack, format, args...)
	sinks.file.Flush()

	err := abortProcess() // Should not return.

	// Failed to abort the process using signals.  Dump a stack trace and exit.
	Errorf("abortProcess returned unexpectedly: %v", err)
	sinks.file.Flush()
	pprof.Lookup("goroutine").WriteTo(os.Stderr, 1)
	os.Exit(2) // Exit with the same code as the default SIGABRT handler.
}

// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(2).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Fatal(args ...any) {
	FatalDepth(1, args...)
}

// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
// FatalDepth(0, "msg") is the same as Fatal("msg").
func FatalDepth(depth int, args ...any) {
	fatalf(depth+1, defaultFormat(args), args...)
}

// FatalDepthf acts as Fatalf but uses depth to determine which call frame to log.
// FatalDepthf(0, "msg") is the same as Fatalf("msg").
func FatalDepthf(depth int, format string, args ...any) {
	fatalf(depth+1, format, args...)
}

// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(2).
// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
func Fatalln(args ...any) {
	fatalf(1, lnFormat(args), args...)
}

// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
// including a stack trace of all running goroutines, then calls os.Exit(2).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Fatalf(format string, args ...any) {
	fatalf(1, format, args...)
}

func exitf(depth int, format string, args ...any) {
	logf(depth+1, logsink.Fatal, false, noStack, format, args...)
	sinks.file.Flush()
	os.Exit(1)
}

// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
func Exit(args ...any) {
	ExitDepth(1, args...)
}

// ExitDepth acts as Exit but uses depth to determine which call frame to log.
// ExitDepth(0, "msg") is the same as Exit("msg").
func ExitDepth(depth int, args ...any) {
	exitf(depth+1, defaultFormat(args), args...)
}

// ExitDepthf acts as Exitf but uses depth to determine which call frame to log.
// ExitDepthf(0, "msg") is the same as Exitf("msg").
func ExitDepthf(depth int, format string, args ...any) {
	exitf(depth+1, format, args...)
}

// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
func Exitln(args ...any) {
	exitf(1, lnFormat(args), args...)
}

// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
func Exitf(format string, args ...any) {
	exitf(1, format, args...)
}