github.com/criteo-forks/consul@v1.4.5-criteonogrpc/logger/logfile.go (about)

     1  package logger
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  var (
    13  	now = time.Now
    14  )
    15  
    16  //LogFile is used to setup a file based logger that also performs log rotation
    17  type LogFile struct {
    18  	//Name of the log file
    19  	fileName string
    20  
    21  	//Path to the log file
    22  	logPath string
    23  
    24  	//Duration between each file rotation operation
    25  	duration time.Duration
    26  
    27  	//LastCreated represents the creation time of the latest log
    28  	LastCreated time.Time
    29  
    30  	//FileInfo is the pointer to the current file being written to
    31  	FileInfo *os.File
    32  
    33  	//MaxBytes is the maximum number of desired bytes for a log file
    34  	MaxBytes int
    35  
    36  	//BytesWritten is the number of bytes written in the current log file
    37  	BytesWritten int64
    38  
    39  	//acquire is the mutex utilized to ensure we have no concurrency issues
    40  	acquire sync.Mutex
    41  }
    42  
    43  func (l *LogFile) openNew() error {
    44  	// Extract the file extension
    45  	fileExt := filepath.Ext(l.fileName)
    46  	// If we have no file extension we append .log
    47  	if fileExt == "" {
    48  		fileExt = ".log"
    49  	}
    50  	// Remove the file extension from the filename
    51  	fileName := strings.TrimSuffix(l.fileName, fileExt)
    52  	// New file name has the format : filename-timestamp.extension
    53  	createTime := now()
    54  	newfileName := fileName + "-" + strconv.FormatInt(createTime.UnixNano(), 10) + fileExt
    55  	newfilePath := filepath.Join(l.logPath, newfileName)
    56  	// Try creating a file. We truncate the file because we are the only authority to write the logs
    57  	filePointer, err := os.OpenFile(newfilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	l.FileInfo = filePointer
    62  	// New file, new bytes tracker, new creation time :)
    63  	l.LastCreated = createTime
    64  	l.BytesWritten = 0
    65  	return nil
    66  }
    67  
    68  func (l *LogFile) rotate() error {
    69  	// Get the time from the last point of contact
    70  	timeElapsed := time.Since(l.LastCreated)
    71  	// Rotate if we hit the byte file limit or the time limit
    72  	if (l.BytesWritten >= int64(l.MaxBytes) && (l.MaxBytes > 0)) || timeElapsed >= l.duration {
    73  		l.FileInfo.Close()
    74  		return l.openNew()
    75  	}
    76  	return nil
    77  }
    78  
    79  func (l *LogFile) Write(b []byte) (n int, err error) {
    80  	l.acquire.Lock()
    81  	defer l.acquire.Unlock()
    82  	//Create a new file if we have no file to write to
    83  	if l.FileInfo == nil {
    84  		if err := l.openNew(); err != nil {
    85  			return 0, err
    86  		}
    87  	}
    88  	// Check for the last contact and rotate if necessary
    89  	if err := l.rotate(); err != nil {
    90  		return 0, err
    91  	}
    92  	l.BytesWritten += int64(len(b))
    93  	return l.FileInfo.Write(b)
    94  }