github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/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  	maxFiles     int   //maximum number of files
    17  	notifyRotate *pubsub.Publisher
    18  }
    19  
    20  //NewRotateFileWriter creates new RotateFileWriter
    21  func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
    22  	log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
    23  	if err != nil {
    24  		return &RotateFileWriter{}, err
    25  	}
    26  
    27  	return &RotateFileWriter{
    28  		f:            log,
    29  		capacity:     capacity,
    30  		maxFiles:     maxFiles,
    31  		notifyRotate: pubsub.NewPublisher(0, 1),
    32  	}, nil
    33  }
    34  
    35  //WriteLog write log message to File
    36  func (w *RotateFileWriter) Write(message []byte) (int, error) {
    37  	w.mu.Lock()
    38  	defer w.mu.Unlock()
    39  	if err := w.checkCapacityAndRotate(); err != nil {
    40  		return -1, err
    41  	}
    42  
    43  	return w.f.Write(message)
    44  }
    45  
    46  func (w *RotateFileWriter) checkCapacityAndRotate() error {
    47  	if w.capacity == -1 {
    48  		return nil
    49  	}
    50  
    51  	meta, err := w.f.Stat()
    52  	if err != nil {
    53  		return err
    54  	}
    55  
    56  	if meta.Size() >= w.capacity {
    57  		name := w.f.Name()
    58  		if err := w.f.Close(); err != nil {
    59  			return err
    60  		}
    61  		if err := rotate(name, w.maxFiles); err != nil {
    62  			return err
    63  		}
    64  		file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
    65  		if err != nil {
    66  			return err
    67  		}
    68  		w.f = file
    69  		w.notifyRotate.Publish(struct{}{})
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func rotate(name string, maxFiles int) error {
    76  	if maxFiles < 2 {
    77  		return nil
    78  	}
    79  	for i := maxFiles - 1; i > 1; i-- {
    80  		toPath := name + "." + strconv.Itoa(i)
    81  		fromPath := name + "." + strconv.Itoa(i-1)
    82  		if err := backup(fromPath, toPath); err != nil && !os.IsNotExist(err) {
    83  			return err
    84  		}
    85  	}
    86  
    87  	if err := backup(name, name+".1"); err != nil {
    88  		return err
    89  	}
    90  	return nil
    91  }
    92  
    93  // backup renames a file from fromPath to toPath
    94  func backup(fromPath, toPath string) error {
    95  	if _, err := os.Stat(fromPath); os.IsNotExist(err) {
    96  		return err
    97  	}
    98  
    99  	if _, err := os.Stat(toPath); !os.IsNotExist(err) {
   100  		err := os.Remove(toPath)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	return os.Rename(fromPath, toPath)
   107  }
   108  
   109  // LogPath returns the location the given writer logs to.
   110  func (w *RotateFileWriter) LogPath() string {
   111  	return w.f.Name()
   112  }
   113  
   114  // MaxFiles return maximum number of files
   115  func (w *RotateFileWriter) MaxFiles() int {
   116  	return w.maxFiles
   117  }
   118  
   119  //NotifyRotate returns the new subscriber
   120  func (w *RotateFileWriter) NotifyRotate() chan interface{} {
   121  	return w.notifyRotate.Subscribe()
   122  }
   123  
   124  //NotifyRotateEvict removes the specified subscriber from receiving any more messages.
   125  func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
   126  	w.notifyRotate.Evict(sub)
   127  }
   128  
   129  // Close closes underlying file and signals all readers to stop.
   130  func (w *RotateFileWriter) Close() error {
   131  	return w.f.Close()
   132  }