Error Handling and Logging
Motivation
This project aims to ensure robust and informative error handling and logging practices. The goals are:
- Clear and Consistent Error Reporting: Provide developers with comprehensive error information to facilitate debugging and issue resolution.
- Structured Logging: Log events in a structured and machine-readable format for easier analysis and aggregation.
- Appropriate Error Handling: Implement sensible error handling mechanisms to avoid program crashes and maintain application stability.
Error Handling
Error Handling Best Practices
- Explicit Error Handling: Always handle errors explicitly using
if err != nil
checks. - Detailed Error Information: Provide context and details about the error within error messages.
- Return Errors Up the Call Stack: Propagate errors upward until they can be handled appropriately.
- Use the
errors
Package: Leverage theerrors
package for creating and wrapping errors to maintain context and information.
package main
import (
"errors"
"fmt"
)
func doSomething() error {
// Simulate an error
if err := someOperation(); err != nil {
return fmt.Errorf("failed to perform operation: %w", err) // Wrap the error with more context
}
return nil
}
func main() {
if err := doSomething(); err != nil {
fmt.Println("Error:", err) // Output the detailed error
}
}
Error Types
- Standard Errors: Utilize built-in Go error types (e.g.,
io.EOF
,os.PathError
). - Custom Error Types: Define custom error types for specific scenarios and provide context-specific information.
package main
import (
"errors"
"fmt"
)
// Define a custom error type
type InvalidInputError struct {
Value string
}
func (e *InvalidInputError) Error() string {
return fmt.Sprintf("invalid input: %s", e.Value)
}
func validateInput(input string) error {
if input == "" {
return &InvalidInputError{Value: input}
}
return nil
}
func main() {
if err := validateInput(""); err != nil {
fmt.Println("Error:", err) // Output the custom error
}
}
Logging
Logging Levels
- Debug: Detailed information for development and debugging.
- Info: Informational messages about application events.
- Warning: Potential issues or non-critical errors.
- Error: Critical errors that impact application functionality.
- Fatal: Severe errors that cause application termination.
Logging Best Practices
- Structured Logging: Log events in a structured format for easier analysis and aggregation. Use libraries like
logrus
,zap
, orzerolog
. - Contextual Information: Include relevant context within log messages (e.g., timestamps, user IDs, request IDs).
- Log Rotation: Implement log rotation to prevent log files from growing indefinitely.
package main
import (
"fmt"
"github.com/sirupsen/logrus"
)
func main() {
// Configure the logger
logger := logrus.New()
logger.Formatter = &logrus.JSONFormatter{}
// Log messages at different levels
logger.Debug("Debug message")
logger.Info("Info message")
logger.Warn("Warning message")
logger.Error("Error message")
logger.Fatal("Fatal message")
}