Files
proton-bridge/internal/logging/logging.go
Leander Beernaert e92badef0e GODT-2004: Ensure log files don't have color formatting
This patch ensures that log files written to disk do not have any color
formatting present.

Sadly due to limitations of the logrus library, we have to force
coloring enabled on logs to stdout.
2022-11-16 13:48:30 +01:00

150 lines
3.9 KiB
Go

// Copyright (c) 2022 Proton AG
//
// This file is part of Proton Mail Bridge.
//
// Proton Mail Bridge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Proton Mail Bridge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
package logging
import (
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"time"
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
"github.com/sirupsen/logrus"
)
const (
// MaxLogSize defines the maximum log size we should permit: 5 MB
//
// The Zendesk limit for an attachement is 50MB and this is what will
// be allowed via the API. However, if that fails for some reason, the
// fallback is sending the report via email, which has a limit of 10mb
// total or 7MB per file. Since we can produce up to 6 logs, and we
// compress all the files (avarage compression - 80%), we need to have
// a limit of 30MB total before compression, hence 5MB per log file.
MaxLogSize = 5 * 1024 * 1024
// MaxLogs defines how many log files should be kept.
MaxLogs = 3
)
type coloredStdOutHook struct {
formatter logrus.Formatter
}
func newColoredStdOutHook() *coloredStdOutHook {
return &coloredStdOutHook{
formatter: &logrus.TextFormatter{
ForceColors: true,
FullTimestamp: true,
TimestampFormat: time.StampMilli,
},
}
}
func (cs *coloredStdOutHook) Levels() []logrus.Level {
return []logrus.Level{
logrus.PanicLevel,
logrus.FatalLevel,
logrus.ErrorLevel,
logrus.WarnLevel,
}
}
func (cs *coloredStdOutHook) Fire(entry *logrus.Entry) error {
bytes, err := cs.formatter.Format(entry)
if err != nil {
return err
}
if _, err := os.Stdout.Write(bytes); err != nil {
return err
}
return nil
}
func Init(logsPath, level string) error {
logrus.SetFormatter(&logrus.TextFormatter{
DisableColors: true,
FullTimestamp: true,
TimestampFormat: time.StampMilli,
})
logrus.AddHook(newColoredStdOutHook())
rotator, err := NewRotator(MaxLogSize, func() (io.WriteCloser, error) {
// Leaving MaxLogs-1 since new log file will be opened right away.
if err := clearLogs(logsPath, MaxLogs-1, MaxLogs); err != nil {
return nil, err
}
return os.Create(filepath.Join(logsPath, getLogName(constants.Version, constants.Revision))) //nolint:gosec // G304
})
if err != nil {
return err
}
logrus.SetOutput(rotator)
return setLevel(level)
}
// setLevel will change the level of logging and in case of Debug or Trace
// level it will also prevent from writing to file. Setting level to Info or
// higher will not set writing to file again if it was previously cancelled by
// Debug or Trace.
func setLevel(level string) error {
if level == "" {
return nil
}
logLevel, err := logrus.ParseLevel(level)
if err != nil {
return err
}
logrus.SetLevel(logLevel)
// The hook to print panic, fatal and error to stderr is always
// added. We want to avoid log duplicates by replacing all hooks.
if logrus.GetLevel() == logrus.TraceLevel {
_ = logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{})
logrus.SetOutput(os.Stderr)
logrus.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
TimestampFormat: time.StampMilli,
})
}
return nil
}
func getLogName(version, revision string) string {
return fmt.Sprintf("v%v_%v_%v.log", version, revision, time.Now().Unix())
}
func MatchLogName(name string) bool {
return regexp.MustCompile(`^v.*\.log$`).MatchString(name)
}
func MatchGUILogName(name string) bool {
return regexp.MustCompile(`^gui_v.*\.log$`).MatchString(name)
}