gopkg.in/dotcloud/docker.v1@v1.13.1/daemon/logger/loggerutils/rotatefilewriter.go (about)

     1  package loggerutils
     2  
     3  import (
     4  	"os"
     5  	"strconv"
     6  	"sync"
     7  
     8  	"github.com/docker/docker/pkg/pubsub"
     9  )
    10  
    11  // RotateFileWriter is Logger implementation for default Docker logging.
    12  type RotateFileWriter struct {
    13  	f            *os.File // store for closing
    14  	mu           sync.Mutex
    15  	capacity     int64 //maximum size of each file
    16  	currentSize  int64 // current size of the latest file
    17  	maxFiles     int   //maximum number of files
    18  	notifyRotate *pubsub.Publisher
    19  }
    20  
    21  //NewRotateFileWriter creates new RotateFileWriter
    22  func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
    23  	log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	size, err := log.Seek(0, os.SEEK_END)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	return &RotateFileWriter{
    34  		f:            log,
    35  		capacity:     capacity,
    36  		currentSize:  size,
    37  		maxFiles:     maxFiles,
    38  		notifyRotate: pubsub.NewPublisher(0, 1),
    39  	}, nil
    40  }
    41  
    42  //WriteLog write log message to File
    43  func (w *RotateFileWriter) Write(message []byte) (int, error) {
    44  	w.mu.Lock()
    45  	if err := w.checkCapacityAndRotate(); err != nil {
    46  		w.mu.Unlock()
    47  		return -1, err
    48  	}
    49  
    50  	n, err := w.f.Write(message)
    51  	if err == nil {
    52  		w.currentSize += int64(n)
    53  	}
    54  	w.mu.Unlock()
    55  	return n, err
    56  }
    57  
    58  func (w *RotateFileWriter) checkCapacityAndRotate() error {
    59  	if w.capacity == -1 {
    60  		return nil
    61  	}
    62  
    63  	if w.currentSize >= w.capacity {
    64  		name := w.f.Name()
    65  		if err := w.f.Close(); err != nil {
    66  			return err
    67  		}
    68  		if err := rotate(name, w.maxFiles); err != nil {
    69  			return err
    70  		}
    71  		file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
    72  		if err != nil {
    73  			return err
    74  		}
    75  		w.f = file
    76  		w.currentSize = 0
    77  		w.notifyRotate.Publish(struct{}{})
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func rotate(name string, maxFiles int) error {
    84  	if maxFiles < 2 {
    85  		return nil
    86  	}
    87  	for i := maxFiles - 1; i > 1; i-- {
    88  		toPath := name + "." + strconv.Itoa(i)
    89  		fromPath := name + "." + strconv.Itoa(i-1)
    90  		if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
    91  			return err
    92  		}
    93  	}
    94  
    95  	if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
    96  		return err
    97  	}
    98  	return nil
    99  }
   100  
   101  // LogPath returns the location the given writer logs to.
   102  func (w *RotateFileWriter) LogPath() string {
   103  	return w.f.Name()
   104  }
   105  
   106  // MaxFiles return maximum number of files
   107  func (w *RotateFileWriter) MaxFiles() int {
   108  	return w.maxFiles
   109  }
   110  
   111  //NotifyRotate returns the new subscriber
   112  func (w *RotateFileWriter) NotifyRotate() chan interface{} {
   113  	return w.notifyRotate.Subscribe()
   114  }
   115  
   116  //NotifyRotateEvict removes the specified subscriber from receiving any more messages.
   117  func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
   118  	w.notifyRotate.Evict(sub)
   119  }
   120  
   121  // Close closes underlying file and signals all readers to stop.
   122  func (w *RotateFileWriter) Close() error {
   123  	return w.f.Close()
   124  }