diff --git a/internal/files/download.go b/internal/files/download.go index a44a483..f4a8835 100644 --- a/internal/files/download.go +++ b/internal/files/download.go @@ -3,10 +3,9 @@ package files import ( "fmt" "io" + "log/slog" "net/http" "os" - - "github.com/trezorg/lingualeo/internal/logger" ) const ( @@ -56,7 +55,7 @@ func (f *FileDownloader) Download(url string) (string, error) { defer func() { cErr := fd.Close() if cErr != nil { - logger.Error(cErr) + slog.Error("cannot close write file descriptor", "error", cErr) } }() resp, err := http.Get(url) @@ -66,7 +65,7 @@ func (f *FileDownloader) Download(url string) (string, error) { defer func() { cErr := resp.Body.Close() if cErr != nil { - logger.Error(cErr) + slog.Error("cannot close response body", "error", cErr) } }() if resp.StatusCode != http.StatusOK { diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 6dd819e..a4ea713 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -1,52 +1,37 @@ package logger import ( + "fmt" + "log/slog" "os" - "sync" - - "github.com/sirupsen/logrus" ) -var log *logrus.Entry -var onceLog sync.Once - -type utcFormatter struct { - logrus.Formatter -} - -func (u utcFormatter) Format(e *logrus.Entry) ([]byte, error) { - e.Time = e.Time.UTC() - return u.Formatter.Format(e) -} - -func initLogger(logLevel string, logPrettyPrint bool) { - - level, err := logrus.ParseLevel(logLevel) - if err != nil { - logrus.Fatalf("Cannot parse log level: %s", logLevel) +var levelVar = new(slog.LevelVar) + +func ParseLevel(level string) (slog.Level, error) { + switch level { + case "DEBUG": + return slog.LevelDebug, nil + case "INFO": + return slog.LevelInfo, nil + case "WARNING", "WARN": + return slog.LevelWarn, nil + case "ERROR": + return slog.LevelError, nil + default: + return slog.LevelError, fmt.Errorf("unknown log level: %s", level) } - - logrus.SetFormatter(utcFormatter{&logrus.JSONFormatter{ - PrettyPrint: logPrettyPrint, - TimestampFormat: "2006-01-02 15:04:05 -0700", - }}) - logrus.SetReportCaller(true) - logrus.SetOutput(os.Stdout) - logrus.SetLevel(level) - log = logrus.WithFields(logrus.Fields{"service": "lingualeo"}) -} - -func Error(args ...interface{}) { - log.Error(args...) } -func Debug(args ...interface{}) { - log.Debug(args...) +func SetLevel(level slog.Level) { + levelVar.Set(level) } -func InitLogger(logLevel string, logPrettyPrint bool) *logrus.Entry { - onceLog.Do(func() { - initLogger(logLevel, logPrettyPrint) +func Prepare(level slog.Level) { + SetLevel(level) + handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + Level: levelVar, }) - return log + logger := slog.New(handler) + slog.SetDefault(logger) } diff --git a/pkg/api/api.go b/pkg/api/api.go index f28a6f2..37455ec 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "net/http/cookiejar" "net/http/httputil" @@ -18,7 +19,6 @@ import ( "github.com/sirupsen/logrus" - "github.com/trezorg/lingualeo/internal/logger" "github.com/trezorg/lingualeo/pkg/channel" "golang.org/x/net/publicsuffix" @@ -172,12 +172,12 @@ func request(method string, url string, client *http.Client, body []byte, query defer func() { dErr := resp.Body.Close() if dErr != nil { - logger.Error(dErr) + slog.Error("cannot close response body", "error", dErr) } }() responseBody, err := readBody(resp) if err != nil { - logger.Error(err) + slog.Error("cannot read response body", "error", err) return nil, err } if resp.StatusCode != 200 { diff --git a/pkg/api/helpers.go b/pkg/api/helpers.go index 81b3a57..437c0b4 100644 --- a/pkg/api/helpers.go +++ b/pkg/api/helpers.go @@ -4,18 +4,17 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "github.com/trezorg/lingualeo/pkg/messages" - - "github.com/trezorg/lingualeo/internal/logger" ) func readBody(resp *http.Response) ([]byte, error) { defer func() { err := resp.Body.Close() if err != nil { - logger.Error(err) + slog.Error("cannot close response body", "error", err) } }() return io.ReadAll(resp.Body) @@ -46,11 +45,11 @@ func printTranslation(result *Result) { } err := messages.Message(messages.RED, "Found %s word:\n", strTitle) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } err = messages.Message(messages.GREEN, "['%s'] (%s)\n", result.Word, result.Transcription) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } for _, word := range result.Words { _ = messages.Message(messages.YELLOW, "%s\n", word) @@ -66,10 +65,10 @@ func printAddedTranslation(result *Result) { } err := messages.Message(messages.RED, "%s word: ", strTitle) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } err = messages.Message(messages.GREEN, "['%s']\n", result.Word) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } } diff --git a/pkg/translator/get_word_test.go b/pkg/translator/get_word_test.go index bf09fcb..e6fccc5 100644 --- a/pkg/translator/get_word_test.go +++ b/pkg/translator/get_word_test.go @@ -2,6 +2,7 @@ package translator import ( "context" + "log/slog" "sync" "testing" @@ -28,7 +29,7 @@ func TestProcessTranslationResponseJson(t *testing.T) { downloader.EXPECT().Download(fakeapi.SoundURL).Return(testFile, nil).Times(count) translator.EXPECT().TranslateWord(fakeapi.SearchWord).Return(res).Times(count) - logger.InitLogger("FATAL", true) + logger.Prepare(slog.LevelError + 1) searchWords := make([]string, 0, count) for i := 0; i < count; i++ { diff --git a/pkg/translator/linguleo.go b/pkg/translator/linguleo.go index 662ea61..43c9005 100644 --- a/pkg/translator/linguleo.go +++ b/pkg/translator/linguleo.go @@ -2,6 +2,7 @@ package translator import ( "context" + "log/slog" "os" "strings" "sync" @@ -25,13 +26,13 @@ func (args *Lingualeo) checkMediaPlayer() { if len(args.Player) == 0 { err := messages.Message(messages.RED, "Please set player parameter\n") if err != nil { - logger.Debug(err) + slog.Error("cannot show message", "error", err) } args.Sound = false } else if !isCommandAvailable(args.Player) { err := messages.Message(messages.RED, "Executable file %s is not available on your system\n", args.Player) if err != nil { - logger.Debug(err) + slog.Error("cannot show message", "error", err) } args.Sound = false } @@ -87,7 +88,11 @@ func New(version string) (Lingualeo, error) { client.LogLevel = logrus.DebugLevel.String() client.LogPrettyPrint = true } - logger.InitLogger(client.LogLevel, client.LogPrettyPrint) + level, err := logger.ParseLevel(client.LogLevel) + if err != nil { + return client, err + } + logger.Prepare(level) client.checkMediaPlayer() client.Translator, err = api.New(client.Email, client.Password, client.Debug) if err != nil { @@ -155,7 +160,7 @@ func (args *Lingualeo) translateWords(ctx context.Context) <-chan api.OperationR cases.Title(language.Make(strings.ToLower(res.Error.Error()))), ) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } continue } @@ -163,7 +168,7 @@ func (args *Lingualeo) translateWords(ctx context.Context) <-chan api.OperationR _ = messages.Message(messages.RED, "There are no translations for word: ") err := messages.Message(messages.GREEN, "['%s']\n", res.Result.Word) if err != nil { - logger.Error(err) + slog.Error("cannot show message", "error", err) } continue } @@ -189,7 +194,7 @@ func (args *Lingualeo) downloadAndPronounce(ctx context.Context, urls <-chan str fileChannel := files.OrderedChannel(DownloadFiles(ctx, urls, downloader), len(urls)) for res := range channel.OrDone(ctx, fileChannel) { if res.Error != nil { - logger.Error(res.Error) + slog.Error("cannot download", "error", res.Error) continue } if res.Filename == "" { @@ -197,21 +202,21 @@ func (args *Lingualeo) downloadAndPronounce(ctx context.Context, urls <-chan str } err := PlaySound(args.Player, res.Filename) if err != nil { - logger.Error(err) + slog.Error("cannot play filename", "filename", res.Filename, "error", err) } err = os.Remove(res.Filename) if err != nil { - logger.Error(err) + slog.Error("cannot remove filename", "filename", res.Filename, "error", err) } } } func (args *Lingualeo) pronounce(ctx context.Context, urls <-chan string, wg *sync.WaitGroup) { defer wg.Done() - for res := range channel.OrDone(ctx, urls) { - err := PlaySound(args.Player, res) + for url := range channel.OrDone(ctx, urls) { + err := PlaySound(args.Player, url) if err != nil { - logger.Error(err) + slog.Error("cannot play url", "url", url, "error", err) } } } @@ -231,7 +236,7 @@ func (args *Lingualeo) AddToDictionary(ctx context.Context, resultsToAdd <-chan ch := addWords(ctx, args.Translator, resultsToAdd) for res := range ch { if res.Error != nil { - logger.Error(res.Error) + slog.Error("cannot add word to dictionary", "word", res.Result.Word, "error", res.Error) continue } res.Result.PrintAddedTranslation() @@ -254,7 +259,7 @@ func (args *Lingualeo) Process(ctx context.Context, wg *sync.WaitGroup) (<-chan for result := range args.translateWords(ctx) { if result.Error != nil { - logger.Error(result.Error) + slog.Error("cannot translate word", "word", result.Result.Word, "error", result.Error) continue } if args.Sound { @@ -312,7 +317,7 @@ func (args *Lingualeo) TranslateWithReverseRussian(ctx context.Context, resultFu var englishWords []string for result := range channel.OrDone(ctx, args.translateToChan(ctx)) { if err := resultFunc(result); err != nil { - logger.Error(err) + slog.Error("cannot translate word", "word", result.Word, "error", err) } for _, word := range result.Words { if args.ReverseTranslate && isEnglishWord(word) { @@ -324,7 +329,7 @@ func (args *Lingualeo) TranslateWithReverseRussian(ctx context.Context, resultFu args.Words = englishWords for result := range channel.OrDone(ctx, args.translateToChan(ctx)) { if err := resultFunc(result); err != nil { - logger.Error(err) + slog.Error("cannot translate word", "word", result.Word, "error", err) } } }