github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/logconfig/rollingwriter/rollingwriter.go (about)

     1  /*
     2   * Copyright (C) 2019 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package rollingwriter
    19  
    20  import (
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"path"
    25  	"sort"
    26  	"strings"
    27  
    28  	"github.com/arthurkiller/rollingwriter"
    29  	"github.com/rs/zerolog/log"
    30  )
    31  
    32  // RollingWriter represents logs writer with logs rolling and cleanup support.
    33  type RollingWriter struct {
    34  	config rollingwriter.Config
    35  	Writer io.Writer
    36  }
    37  
    38  // NewRollingWriter creates new rolling writer.
    39  func NewRollingWriter(filepath string) (writer *RollingWriter, err error) {
    40  	writer = &RollingWriter{}
    41  	writer.config = rollingwriter.Config{
    42  		TimeTagFormat:     "20060102T150405",
    43  		LogPath:           path.Dir(filepath),
    44  		FileName:          path.Base(filepath),
    45  		RollingPolicy:     rollingwriter.VolumeRolling,
    46  		RollingVolumeSize: "50MB",
    47  		Compress:          true,
    48  		WriterMode:        "lock",
    49  		MaxRemain:         5,
    50  	}
    51  	writer.Writer, err = rollingwriter.NewWriterFromConfig(&writer.config)
    52  	return writer, err
    53  }
    54  
    55  // Write writes to underlying rolling writer.
    56  func (w *RollingWriter) Write(b []byte) (int, error) {
    57  	return w.Writer.Write(b)
    58  }
    59  
    60  // CleanObsoleteLogs cleans obsolete logs so that the count of remaining log files is equal to w.config.MaxRemain.
    61  // rollingWriter only handles file rolling at runtime, but if the node is restarted, the count is lost thus we have
    62  // to do this manually.
    63  func (w *RollingWriter) CleanObsoleteLogs() error {
    64  	files, err := os.ReadDir(w.config.LogPath)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	var oldLogFiles []os.FileInfo
    69  	baseFilename := w.config.FileName + ".log"
    70  	for _, file := range files {
    71  		if strings.Contains(file.Name(), baseFilename) && file.Name() != baseFilename {
    72  			fInfo, err := file.Info()
    73  			if err != nil {
    74  				return fmt.Errorf("failed to get file info: %w", err)
    75  			}
    76  			oldLogFiles = append(oldLogFiles, fInfo)
    77  		}
    78  	}
    79  	if len(oldLogFiles) <= w.config.MaxRemain {
    80  		log.Debug().Msgf("Found %d old log files in log directory, skipping cleanup", len(oldLogFiles))
    81  		return nil
    82  	}
    83  	log.Debug().Msgf("Found %d old log files in log directory, proceeding to cleanup", len(oldLogFiles))
    84  	sort.Slice(oldLogFiles, func(i, j int) bool {
    85  		return oldLogFiles[i].ModTime().After(oldLogFiles[j].ModTime())
    86  	})
    87  	for i := w.config.MaxRemain; i < len(oldLogFiles); i++ {
    88  		fp := path.Join(w.config.LogPath, oldLogFiles[i].Name())
    89  		if err := os.Remove(fp); err != nil {
    90  			log.Warn().Err(err).Msg("Failed to remove log file: " + fp)
    91  		}
    92  	}
    93  	return nil
    94  }