Skip to content

Commit

Permalink
Update logger to facilitate the use of global logger (#150)
Browse files Browse the repository at this point in the history
* Update logger to facilitate the use of global logger

* Fix demo-cli typo

* Fix linter errors

* Using global log at pkg metrics and runner

* Update CI for deprectaed steps

* Removing already fixed fixme comment

* Updated logger README.md using ia

* Fix logger README.md
  • Loading branch information
rawmind0 authored Oct 31, 2024
1 parent 2956bbb commit f526f8c
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/checks.golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-go@v3
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ check-modtidy:
go mod tidy
git diff --exit-code -- go.mod go.sum

fmt:
gofmt -l -s -w .

vet:
go vet ./...

lint:
golangci-lint --version
golangci-lint run
Expand Down
1 change: 1 addition & 0 deletions cmd/demo-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func main() {
EnvPrefix: "golem",
GitVersion: version.GitVersion,
GitRevision: version.GitRevision,
LogLevel: "debug",
}

// Define application level features
Expand Down
7 changes: 3 additions & 4 deletions pkg/cli/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cli

import (
"context"
"fmt"
"github.com/zondax/golem/pkg/logger"
"strings"
Expand All @@ -15,7 +14,7 @@ func SetupConfiguration(c *cobra.Command) {
c.PersistentFlags().StringVarP(&configFileFlag, "config", "c", "", "The path to the config file to use.")
err := viper.BindPFlag("config", c.PersistentFlags().Lookup("config"))
if err != nil {
logger.GetLoggerFromContext(context.Background()).Fatalf("unable to bind config flag: %+v", err)
logger.Fatalf("unable to bind config flag: %+v", err)
}

viper.SetConfigName("config") // config file name without extension
Expand Down Expand Up @@ -50,12 +49,12 @@ func LoadConfig[T Config]() (*T, error) {
configFileOverride := viper.GetString("config")
if configFileOverride != "" {
viper.SetConfigFile(configFileOverride)
logger.GetLoggerFromContext(context.Background()).Infof("Using config file: %s", viper.ConfigFileUsed())
logger.Infof("Using config file: %s", viper.ConfigFileUsed())
}

err = viper.ReadInConfig()
if err != nil {
logger.GetLoggerFromContext(context.Background()).Fatalf("%+v", err)
logger.Fatalf("%+v", err)
}

// adds all default+configFile values in viper to struct
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/handlers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package cli

import (
"context"
"github.com/zondax/golem/pkg/logger"
"os"
"os/signal"
"syscall"

"github.com/zondax/golem/pkg/logger"
)

var defaultConfigHandler DefaultConfigHandler
Expand All @@ -15,7 +15,7 @@ func setupCloseHandler(handler CleanUpHandler) {
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
logger.GetLoggerFromContext(context.Background()).Warn("\r- Ctrl+C pressed in Terminal")
logger.Warn("\r- Ctrl+C pressed in Terminal")

if handler != nil {
handler()
Expand Down
23 changes: 12 additions & 11 deletions pkg/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package cli

import (
"fmt"
"os"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zondax/golem/pkg/constants"
"github.com/zondax/golem/pkg/logger"
"os"
)

type AppSettings struct {
Expand All @@ -16,6 +16,7 @@ type AppSettings struct {
EnvPrefix string // environment variable MYAPP_.....
GitVersion string
GitRevision string
LogLevel string // Global log level for the app
}

type CLI struct {
Expand Down Expand Up @@ -50,9 +51,9 @@ func (c *CLI) init() {
Run: func(cmd *cobra.Command, args []string) {
err := c.checkConfig()
if err != nil {
fmt.Printf("%s\n", c.checkConfig().Error())
logger.Errorf("%s\n", c.checkConfig().Error())
} else {
fmt.Printf("Configuration OK\n")
logger.Infof("Configuration OK\n")
}
},
}
Expand All @@ -61,15 +62,18 @@ func (c *CLI) init() {
Use: "version",
Short: "Print version",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("%s\n", c.GetVersionString())
logger.Infof("%s\n", c.GetVersionString())
},
}

c.GetRoot().AddCommand(checkCmd)
c.GetRoot().AddCommand(versionCmd)

// TODO: We can make this optional? and more configurable if we see the need
logger.InitLogger(logger.Config{Level: constants.DebugLevel})
// If app log level is defined it is configued, logger.defaultConfig by default
if len(c.app.LogLevel) > 0 {
logger.InitLogger(logger.Config{Level: c.app.LogLevel})
}

setupCloseHandler(nil)
// Set Configuration Defaults
setupDefaultConfiguration(func() {
Expand All @@ -91,10 +95,7 @@ func (c *CLI) GetVersionString() string {

func (c *CLI) Run() {
if err := c.rootCmd.Execute(); err != nil {
_, err := fmt.Fprintln(os.Stderr, err)
if err != nil {
return
}
logger.Error(err.Error())
_ = logger.Sync()
os.Exit(1)
}
Expand Down
162 changes: 162 additions & 0 deletions pkg/logger/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Logger Package

A structured logging package built on top of [uber-go/zap](https://github.com/uber-go/zap), providing both global and context-aware logging capabilities with sensible defaults.

## Features

- Built on top of the high-performance zap logger
- Supports both structured and printf-style logging
- Context-aware logging
- Global and instance-based logging
- Configurable log levels and encoding formats
- Request ID tracking support
- Easy integration with existing applications

## Installation

```go
go get -u github.com/zondax/golem/pkg/logger
```

## Quick Start

```go
// Initialize with default configuration
logger.InitLogger(logger.Config{
Level: "info",
Encoding: "json",
})

// Basic logging
logger.Info("Server started")
logger.Error("Connection failed")

// Structured logging with fields
log := logger.NewLogger(
logger.Field{Key: "service", Value: "api"},
logger.Field{Key: "version", Value: "1.0.0"},
)
log.Info("Service initialized")
```

## Configuration

### Logger Config

```go
type Config struct {
Level string `json:"level"` // Logging level
Encoding string `json:"encoding"` // Output format
}
```

### Log Levels

Available log levels (in order of increasing severity):
- `debug`: Detailed information for debugging
- `info`: General operational information
- `warn`: Warning messages for potentially harmful situations
- `error`: Error conditions that should be addressed
- `dpanic`: Critical errors in development that cause panic
- `panic`: Critical errors that cause panic in production
- `fatal`: Fatal errors that terminate the program

### Encoding Formats

1. **JSON Format** (Default)
- Recommended for production
- Machine-readable structured output
```json
{"level":"INFO","ts":"2024-03-20T10:00:00.000Z","msg":"Server started","service":"api"}
```

2. **Console Format**
- Recommended for development
- Human-readable output
```
2024-03-20T10:00:00.000Z INFO Server started service=api
```

## Advanced Usage

### Context-Aware Logging

```go
// Create a context with logger
ctx := context.Background()
log := logger.NewLogger(logger.Field{
Key: logger.RequestIDKey,
Value: "req-123",
})
ctx = logger.ContextWithLogger(ctx, log)

// Get logger from context
contextLogger := logger.GetLoggerFromContext(ctx)
contextLogger.Info("Processing request")
```

### Structured Logging with Fields

```go
log := logger.NewLogger()
log.WithFields(
zap.String("user_id", "12345"),
zap.String("action", "login"),
zap.Int("attempt", 1),
).Info("User login attempt")
```

### Printf-Style Logging

```go
logger.Infof("Processing item %d of %d", current, total)
logger.Errorf("Failed to connect to %s: %v", host, err)
```

## Best Practices

1. **Use Structured Logging**
```go
// Good
log.WithFields(
zap.String("user_id", "12345"),
zap.String("action", "purchase"),
zap.Float64("amount", 99.99),
).Info("Purchase completed")

// Avoid
log.Infof("User %s completed purchase of $%.2f", userID, amount)
```

2. **Include Request IDs**
```go
log.WithFields(
zap.String(logger.RequestIDKey, requestID),
).Info("Handling request")
```

3. **Proper Error Logging**
```go
if err != nil {
log.WithFields(
zap.Error(err),
zap.String("operation", "database_query"),
).Error("Query failed")
}
```

4. **Resource Cleanup**
```go
defer logger.Sync()
```

## Performance Considerations

- The logger is designed to be zero-allocation in most cases
- JSON encoding is more CPU-intensive but provides structured data
- Log level checks are performed atomically
- Field allocation is optimized for minimal overhead

## Thread Safety

The logger is completely thread-safe and can be used concurrently from multiple goroutines.
Loading

0 comments on commit f526f8c

Please sign in to comment.