Files
gibidify/shared/logger.go
Ismo Vuorinen 95b7ef6dd3 chore: modernize workflows, security scanning, and linting configuration (#50)
* build: update Go 1.25, CI workflows, and build tooling

- Upgrade to Go 1.25
- Add benchmark targets to Makefile
- Implement parallel gosec execution
- Lock tool versions for reproducibility
- Add shellcheck directives to scripts
- Update CI workflows with improved caching

* refactor: migrate from golangci-lint to revive

- Replace golangci-lint with revive for linting
- Configure comprehensive revive rules
- Fix all EditorConfig violations
- Add yamllint and yamlfmt support
- Remove deprecated .golangci.yml

* refactor: rename utils to shared and deduplicate code

- Rename utils package to shared
- Add shared constants package
- Deduplicate constants across packages
- Address CodeRabbit review feedback

* fix: resolve SonarQube issues and add safety guards

- Fix all 73 SonarQube OPEN issues
- Add nil guards for resourceMonitor, backpressure, metricsCollector
- Implement io.Closer for headerFileReader
- Propagate errors from processing helpers
- Add metrics and templates packages
- Improve error handling across codebase

* test: improve test infrastructure and coverage

- Add benchmarks for cli, fileproc, metrics
- Improve test coverage for cli, fileproc, config
- Refactor tests with helper functions
- Add shared test constants
- Fix test function naming conventions
- Reduce cognitive complexity in benchmark tests

* docs: update documentation and configuration examples

- Update CLAUDE.md with current project state
- Refresh README with new features
- Add usage and configuration examples
- Add SonarQube project configuration
- Consolidate config.example.yaml

* fix: resolve shellcheck warnings in scripts

- Use ./*.go instead of *.go to prevent dash-prefixed filenames
  from being interpreted as options (SC2035)
- Remove unreachable return statement after exit (SC2317)
- Remove obsolete gibidiutils/ directory reference

* chore(deps): upgrade go dependencies

* chore(lint): megalinter fixes

* fix: improve test coverage and fix file descriptor leaks

- Add defer r.Close() to fix pipe file descriptor leaks in benchmark tests
- Refactor TestProcessorConfigureFileTypes with helper functions and assertions
- Refactor TestProcessorLogFinalStats with output capture and keyword verification
- Use shared constants instead of literal strings (TestFilePNG, FormatMarkdown, etc.)
- Reduce cognitive complexity by extracting helper functions

* fix: align test comments with function names

Remove underscores from test comments to match actual function names:
- benchmark/benchmark_test.go (2 fixes)
- fileproc/filetypes_config_test.go (4 fixes)
- fileproc/filetypes_registry_test.go (6 fixes)
- fileproc/processor_test.go (6 fixes)
- fileproc/resource_monitor_types_test.go (4 fixes)
- fileproc/writer_test.go (3 fixes)

* fix: various test improvements and bug fixes

- Remove duplicate maxCacheSize check in filetypes_registry_test.go
- Shorten long comment in processor_test.go to stay under 120 chars
- Remove flaky time.Sleep in collector_test.go, use >= 0 assertion
- Close pipe reader in benchmark_test.go to fix file descriptor leak
- Use ContinueOnError in flags_test.go to match ResetFlags behavior
- Add nil check for p.ui in processor_workers.go before UpdateProgress
- Fix resource_monitor_validation_test.go by setting hardMemoryLimitBytes directly

* chore(yaml): add missing document start markers

Add --- document start to YAML files to satisfy yamllint:
- .github/workflows/codeql.yml
- .github/workflows/build-test-publish.yml
- .github/workflows/security.yml
- .github/actions/setup/action.yml

* fix: guard nil resourceMonitor and fix test deadlock

- Guard resourceMonitor before CreateFileProcessingContext call
- Add ui.UpdateProgress on emergency stop and path error returns
- Fix potential deadlock in TestProcessFile using wg.Go with defer close
2025-12-10 19:07:11 +02:00

165 lines
3.7 KiB
Go

// Package shared provides logging utilities for gibidify.
package shared
import (
"io"
"os"
"sync"
"github.com/sirupsen/logrus"
)
// Logger interface defines the logging contract for gibidify.
type Logger interface {
Debug(args ...any)
Debugf(format string, args ...any)
Info(args ...any)
Infof(format string, args ...any)
Warn(args ...any)
Warnf(format string, args ...any)
Error(args ...any)
Errorf(format string, args ...any)
WithFields(fields map[string]any) Logger
SetLevel(level LogLevel)
SetOutput(output io.Writer)
}
// LogLevel represents available log levels.
type LogLevel string
// logService implements the Logger interface using logrus.
type logService struct {
logger *logrus.Logger
entry *logrus.Entry
}
var (
instance Logger
once sync.Once
)
// GetLogger returns the singleton logger instance.
// Default level is WARNING to reduce noise in CLI output.
func GetLogger() Logger {
once.Do(
func() {
logger := logrus.New()
logger.SetLevel(logrus.WarnLevel) // Default to WARNING level
logger.SetOutput(os.Stderr)
logger.SetFormatter(
&logrus.TextFormatter{
DisableColors: false,
FullTimestamp: false,
},
)
instance = &logService{
logger: logger,
entry: logger.WithFields(logrus.Fields{}),
}
},
)
return instance
}
// Debug logs a debug message.
func (l *logService) Debug(args ...any) {
l.entry.Debug(args...)
}
// Debugf logs a formatted debug message.
func (l *logService) Debugf(format string, args ...any) {
l.entry.Debugf(format, args...)
}
// Info logs an info message.
func (l *logService) Info(args ...any) {
l.entry.Info(args...)
}
// Infof logs a formatted info message.
func (l *logService) Infof(format string, args ...any) {
l.entry.Infof(format, args...)
}
// Warn logs a warning message.
func (l *logService) Warn(args ...any) {
l.entry.Warn(args...)
}
// Warnf logs a formatted warning message.
func (l *logService) Warnf(format string, args ...any) {
l.entry.Warnf(format, args...)
}
// Error logs an error message.
func (l *logService) Error(args ...any) {
l.entry.Error(args...)
}
// Errorf logs a formatted error message.
func (l *logService) Errorf(format string, args ...any) {
l.entry.Errorf(format, args...)
}
// WithFields adds structured fields to log entries.
func (l *logService) WithFields(fields map[string]any) Logger {
logrusFields := make(logrus.Fields)
for k, v := range fields {
logrusFields[k] = v
}
return &logService{
logger: l.logger,
entry: l.entry.WithFields(logrusFields),
}
}
// SetLevel sets the logging level.
func (l *logService) SetLevel(level LogLevel) {
var logrusLevel logrus.Level
switch level {
case LogLevelDebug:
logrusLevel = logrus.DebugLevel
case LogLevelInfo:
logrusLevel = logrus.InfoLevel
case LogLevelError:
logrusLevel = logrus.ErrorLevel
default:
// LogLevelWarn and unknown levels default to warn
logrusLevel = logrus.WarnLevel
}
l.logger.SetLevel(logrusLevel)
}
// SetOutput sets the output destination for logs.
func (l *logService) SetOutput(output io.Writer) {
l.logger.SetOutput(output)
}
// ParseLogLevel parses string log level to LogLevel.
func ParseLogLevel(level string) LogLevel {
switch level {
case string(LogLevelDebug):
return LogLevelDebug
case string(LogLevelInfo):
return LogLevelInfo
case string(LogLevelError):
return LogLevelError
default:
// "warn", "warning", and unknown levels default to warn
return LogLevelWarn
}
}
// ValidateLogLevel validates if the provided log level is valid.
func ValidateLogLevel(level string) bool {
switch level {
case string(LogLevelDebug), string(LogLevelInfo), string(LogLevelWarn), LogLevelWarningAlias, string(LogLevelError):
return true
default:
return false
}
}