From 8466bdebfcc9cd1585a84cee492cc2cd004c3e8e Mon Sep 17 00:00:00 2001 From: Joe Turk Date: Wed, 8 Jan 2025 21:26:03 -0600 Subject: [PATCH] Upgrade golangci, Enable more linters Upgrade `golangci` version, Introduce more linters. Relates to pion/.goassets#220 --- .golangci.yml | 49 +++++++++++++++++++++++++--------------- logger.go | 60 ++++++++++++++++++++++++++++--------------------- logging_test.go | 14 +++++++++--- scoped.go | 22 +++++++++--------- 4 files changed, 88 insertions(+), 57 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a3235be..50211be 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,17 +25,32 @@ linters-settings: - ^os.Exit$ - ^panic$ - ^print(ln)?$ + varnamelen: + max-distance: 12 + min-name-length: 2 + ignore-type-assert-ok: true + ignore-map-index-ok: true + ignore-chan-recv-ok: true + ignore-decls: + - i int + - n int + - w io.Writer + - r io.Reader + - b []byte linters: enable: - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers - bidichk # Checks for dangerous unicode character sequences - bodyclose # checks whether HTTP response body is closed successfully + - containedctx # containedctx is a linter that detects struct contained context.Context field - contextcheck # check the function whether use a non-inherited context + - cyclop # checks function and package cyclomatic complexity - decorder # check declaration order and count of types, constants, variables and functions - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - dupl # Tool for code clone detection - durationcheck # check for two durations multiplied together + - err113 # Golang linter to check the errors handling expressions - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. @@ -44,20 +59,20 @@ linters: - exportloopref # checks for pointers to enclosing loop variables - forbidigo # Forbids identifiers - forcetypeassert # finds forced type assertions + - funlen # Tool for detection of long functions - gci # Gci control golang package import order and make it always deterministic. - gochecknoglobals # Checks that no globals are present in Go code - - gochecknoinits # Checks that no init functions are present in Go code - gocognit # Computes and checks the cognitive complexity of functions - goconst # Finds repeated strings that could be replaced by a constant - gocritic # The most opinionated Go source code linter + - gocyclo # Computes and checks the cyclomatic complexity of functions + - godot # Check if comments end in a period - godox # Tool for detection of FIXME, TODO and other comment keywords - - err113 # Golang linter to check the errors handling expressions - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems - gosimple # Linter for Go source code that specializes in simplifying a code @@ -65,9 +80,15 @@ linters: - grouper # An analyzer to analyze expression groups. - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used + - lll # Reports long lines + - maintidx # maintidx measures the maintainability index of each function. + - makezero # Finds slice declarations with non-zero initial length - misspell # Finds commonly misspelled English words in comments + - nakedret # Finds naked returns in functions greater than a specified function length + - nestif # Reports deeply nested if statements - nilerr # Finds the code that returns nil even if it checks that the error is not nil. - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. + - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - noctx # noctx finds sending http request without context.Context - predeclared # find code that shadows one of Go's predeclared identifiers - revive # golint replacement, finds style mistakes @@ -75,28 +96,21 @@ linters: - stylecheck # Stylecheck is a replacement for golint - tagliatelle # Checks the struct tags. - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types + - varnamelen # checks that the length of a variable's name matches its scope - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: - depguard # Go linter that checks if package imports are in a list of acceptable packages - - containedctx # containedctx is a linter that detects struct contained context.Context field - - cyclop # checks function and package cyclomatic complexity - - funlen # Tool for detection of long functions - - gocyclo # Computes and checks the cyclomatic complexity of functions - - godot # Check if comments end in a period - - gomnd # An analyzer to detect magic numbers. + - gochecknoinits # Checks that no init functions are present in Go code + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. + - interfacebloat # A linter that checks length of interface. - ireturn # Accept Interfaces, Return Concrete Types - - lll # Reports long lines - - maintidx # maintidx measures the maintainability index of each function. - - makezero # Finds slice declarations with non-zero initial length - - nakedret # Finds naked returns in functions greater than a specified function length - - nestif # Reports deeply nested if statements - - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity + - mnd # An analyzer to detect magic numbers - nolintlint # Reports ill-formed or insufficient nolint directives - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - prealloc # Finds slice declarations that could potentially be preallocated @@ -104,8 +118,7 @@ linters: - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. - testpackage # linter that makes you use a separate _test package - - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - - varnamelen # checks that the length of a variable's name matches its scope + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - wrapcheck # Checks that errors returned from external packages are wrapped - wsl # Whitespace Linter - Forces you to use empty lines! diff --git a/logger.go b/logger.go index 29fcc99..6055c8d 100644 --- a/logger.go +++ b/logger.go @@ -13,8 +13,8 @@ import ( "sync" ) -// Use this abstraction to ensure thread-safe access to the logger's io.Writer -// (which could change at runtime) +// Use this abstraction to ensure thread-safe access to the logger's io.Writer. +// (which could change at runtime). type loggerWriter struct { sync.RWMutex output io.Writer @@ -29,11 +29,12 @@ func (lw *loggerWriter) SetOutput(output io.Writer) { func (lw *loggerWriter) Write(data []byte) (int, error) { lw.RLock() defer lw.RUnlock() + return lw.output.Write(data) } -// DefaultLeveledLogger encapsulates functionality for providing logging at -// user-defined levels +// DefaultLeveledLogger encapsulates functionality for providing logging at. +// user-defined levels. type DefaultLeveledLogger struct { level LogLevel writer *loggerWriter @@ -45,44 +46,50 @@ type DefaultLeveledLogger struct { } // WithTraceLogger is a chainable configuration function which sets the -// Trace-level logger +// Trace-level logger. func (ll *DefaultLeveledLogger) WithTraceLogger(log *log.Logger) *DefaultLeveledLogger { ll.trace = log + return ll } // WithDebugLogger is a chainable configuration function which sets the -// Debug-level logger +// Debug-level logger. func (ll *DefaultLeveledLogger) WithDebugLogger(log *log.Logger) *DefaultLeveledLogger { ll.debug = log + return ll } // WithInfoLogger is a chainable configuration function which sets the -// Info-level logger +// Info-level logger. func (ll *DefaultLeveledLogger) WithInfoLogger(log *log.Logger) *DefaultLeveledLogger { ll.info = log + return ll } // WithWarnLogger is a chainable configuration function which sets the -// Warn-level logger +// Warn-level logger. func (ll *DefaultLeveledLogger) WithWarnLogger(log *log.Logger) *DefaultLeveledLogger { ll.warn = log + return ll } // WithErrorLogger is a chainable configuration function which sets the -// Error-level logger +// Error-level logger. func (ll *DefaultLeveledLogger) WithErrorLogger(log *log.Logger) *DefaultLeveledLogger { ll.err = log + return ll } // WithOutput is a chainable configuration function which sets the logger's -// logging output to the supplied io.Writer +// logging output to the supplied io.Writer. func (ll *DefaultLeveledLogger) WithOutput(output io.Writer) *DefaultLeveledLogger { ll.writer.SetOutput(output) + return ll } @@ -98,62 +105,62 @@ func (ll *DefaultLeveledLogger) logf(logger *log.Logger, level LogLevel, format } } -// SetLevel sets the logger's logging level +// SetLevel sets the logger's logging level. func (ll *DefaultLeveledLogger) SetLevel(newLevel LogLevel) { ll.level.Set(newLevel) } -// Trace emits the preformatted message if the logger is at or below LogLevelTrace +// Trace emits the preformatted message if the logger is at or below LogLevelTrace. func (ll *DefaultLeveledLogger) Trace(msg string) { ll.logf(ll.trace, LogLevelTrace, msg) // nolint: govet } -// Tracef formats and emits a message if the logger is at or below LogLevelTrace +// Tracef formats and emits a message if the logger is at or below LogLevelTrace. func (ll *DefaultLeveledLogger) Tracef(format string, args ...interface{}) { ll.logf(ll.trace, LogLevelTrace, format, args...) } -// Debug emits the preformatted message if the logger is at or below LogLevelDebug +// Debug emits the preformatted message if the logger is at or below LogLevelDebug. func (ll *DefaultLeveledLogger) Debug(msg string) { ll.logf(ll.debug, LogLevelDebug, msg) // nolint: govet } -// Debugf formats and emits a message if the logger is at or below LogLevelDebug +// Debugf formats and emits a message if the logger is at or below LogLevelDebug. func (ll *DefaultLeveledLogger) Debugf(format string, args ...interface{}) { ll.logf(ll.debug, LogLevelDebug, format, args...) } -// Info emits the preformatted message if the logger is at or below LogLevelInfo +// Info emits the preformatted message if the logger is at or below LogLevelInfo. func (ll *DefaultLeveledLogger) Info(msg string) { ll.logf(ll.info, LogLevelInfo, msg) // nolint: govet } -// Infof formats and emits a message if the logger is at or below LogLevelInfo +// Infof formats and emits a message if the logger is at or below LogLevelInfo. func (ll *DefaultLeveledLogger) Infof(format string, args ...interface{}) { ll.logf(ll.info, LogLevelInfo, format, args...) } -// Warn emits the preformatted message if the logger is at or below LogLevelWarn +// Warn emits the preformatted message if the logger is at or below LogLevelWarn. func (ll *DefaultLeveledLogger) Warn(msg string) { ll.logf(ll.warn, LogLevelWarn, msg) // nolint: govet } -// Warnf formats and emits a message if the logger is at or below LogLevelWarn +// Warnf formats and emits a message if the logger is at or below LogLevelWarn. func (ll *DefaultLeveledLogger) Warnf(format string, args ...interface{}) { ll.logf(ll.warn, LogLevelWarn, format, args...) } -// Error emits the preformatted message if the logger is at or below LogLevelError +// Error emits the preformatted message if the logger is at or below LogLevelError. func (ll *DefaultLeveledLogger) Error(msg string) { ll.logf(ll.err, LogLevelError, msg) // nolint: govet } -// Errorf formats and emits a message if the logger is at or below LogLevelError +// Errorf formats and emits a message if the logger is at or below LogLevelError. func (ll *DefaultLeveledLogger) Errorf(format string, args ...interface{}) { ll.logf(ll.err, LogLevelError, format, args...) } -// NewDefaultLeveledLoggerForScope returns a configured LeveledLogger +// NewDefaultLeveledLoggerForScope returns a configured LeveledLogger. func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Writer) *DefaultLeveledLogger { if writer == nil { writer = os.Stderr @@ -162,6 +169,7 @@ func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Wri writer: &loggerWriter{output: writer}, level: level, } + return logger. WithTraceLogger(log.New(logger.writer, fmt.Sprintf("%s TRACE: ", scope), log.Lmicroseconds|log.Lshortfile)). WithDebugLogger(log.New(logger.writer, fmt.Sprintf("%s DEBUG: ", scope), log.Lmicroseconds|log.Lshortfile)). @@ -170,14 +178,14 @@ func NewDefaultLeveledLoggerForScope(scope string, level LogLevel, writer io.Wri WithErrorLogger(log.New(logger.writer, fmt.Sprintf("%s ERROR: ", scope), log.LstdFlags)) } -// DefaultLoggerFactory define levels by scopes and creates new DefaultLeveledLogger +// DefaultLoggerFactory define levels by scopes and creates new DefaultLeveledLogger. type DefaultLoggerFactory struct { Writer io.Writer DefaultLogLevel LogLevel ScopeLevels map[string]LogLevel } -// NewDefaultLoggerFactory creates a new DefaultLoggerFactory +// NewDefaultLoggerFactory creates a new DefaultLoggerFactory. func NewDefaultLoggerFactory() *DefaultLoggerFactory { factory := DefaultLoggerFactory{} factory.DefaultLogLevel = LogLevelError @@ -206,6 +214,7 @@ func NewDefaultLoggerFactory() *DefaultLoggerFactory { if strings.ToLower(env) == "all" { factory.DefaultLogLevel = level + continue } @@ -218,7 +227,7 @@ func NewDefaultLoggerFactory() *DefaultLoggerFactory { return &factory } -// NewLogger returns a configured LeveledLogger for the given , argsscope +// NewLogger returns a configured LeveledLogger for the given, argsscope. func (f *DefaultLoggerFactory) NewLogger(scope string) LeveledLogger { logLevel := f.DefaultLogLevel if f.ScopeLevels != nil { @@ -228,5 +237,6 @@ func (f *DefaultLoggerFactory) NewLogger(scope string) LeveledLogger { logLevel = scopeLevel } } + return NewDefaultLeveledLoggerForScope(scope, logLevel, f.Writer) } diff --git a/logging_test.go b/logging_test.go index 7f7865e..6bac671 100644 --- a/logging_test.go +++ b/logging_test.go @@ -13,6 +13,8 @@ import ( ) func testNoDebugLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { + t.Helper() + var outBuf bytes.Buffer logger.WithOutput(&outBuf) @@ -27,6 +29,8 @@ func testNoDebugLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { } func testDebugLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { + t.Helper() + var outBuf bytes.Buffer logger.WithOutput(&outBuf) @@ -42,6 +46,8 @@ func testDebugLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { } func testWarnLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { + t.Helper() + var outBuf bytes.Buffer logger.WithOutput(&outBuf) @@ -57,6 +63,8 @@ func testWarnLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { } func testErrorLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { + t.Helper() + var outBuf bytes.Buffer logger.WithOutput(&outBuf) @@ -72,7 +80,7 @@ func testErrorLevel(t *testing.T, logger *logging.DefaultLeveledLogger) { } func TestDefaultLoggerFactory(t *testing.T) { - f := logging.DefaultLoggerFactory{ + factory := logging.DefaultLoggerFactory{ Writer: os.Stderr, DefaultLogLevel: logging.LogLevelWarn, ScopeLevels: map[string]logging.LogLevel{ @@ -80,7 +88,7 @@ func TestDefaultLoggerFactory(t *testing.T) { }, } - logger := f.NewLogger("baz") + logger := factory.NewLogger("baz") bazLogger, ok := logger.(*logging.DefaultLeveledLogger) if !ok { t.Error("Invalid logger type") @@ -89,7 +97,7 @@ func TestDefaultLoggerFactory(t *testing.T) { testNoDebugLevel(t, bazLogger) testWarnLevel(t, bazLogger) - logger = f.NewLogger("foo") + logger = factory.NewLogger("foo") fooLogger, ok := logger.(*logging.DefaultLeveledLogger) if !ok { t.Error("Invalid logger type") diff --git a/scoped.go b/scoped.go index 24cf676..7b3a550 100644 --- a/scoped.go +++ b/scoped.go @@ -7,15 +7,15 @@ import ( "sync/atomic" ) -// LogLevel represents the level at which the logger will emit log messages +// LogLevel represents the level at which the logger will emit log messages. type LogLevel int32 -// Set updates the LogLevel to the supplied value +// Set updates the LogLevel to the supplied value. func (ll *LogLevel) Set(newLevel LogLevel) { atomic.StoreInt32((*int32)(ll), int32(newLevel)) } -// Get retrieves the current LogLevel value +// Get retrieves the current LogLevel value. func (ll *LogLevel) Get() LogLevel { return LogLevel(atomic.LoadInt32((*int32)(ll))) } @@ -40,22 +40,22 @@ func (ll LogLevel) String() string { } const ( - // LogLevelDisabled completely disables logging of any events + // LogLevelDisabled completely disables logging of any events. LogLevelDisabled LogLevel = iota // LogLevelError is for fatal errors which should be handled by user code, - // but are logged to ensure that they are seen + // but are logged to ensure that they are seen. LogLevelError - // LogLevelWarn is for logging abnormal, but non-fatal library operation + // LogLevelWarn is for logging abnormal, but non-fatal library operation. LogLevelWarn - // LogLevelInfo is for logging normal library operation (e.g. state transitions, etc.) + // LogLevelInfo is for logging normal library operation (e.g. state transitions, etc.). LogLevelInfo - // LogLevelDebug is for logging low-level library information (e.g. internal operations) + // LogLevelDebug is for logging low-level library information (e.g. internal operations). LogLevelDebug - // LogLevelTrace is for logging very low-level library information (e.g. network traces) + // LogLevelTrace is for logging very low-level library information (e.g. network traces). LogLevelTrace ) -// LeveledLogger is the basic pion Logger interface +// LeveledLogger is the basic pion Logger interface. type LeveledLogger interface { Trace(msg string) Tracef(format string, args ...interface{}) @@ -69,7 +69,7 @@ type LeveledLogger interface { Errorf(format string, args ...interface{}) } -// LoggerFactory is the basic pion LoggerFactory interface +// LoggerFactory is the basic pion LoggerFactory interface. type LoggerFactory interface { NewLogger(scope string) LeveledLogger }