diff --git a/asset/README.md b/asset/README.md
index 07e8c9b..54e3e50 100644
--- a/asset/README.md
+++ b/asset/README.md
@@ -381,7 +381,7 @@ type Snapshot struct {
```
-## type [Sync]()
+## type [Sync]()
Sync represents the configuration parameters for synchronizing assets between repositories.
@@ -396,11 +396,14 @@ type Sync struct {
// Assets is the name of the assets to be synced. If it is empty, all assets in the target repository
// will be synced instead.
Assets []string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
```
-### func [NewSync]()
+### func [NewSync]()
```go
func NewSync() *Sync
@@ -409,7 +412,7 @@ func NewSync() *Sync
NewSync function initializes a new sync instance with the default parameters.
-### func \(\*Sync\) [Run]()
+### func \(\*Sync\) [Run]()
```go
func (s *Sync) Run(source, target Repository, defaultStartDate time.Time) error
@@ -502,7 +505,7 @@ type TiingoMeta struct {
```
-## type [TiingoRepository]()
+## type [TiingoRepository]()
TiingoRepository provides access to financial market data, retrieving asset snapshots, by interacting with the Tiingo Stock & Financial Markets API. To use this repository, you'll need a valid API key from https://www.tiingo.com.
@@ -512,12 +515,15 @@ type TiingoRepository struct {
// BaseURL is the Tiingo API URL.
BaseURL string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
// contains filtered or unexported fields
}
```
-### func [NewTiingoRepository]()
+### func [NewTiingoRepository]()
```go
func NewTiingoRepository(apiKey string) *TiingoRepository
@@ -526,7 +532,7 @@ func NewTiingoRepository(apiKey string) *TiingoRepository
NewTiingoRepository initializes a file system repository with the given API key.
-### func \(\*TiingoRepository\) [Append]()
+### func \(\*TiingoRepository\) [Append]()
```go
func (*TiingoRepository) Append(_ string, _ <-chan *Snapshot) error
@@ -535,7 +541,7 @@ func (*TiingoRepository) Append(_ string, _ <-chan *Snapshot) error
Append adds the given snapshows to the asset with the given name.
-### func \(\*TiingoRepository\) [Assets]()
+### func \(\*TiingoRepository\) [Assets]()
```go
func (*TiingoRepository) Assets() ([]string, error)
@@ -544,7 +550,7 @@ func (*TiingoRepository) Assets() ([]string, error)
Assets returns the names of all assets in the repository.
-### func \(\*TiingoRepository\) [Get]()
+### func \(\*TiingoRepository\) [Get]()
```go
func (r *TiingoRepository) Get(name string) (<-chan *Snapshot, error)
@@ -553,7 +559,7 @@ func (r *TiingoRepository) Get(name string) (<-chan *Snapshot, error)
Get attempts to return a channel of snapshots for the asset with the given name.
-### func \(\*TiingoRepository\) [GetSince]()
+### func \(\*TiingoRepository\) [GetSince]()
```go
func (r *TiingoRepository) GetSince(name string, date time.Time) (<-chan *Snapshot, error)
@@ -562,7 +568,7 @@ func (r *TiingoRepository) GetSince(name string, date time.Time) (<-chan *Snapsh
GetSince attempts to return a channel of snapshots for the asset with the given name since the given date.
-### func \(\*TiingoRepository\) [LastDate]()
+### func \(\*TiingoRepository\) [LastDate]()
```go
func (r *TiingoRepository) LastDate(name string) (time.Time, error)
diff --git a/asset/sync.go b/asset/sync.go
index 0e9be6d..f7a8c95 100644
--- a/asset/sync.go
+++ b/asset/sync.go
@@ -6,7 +6,7 @@ package asset
import (
"errors"
- "log"
+ "log/slog"
"sync"
"time"
@@ -32,6 +32,9 @@ type Sync struct {
// Assets is the name of the assets to be synced. If it is empty, all assets in the target repository
// will be synced instead.
Assets []string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
// NewSync function initializes a new sync instance with the default parameters.
@@ -40,13 +43,14 @@ func NewSync() *Sync {
Workers: DefaultSyncWorkers,
Delay: DefaultSyncDelay,
Assets: []string{},
+ Logger: slog.Default(),
}
}
// Run synchronizes assets between the source and target repositories using multi-worker concurrency.
func (s *Sync) Run(source, target Repository, defaultStartDate time.Time) error {
if len(s.Assets) == 0 {
- log.Print("No asset names provided. Syncing in all assets in the target repository.")
+ s.Logger.Warn("No asset names provided. Syncing in all assets in the target repository.")
assets, err := target.Assets()
if err != nil {
@@ -56,7 +60,7 @@ func (s *Sync) Run(source, target Repository, defaultStartDate time.Time) error
s.Assets = assets
}
- log.Printf("Will sync %d assets.", len(s.Assets))
+ s.Logger.Info("Start syncing.", "assets", len(s.Assets))
jobs := helper.SliceToChan(s.Assets)
hasErrors := false
@@ -76,18 +80,18 @@ func (s *Sync) Run(source, target Repository, defaultStartDate time.Time) error
lastDate = defaultStartDate
}
- log.Printf("Syncing %s starting %s", name, lastDate.Format("2006-01-02"))
+ s.Logger.Info("Syncing asset.", "asset", name, "start", lastDate.Format("2006-01-02"))
snapshots, err := source.GetSince(name, lastDate)
if err != nil {
- log.Printf("GetSince failed for %s with %v", name, err)
+ s.Logger.Error("GetSince failed.", "asset", name, "error", err)
hasErrors = true
continue
}
err = target.Append(name, snapshots)
if err != nil {
- log.Printf("Append failed for %s with %v", name, err)
+ s.Logger.Error("Append failed.", "asset", name, "error", err)
hasErrors = true
continue
}
diff --git a/asset/tiingo_repository.go b/asset/tiingo_repository.go
index a8a9a75..750afd6 100644
--- a/asset/tiingo_repository.go
+++ b/asset/tiingo_repository.go
@@ -9,7 +9,7 @@ import (
"errors"
"fmt"
"io"
- "log"
+ "log/slog"
"net/http"
"time"
)
@@ -106,6 +106,9 @@ type TiingoRepository struct {
// BaseURL is the Tiingo API URL.
BaseURL string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
// NewTiingoRepository initializes a file system repository with
@@ -115,6 +118,7 @@ func NewTiingoRepository(apiKey string) *TiingoRepository {
apiKey: apiKey,
client: &http.Client{},
BaseURL: "https://api.tiingo.com",
+ Logger: slog.Default(),
}
}
@@ -159,7 +163,7 @@ func (r *TiingoRepository) GetSince(name string, date time.Time) (<-chan *Snapsh
_, err = decoder.Token()
if err != nil {
- log.Print(err)
+ r.Logger.Error("Unable to read token.", "error", err)
return
}
@@ -168,7 +172,7 @@ func (r *TiingoRepository) GetSince(name string, date time.Time) (<-chan *Snapsh
err = decoder.Decode(&data)
if err != nil {
- log.Print(err)
+ r.Logger.Error("Unable to decode data.", "error", err)
break
}
@@ -177,13 +181,13 @@ func (r *TiingoRepository) GetSince(name string, date time.Time) (<-chan *Snapsh
_, err = decoder.Token()
if err != nil {
- log.Printf("GetSince failed with %v", err)
+ r.Logger.Error("GetSince failed.", "error", err)
return
}
err = res.Body.Close()
if err != nil {
- log.Print(err)
+ r.Logger.Error("Unable to close respose.", "error", err)
}
}()
diff --git a/backtest/README.md b/backtest/README.md
index b25002d..09bee26 100644
--- a/backtest/README.md
+++ b/backtest/README.md
@@ -91,7 +91,7 @@ func RegisterReportBuilder(name string, builder ReportBuilderFunc)
RegisterReportBuilder registers the given builder.
-## type [Backtest]()
+## type [Backtest]()
Backtest function rigorously evaluates the potential performance of the specified strategies applied to a defined set of assets. It generates comprehensive visual representations for each strategy\-asset pairing.
@@ -109,12 +109,15 @@ type Backtest struct {
// LastDays is the number of days backtest should go back.
LastDays int
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
// contains filtered or unexported fields
}
```
-### func [NewBacktest]()
+### func [NewBacktest]()
```go
func NewBacktest(repository asset.Repository, report Report) *Backtest
@@ -123,7 +126,7 @@ func NewBacktest(repository asset.Repository, report Report) *Backtest
NewBacktest function initializes a new backtest instance.
-### func \(\*Backtest\) [Run]()
+### func \(\*Backtest\) [Run]()
```go
func (b *Backtest) Run() error
@@ -222,7 +225,7 @@ type DataStrategyResult struct {
```
-## type [HTMLReport]()
+## type [HTMLReport]()
HTMLReport is the backtest HTML report.
@@ -235,12 +238,15 @@ type HTMLReport struct {
// DateFormat is the date format that is used in the reports.
DateFormat string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
// contains filtered or unexported fields
}
```
-### func [NewHTMLReport]()
+### func [NewHTMLReport]()
```go
func NewHTMLReport(outputDir string) *HTMLReport
@@ -249,7 +255,7 @@ func NewHTMLReport(outputDir string) *HTMLReport
NewHTMLReport initializes a new HTML report instance.
-### func \(\*HTMLReport\) [AssetBegin]()
+### func \(\*HTMLReport\) [AssetBegin]()
```go
func (h *HTMLReport) AssetBegin(name string, strategies []strategy.Strategy) error
@@ -258,7 +264,7 @@ func (h *HTMLReport) AssetBegin(name string, strategies []strategy.Strategy) err
AssetBegin is called when backtesting for the given asset begins.
-### func \(\*HTMLReport\) [AssetEnd]()
+### func \(\*HTMLReport\) [AssetEnd]()
```go
func (h *HTMLReport) AssetEnd(name string) error
@@ -267,7 +273,7 @@ func (h *HTMLReport) AssetEnd(name string) error
AssetEnd is called when backtesting for the given asset ends.
-### func \(\*HTMLReport\) [Begin]()
+### func \(\*HTMLReport\) [Begin]()
```go
func (h *HTMLReport) Begin(assetNames []string, _ []strategy.Strategy) error
@@ -276,7 +282,7 @@ func (h *HTMLReport) Begin(assetNames []string, _ []strategy.Strategy) error
Begin is called when the backtest starts.
-### func \(\*HTMLReport\) [End]()
+### func \(\*HTMLReport\) [End]()
```go
func (h *HTMLReport) End() error
@@ -285,7 +291,7 @@ func (h *HTMLReport) End() error
End is called when the backtest ends.
-### func \(\*HTMLReport\) [Write]()
+### func \(\*HTMLReport\) [Write]()
```go
func (h *HTMLReport) Write(assetName string, currentStrategy strategy.Strategy, snapshots <-chan *asset.Snapshot, actions <-chan strategy.Action, outcomes <-chan float64) error
diff --git a/backtest/backtest.go b/backtest/backtest.go
index 1ab2c71..f0ae39f 100644
--- a/backtest/backtest.go
+++ b/backtest/backtest.go
@@ -20,7 +20,7 @@ package backtest
import (
"fmt"
- "log"
+ "log/slog"
"sync"
"time"
@@ -58,6 +58,9 @@ type Backtest struct {
// LastDays is the number of days backtest should go back.
LastDays int
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
// NewBacktest function initializes a new backtest instance.
@@ -69,6 +72,7 @@ func NewBacktest(repository asset.Repository, report Report) *Backtest {
Strategies: []strategy.Strategy{},
Workers: DefaultBacktestWorkers,
LastDays: DefaultLastDays,
+ Logger: slog.Default(),
}
}
@@ -130,10 +134,10 @@ func (b *Backtest) worker(names <-chan string, wg *sync.WaitGroup) {
since := time.Now().AddDate(0, 0, -b.LastDays)
for name := range names {
- log.Printf("Backtesting %s...", name)
+ b.Logger.Info("Backtesting started.", "asset", name)
snapshots, err := b.repository.GetSince(name, since)
if err != nil {
- log.Printf("Unable to retrieve the snapshots for %s: %v", name, err)
+ b.Logger.Error("Unable to retrieve snapshots.", "asset", name, "error", err)
continue
}
@@ -143,7 +147,7 @@ func (b *Backtest) worker(names <-chan string, wg *sync.WaitGroup) {
// Backtesting asset has begun.
err = b.report.AssetBegin(name, b.Strategies)
if err != nil {
- log.Printf("Unable to asset begin for %s: %v", name, err)
+ b.Logger.Error("Unable to begin asset.", "asset", name, "error", err)
continue
}
@@ -154,14 +158,14 @@ func (b *Backtest) worker(names <-chan string, wg *sync.WaitGroup) {
actions, outcomes := strategy.ComputeWithOutcome(currentStrategy, snapshotsSplice[0])
err = b.report.Write(name, currentStrategy, snapshotsSplice[1], actions, outcomes)
if err != nil {
- log.Printf("Unable to write report for %s: %v", name, err)
+ b.Logger.Error("Unable to write report.", "asset", name, "error", err)
}
}
// Backtesting asset had ended
err = b.report.AssetEnd(name)
if err != nil {
- log.Printf("Unable to asset end for %s: %v", name, err)
+ b.Logger.Error("Unable to end asset.", "asset", name, "error", err)
}
}
}
diff --git a/backtest/html_report.go b/backtest/html_report.go
index fa6aee4..b50e838 100644
--- a/backtest/html_report.go
+++ b/backtest/html_report.go
@@ -8,7 +8,7 @@ import (
// Go embed report template.
_ "embed"
"fmt"
- "log"
+ "log/slog"
"os"
"path"
"path/filepath"
@@ -50,6 +50,9 @@ type HTMLReport struct {
// DateFormat is the date format that is used in the reports.
DateFormat string
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
// htmlReportResult encapsulates the outcome of running a strategy.
@@ -80,6 +83,7 @@ func NewHTMLReport(outputDir string) *HTMLReport {
assetResults: make(map[string][]*htmlReportResult),
WriteStrategyReports: DefaultWriteStrategyReports,
DateFormat: helper.DefaultReportDateFormat,
+ Logger: slog.Default(),
}
}
@@ -168,7 +172,7 @@ func (h *HTMLReport) AssetEnd(name string) error {
bestResult := results[0]
// Report the best result for the current asset.
- log.Printf("Best outcome for %s is %.2f%% with %s.", name, bestResult.Outcome, bestResult.StrategyName)
+ h.Logger.Info("Best outcome", "asset", name, "strategy", bestResult.StrategyName, "outcome", bestResult.Outcome)
h.bestResults = append(h.bestResults, bestResult)
// Write the asset report.
diff --git a/cmd/indicator-backtest/main.go b/cmd/indicator-backtest/main.go
index a02292b..c8242cb 100644
--- a/cmd/indicator-backtest/main.go
+++ b/cmd/indicator-backtest/main.go
@@ -8,7 +8,7 @@ package main
import (
"flag"
"fmt"
- "log"
+ "log/slog"
"os"
"github.com/cinar/indicator/v2/asset"
@@ -46,19 +46,24 @@ func main() {
flag.BoolVar(&addAnds, "ands", false, "add the and strategies")
flag.Parse()
+ logger := slog.Default()
+
source, err := asset.NewRepository(repositoryName, repositoryConfig)
if err != nil {
- log.Fatalf("unable to initialize source: %v", err)
+ logger.Error("Unable to initialize source.", "error", err)
+ os.Exit(1)
}
report, err := backtest.NewReport(repositoryName, repositoryConfig)
if err != nil {
- log.Fatalf("unable to initialize report: %v", err)
+ logger.Error("Unable to initialize report.", "error", err)
+ os.Exit(1)
}
backtester := backtest.NewBacktest(source, report)
backtester.Workers = workers
backtester.LastDays = lastDays
+ backtester.Logger = logger
backtester.Names = append(backtester.Names, flag.Args()...)
backtester.Strategies = append(backtester.Strategies, compound.AllStrategies()...)
backtester.Strategies = append(backtester.Strategies, momentum.AllStrategies()...)
@@ -76,6 +81,7 @@ func main() {
err = backtester.Run()
if err != nil {
- log.Fatalf("unable to run backtest: %v", err)
+ logger.Error("Unable to run backtest.", "error", err)
+ os.Exit(1)
}
}
diff --git a/cmd/indicator-sync/main.go b/cmd/indicator-sync/main.go
index d57a8d4..0aa884e 100644
--- a/cmd/indicator-sync/main.go
+++ b/cmd/indicator-sync/main.go
@@ -8,7 +8,7 @@ package main
import (
"flag"
"fmt"
- "log"
+ "log/slog"
"os"
"time"
@@ -39,14 +39,18 @@ func main() {
flag.IntVar(&delay, "delay", asset.DefaultSyncDelay, "delay between each get")
flag.Parse()
+ logger := slog.Default()
+
source, err := asset.NewRepository(sourceName, sourceConfig)
if err != nil {
- log.Fatalf("unable to initialize source: %v", err)
+ logger.Error("Unable to initialize source.", "error", err)
+ os.Exit(1)
}
target, err := asset.NewRepository(targetName, targetConfig)
if err != nil {
- log.Fatalf("unable to initialize target: %v", err)
+ logger.Error("Unable to initialize target.", "error", err)
+ os.Exit(1)
}
defaultStartDate := time.Now().AddDate(0, 0, -minusDays)
@@ -55,7 +59,8 @@ func main() {
if len(assets) == 0 {
assets, err = source.Assets()
if err != nil {
- log.Fatalf("unable to get assets: %v", err)
+ logger.Error("Unable to get assets.", "error", err)
+ os.Exit(1)
}
}
@@ -63,9 +68,11 @@ func main() {
sync.Workers = workers
sync.Delay = delay
sync.Assets = assets
+ sync.Logger = logger
err = sync.Run(source, target, defaultStartDate)
if err != nil {
- log.Fatalf("unable to sync repositories: %v", err)
+ logger.Error("Unable to sync repositories.", "error", err)
+ os.Exit(1)
}
}
diff --git a/helper/README.md b/helper/README.md
index 2ba6371..15fd1ee 100644
--- a/helper/README.md
+++ b/helper/README.md
@@ -37,6 +37,7 @@ The information provided on this project is strictly for informational purposes
- [func ChangeRatio\[T Number\]\(c \<\-chan T, before int\) \<\-chan T](<#ChangeRatio>)
- [func CheckEquals\[T comparable\]\(inputs ...\<\-chan T\) error](<#CheckEquals>)
- [func CloseAndLogError\(closer io.Closer, message string\)](<#CloseAndLogError>)
+- [func CloseAndLogErrorWithLogger\(closer io.Closer, message string, logger \*slog.Logger\)](<#CloseAndLogErrorWithLogger>)
- [func CommonPeriod\(periods ...int\) int](<#CommonPeriod>)
- [func Count\[T Number, O any\]\(from T, other \<\-chan O\) \<\-chan T](<#Count>)
- [func DaysBetween\(from, to time.Time\) int](<#DaysBetween>)
@@ -53,6 +54,7 @@ The information provided on this project is strictly for informational purposes
- [func Head\[T Number\]\(c \<\-chan T, count int\) \<\-chan T](<#Head>)
- [func IncrementBy\[T Number\]\(c \<\-chan T, i T\) \<\-chan T](<#IncrementBy>)
- [func JSONToChan\[T any\]\(r io.Reader\) \<\-chan T](<#JSONToChan>)
+- [func JSONToChanWithLogger\[T any\]\(r io.Reader, logger \*slog.Logger\) \<\-chan T](<#JSONToChanWithLogger>)
- [func KeepNegatives\[T Number\]\(c \<\-chan T\) \<\-chan T](<#KeepNegatives>)
- [func KeepPositives\[T Number\]\(c \<\-chan T\) \<\-chan T](<#KeepPositives>)
- [func Last\[T any\]\(c \<\-chan T, count int\) \<\-chan T](<#Last>)
@@ -176,7 +178,7 @@ fmt.Println(actual) // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
```
-## func [AppendOrWriteToCsvFile]()
+## func [AppendOrWriteToCsvFile]()
```go
func AppendOrWriteToCsvFile[T any](fileName string, hasHeader bool, rows <-chan *T) error
@@ -324,6 +326,15 @@ func CloseAndLogError(closer io.Closer, message string)
CloseAndLogError attempts to close the closer and logs any error.
+
+## func [CloseAndLogErrorWithLogger]()
+
+```go
+func CloseAndLogErrorWithLogger(closer io.Closer, message string, logger *slog.Logger)
+```
+
+CloseAndLogErrorWithLogger attempts to close the closer and logs any error to the given logger.
+
## func [CommonPeriod]()
@@ -562,7 +573,7 @@ fmt.Println(helper.ChanToSlice(actual)) // [2, 3, 4, 5]
```
-## func [JSONToChan]()
+## func [JSONToChan]()
```go
func JSONToChan[T any](r io.Reader) <-chan T
@@ -570,7 +581,14 @@ func JSONToChan[T any](r io.Reader) <-chan T
JSONToChan reads values from the specified reader in JSON format into a channel of values.
-Example:
+
+## func [JSONToChanWithLogger]()
+
+```go
+func JSONToChanWithLogger[T any](r io.Reader, logger *slog.Logger) <-chan T
+```
+
+JSONToChanWithLogger reads values from the specified reader in JSON format into a channel of values.
## func [KeepNegatives]()
@@ -765,7 +783,7 @@ fmt.Println(helper.ChanToSlice(squared)) // [4, 9, 25, 100]
```
-## func [ReadFromCsvFile]()
+## func [ReadFromCsvFile]()
```go
func ReadFromCsvFile[T any](fileName string, hasHeader bool) (<-chan *T, error)
@@ -1037,18 +1055,21 @@ type BstNode[T Number] struct {
```
-## type [Csv]()
+## type [Csv]()
Csv represents the configuration for CSV reader and writer.
```go
type Csv[T any] struct {
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
// contains filtered or unexported fields
}
```
-### func [NewCsv]()
+### func [NewCsv]()
```go
func NewCsv[T any](hasHeader bool) (*Csv[T], error)
@@ -1057,7 +1078,7 @@ func NewCsv[T any](hasHeader bool) (*Csv[T], error)
NewCsv function initializes a new CSV instance. The parameter hasHeader indicates whether the CSV contains a header row.
-### func \(\*Csv\[T\]\) [AppendToFile]()
+### func \(\*Csv\[T\]\) [AppendToFile]()
```go
func (c *Csv[T]) AppendToFile(fileName string, rows <-chan *T) error
@@ -1066,7 +1087,7 @@ func (c *Csv[T]) AppendToFile(fileName string, rows <-chan *T) error
AppendToFile appends the provided rows of data to the end of the specified file, creating the file if it doesn't exist. In append mode, the function assumes that the existing file's column order matches the field order of the given row struct to ensure consistent data structure.
-### func \(\*Csv\[T\]\) [ReadFromFile]()
+### func \(\*Csv\[T\]\) [ReadFromFile]()
```go
func (c *Csv[T]) ReadFromFile(fileName string) (<-chan *T, error)
@@ -1075,7 +1096,7 @@ func (c *Csv[T]) ReadFromFile(fileName string) (<-chan *T, error)
ReadFromFile parses the CSV data from the provided file name, maps the data to corresponding struct fields, and delivers the resulting rows through the channel.
-### func \(\*Csv\[T\]\) [ReadFromReader]()
+### func \(\*Csv\[T\]\) [ReadFromReader]()
```go
func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T
@@ -1084,7 +1105,7 @@ func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T
ReadFromReader parses the CSV data from the provided reader, maps the data to corresponding struct fields, and delivers the resulting it through the channel.
-### func \(\*Csv\[T\]\) [WriteToFile]()
+### func \(\*Csv\[T\]\) [WriteToFile]()
```go
func (c *Csv[T]) WriteToFile(fileName string, rows <-chan *T) error
diff --git a/helper/closer.go b/helper/closer.go
index 9e73ae8..d60004a 100644
--- a/helper/closer.go
+++ b/helper/closer.go
@@ -6,13 +6,18 @@ package helper
import (
"io"
- "log"
+ "log/slog"
)
// CloseAndLogError attempts to close the closer and logs any error.
func CloseAndLogError(closer io.Closer, message string) {
+ CloseAndLogErrorWithLogger(closer, message, slog.Default())
+}
+
+// CloseAndLogErrorWithLogger attempts to close the closer and logs any error to the given logger.
+func CloseAndLogErrorWithLogger(closer io.Closer, message string, logger *slog.Logger) {
err := closer.Close()
if err != nil {
- log.Printf("%s: %v", message, err)
+ logger.Error(message, "error", err)
}
}
diff --git a/helper/csv.go b/helper/csv.go
index 618aa9b..6d5e480 100644
--- a/helper/csv.go
+++ b/helper/csv.go
@@ -9,7 +9,7 @@ import (
"errors"
"io"
"io/fs"
- "log"
+ "log/slog"
"os"
"path/filepath"
"reflect"
@@ -44,6 +44,9 @@ type Csv[T any] struct {
// columns are the mappings between the CSV columns and
// the corresponding struct fields.
columns []csvColumn
+
+ // Logger is the slog logger instance.
+ Logger *slog.Logger
}
// NewCsv function initializes a new CSV instance. The parameter
@@ -51,6 +54,7 @@ type Csv[T any] struct {
func NewCsv[T any](hasHeader bool) (*Csv[T], error) {
c := &Csv[T]{
hasHeader: hasHeader,
+ Logger: slog.Default(),
}
// Row type must be a pointer to struct.
@@ -101,7 +105,7 @@ func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T {
if c.hasHeader {
err := c.updateColumnIndexes(csvReader)
if err != nil {
- log.Printf("Update colum index failed with %v", err)
+ c.Logger.Error("Unable to update the column indexes.", "error", err)
return
}
}
@@ -113,7 +117,7 @@ func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T {
}
if err != nil {
- log.Printf("Read row failed with %v", err)
+ c.Logger.Error("Unable to read row.", "error", err)
break
}
@@ -128,7 +132,7 @@ func (c *Csv[T]) ReadFromReader(reader io.Reader) <-chan *T {
err := setReflectValue(rowValue.Field(column.FieldIndex),
record[column.ColumnIndex], column.Format)
if err != nil {
- log.Printf("set value failed with %v", err)
+ c.Logger.Error("Unable to set value.", "error", err)
return
}
}
@@ -150,13 +154,13 @@ func (c *Csv[T]) ReadFromFile(fileName string) (<-chan *T, error) {
}
wg := &sync.WaitGroup{}
- rows := Waitable[*T](wg, c.ReadFromReader(file))
+ rows := Waitable(wg, c.ReadFromReader(file))
go func() {
wg.Wait()
err := file.Close()
if err != nil {
- log.Printf("file close failed with %v", err)
+ c.Logger.Error("Unable to close file.", "error", err)
}
}()
diff --git a/helper/json_to_chan.go b/helper/json_to_chan.go
index cb4559e..cd7d49b 100644
--- a/helper/json_to_chan.go
+++ b/helper/json_to_chan.go
@@ -7,13 +7,16 @@ package helper
import (
"encoding/json"
"io"
- "log"
+ "log/slog"
)
// JSONToChan reads values from the specified reader in JSON format into a channel of values.
-//
-// Example:
func JSONToChan[T any](r io.Reader) <-chan T {
+ return JSONToChanWithLogger[T](r, slog.Default())
+}
+
+// JSONToChanWithLogger reads values from the specified reader in JSON format into a channel of values.
+func JSONToChanWithLogger[T any](r io.Reader, logger *slog.Logger) <-chan T {
c := make(chan T)
go func() {
@@ -23,12 +26,12 @@ func JSONToChan[T any](r io.Reader) <-chan T {
token, err := decoder.Token()
if err != nil {
- log.Print(err)
+ logger.Error("Unable to read token.", "error", err)
return
}
if token != json.Delim('[') {
- log.Printf("expecting start of array got %v", token)
+ logger.Error("Expecting start of array.", "token", token)
return
}
@@ -37,7 +40,7 @@ func JSONToChan[T any](r io.Reader) <-chan T {
err = decoder.Decode(&value)
if err != nil {
- log.Print(err)
+ logger.Error("Unable to decode value.", "error", err)
return
}
@@ -46,12 +49,12 @@ func JSONToChan[T any](r io.Reader) <-chan T {
token, err = decoder.Token()
if err != nil {
- log.Print(err)
+ logger.Error("Unable to read token.", "error", err)
return
}
if token != json.Delim(']') {
- log.Printf("expecting end of array got %v", token)
+ logger.Error("Expecting end of array.", "token", token)
return
}
}()