Source File
logger.go
Belonging Package
go.uber.org/zap
// Copyright (c) 2016 Uber Technologies, Inc.//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.package zapimport ()// A Logger provides fast, leveled, structured logging. All methods are safe// for concurrent use.//// The Logger is designed for contexts in which every microsecond and every// allocation matters, so its API intentionally favors performance and type// safety over brevity. For most applications, the SugaredLogger strikes a// better balance between performance and ergonomics.type Logger struct {core zapcore.Coredevelopment booladdCaller boolonFatal zapcore.CheckWriteHook // default is WriteThenFatalname stringerrorOutput zapcore.WriteSynceraddStack zapcore.LevelEnablercallerSkip intclock zapcore.Clock}// New constructs a new Logger from the provided zapcore.Core and Options. If// the passed zapcore.Core is nil, it falls back to using a no-op// implementation.//// This is the most flexible way to construct a Logger, but also the most// verbose. For typical use cases, the highly-opinionated presets// (NewProduction, NewDevelopment, and NewExample) or the Config struct are// more convenient.//// For sample code, see the package-level AdvancedConfiguration example.func ( zapcore.Core, ...Option) *Logger {if == nil {return NewNop()}:= &Logger{core: ,errorOutput: zapcore.Lock(os.Stderr),addStack: zapcore.FatalLevel + 1,clock: zapcore.DefaultClock,}return .WithOptions(...)}// NewNop returns a no-op Logger. It never writes out logs or internal errors,// and it never runs user-defined hooks.//// Using WithOptions to replace the Core or error output of a no-op Logger can// re-enable logging.func () *Logger {return &Logger{core: zapcore.NewNopCore(),errorOutput: zapcore.AddSync(io.Discard),addStack: zapcore.FatalLevel + 1,clock: zapcore.DefaultClock,}}// NewProduction builds a sensible production Logger that writes InfoLevel and// above logs to standard error as JSON.//// It's a shortcut for NewProductionConfig().Build(...Option).func ( ...Option) (*Logger, error) {return NewProductionConfig().Build(...)}// NewDevelopment builds a development Logger that writes DebugLevel and above// logs to standard error in a human-friendly format.//// It's a shortcut for NewDevelopmentConfig().Build(...Option).func ( ...Option) (*Logger, error) {return NewDevelopmentConfig().Build(...)}// Must is a helper that wraps a call to a function returning (*Logger, error)// and panics if the error is non-nil. It is intended for use in variable// initialization such as://// var logger = zap.Must(zap.NewProduction())func ( *Logger, error) *Logger {if != nil {panic()}return}// NewExample builds a Logger that's designed for use in zap's testable// examples. It writes DebugLevel and above logs to standard out as JSON, but// omits the timestamp and calling function to keep example output// short and deterministic.func ( ...Option) *Logger {:= zapcore.EncoderConfig{MessageKey: "msg",LevelKey: "level",NameKey: "logger",EncodeLevel: zapcore.LowercaseLevelEncoder,EncodeTime: zapcore.ISO8601TimeEncoder,EncodeDuration: zapcore.StringDurationEncoder,}:= zapcore.NewCore(zapcore.NewJSONEncoder(), os.Stdout, DebugLevel)return New().WithOptions(...)}// Sugar wraps the Logger to provide a more ergonomic, but slightly slower,// API. Sugaring a Logger is quite inexpensive, so it's reasonable for a// single application to use both Loggers and SugaredLoggers, converting// between them on the boundaries of performance-sensitive code.func ( *Logger) () *SugaredLogger {:= .clone().callerSkip += 2return &SugaredLogger{}}// Named adds a new path segment to the logger's name. Segments are joined by// periods. By default, Loggers are unnamed.func ( *Logger) ( string) *Logger {if == "" {return}:= .clone()if .name == "" {.name =} else {.name = strings.Join([]string{.name, }, ".")}return}// WithOptions clones the current Logger, applies the supplied Options, and// returns the resulting Logger. It's safe to use concurrently.func ( *Logger) ( ...Option) *Logger {:= .clone()for , := range {.apply()}return}// With creates a child logger and adds structured context to it. Fields added// to the child don't affect the parent, and vice versa. Any fields that// require evaluation (such as Objects) are evaluated upon invocation of With.func ( *Logger) ( ...Field) *Logger {if len() == 0 {return}:= .clone().core = .core.With()return}// WithLazy creates a child logger and adds structured context to it lazily.//// The fields are evaluated only if the logger is further chained with [With]// or is written to with any of the log level methods.// Until that occurs, the logger may retain references to objects inside the fields,// and logging will reflect the state of an object at the time of logging,// not the time of WithLazy().//// WithLazy provides a worthwhile performance optimization for contextual loggers// when the likelihood of using the child logger is low,// such as error paths and rarely taken branches.//// Similar to [With], fields added to the child don't affect the parent, and vice versa.func ( *Logger) ( ...Field) *Logger {if len() == 0 {return}return .WithOptions(WrapCore(func( zapcore.Core) zapcore.Core {return zapcore.NewLazyWith(, )}))}// Level reports the minimum enabled level for this logger.//// For NopLoggers, this is [zapcore.InvalidLevel].func ( *Logger) () zapcore.Level {return zapcore.LevelOf(.core)}// Check returns a CheckedEntry if logging a message at the specified level// is enabled. It's a completely optional optimization; in high-performance// applications, Check can help avoid allocating a slice to hold fields.func ( *Logger) ( zapcore.Level, string) *zapcore.CheckedEntry {return .check(, )}// Log logs a message at the specified level. The message includes any fields// passed at the log site, as well as any fields accumulated on the logger.// Any Fields that require evaluation (such as Objects) are evaluated upon// invocation of Log.func ( *Logger) ( zapcore.Level, string, ...Field) {if := .check(, ); != nil {.Write(...)}}// Debug logs a message at DebugLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.func ( *Logger) ( string, ...Field) {if := .check(DebugLevel, ); != nil {.Write(...)}}// Info logs a message at InfoLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.func ( *Logger) ( string, ...Field) {if := .check(InfoLevel, ); != nil {.Write(...)}}// Warn logs a message at WarnLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.func ( *Logger) ( string, ...Field) {if := .check(WarnLevel, ); != nil {.Write(...)}}// Error logs a message at ErrorLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.func ( *Logger) ( string, ...Field) {if := .check(ErrorLevel, ); != nil {.Write(...)}}// DPanic logs a message at DPanicLevel. The message includes any fields// passed at the log site, as well as any fields accumulated on the logger.//// If the logger is in development mode, it then panics (DPanic means// "development panic"). This is useful for catching errors that are// recoverable, but shouldn't ever happen.func ( *Logger) ( string, ...Field) {if := .check(DPanicLevel, ); != nil {.Write(...)}}// Panic logs a message at PanicLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.//// The logger then panics, even if logging at PanicLevel is disabled.func ( *Logger) ( string, ...Field) {if := .check(PanicLevel, ); != nil {.Write(...)}}// Fatal logs a message at FatalLevel. The message includes any fields passed// at the log site, as well as any fields accumulated on the logger.//// The logger then calls os.Exit(1), even if logging at FatalLevel is// disabled.func ( *Logger) ( string, ...Field) {if := .check(FatalLevel, ); != nil {.Write(...)}}// Sync calls the underlying Core's Sync method, flushing any buffered log// entries. Applications should take care to call Sync before exiting.func ( *Logger) () error {return .core.Sync()}// Core returns the Logger's underlying zapcore.Core.func ( *Logger) () zapcore.Core {return .core}// Name returns the Logger's underlying name,// or an empty string if the logger is unnamed.func ( *Logger) () string {return .name}func ( *Logger) () *Logger {:= *return &}func ( *Logger) ( zapcore.Level, string) *zapcore.CheckedEntry {// Logger.check must always be called directly by a method in the// Logger interface (e.g., Check, Info, Fatal).// This skips Logger.check and the Info/Fatal/Check/etc. method that// called it.const = 2// Check the level first to reduce the cost of disabled log calls.// Since Panic and higher may exit, we skip the optimization for those levels.if < zapcore.DPanicLevel && !.core.Enabled() {return nil}// Create basic checked entry thru the core; this will be non-nil if the// log message will actually be written somewhere.:= zapcore.Entry{LoggerName: .name,Time: .clock.Now(),Level: ,Message: ,}:= .core.Check(, nil):= != nil// Set up any required terminal behavior.switch .Level {case zapcore.PanicLevel:= .After(, zapcore.WriteThenPanic)case zapcore.FatalLevel::= .onFatal// nil or WriteThenNoop will lead to continued execution after// a Fatal log entry, which is unexpected. For example,//// f, err := os.Open(..)// if err != nil {// log.Fatal("cannot open", zap.Error(err))// }// fmt.Println(f.Name())//// The f.Name() will panic if we continue execution after the// log.Fatal.if == nil || == zapcore.WriteThenNoop {= zapcore.WriteThenFatal}= .After(, )case zapcore.DPanicLevel:if .development {= .After(, zapcore.WriteThenPanic)}}// Only do further annotation if we're going to write this message; checked// entries that exist only for terminal behavior don't benefit from// annotation.if ! {return}// Thread the error output through to the CheckedEntry..ErrorOutput = .errorOutput:= .addStack.Enabled(.Level)if !.addCaller && ! {return}// Adding the caller or stack trace requires capturing the callers of// this function. We'll share information between these two.:= stacktrace.Firstif {= stacktrace.Full}:= stacktrace.Capture(.callerSkip+, )defer .Free()if .Count() == 0 {if .addCaller {fmt.Fprintf(.errorOutput, "%v Logger.check error: failed to get caller\n", .Time.UTC())_ = .errorOutput.Sync()}return}, := .Next()if .addCaller {.Caller = zapcore.EntryCaller{Defined: .PC != 0,PC: .PC,File: .File,Line: .Line,Function: .Function,}}if {:= bufferpool.Get()defer .Free():= stacktrace.NewFormatter()// We've already extracted the first frame, so format that// separately and defer to stackfmt for the rest..FormatFrame()if {.FormatStack()}.Stack = .String()}return}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |