github.com/tendermint/tmlibs@v0.9.0/autofile/autofile.go (about)

     1  package autofile
     2  
     3  import (
     4  	"os"
     5  	"sync"
     6  	"time"
     7  
     8  	cmn "github.com/tendermint/tmlibs/common"
     9  )
    10  
    11  /* AutoFile usage
    12  
    13  // Create/Append to ./autofile_test
    14  af, err := OpenAutoFile("autofile_test")
    15  if err != nil {
    16  	panic(err)
    17  }
    18  
    19  // Stream of writes.
    20  // During this time, the file may be moved e.g. by logRotate.
    21  for i := 0; i < 60; i++ {
    22  	af.Write([]byte(Fmt("LOOP(%v)", i)))
    23  	time.Sleep(time.Second)
    24  }
    25  
    26  // Close the AutoFile
    27  err = af.Close()
    28  if err != nil {
    29  	panic(err)
    30  }
    31  */
    32  
    33  const autoFileOpenDuration = 1000 * time.Millisecond
    34  
    35  // Automatically closes and re-opens file for writing.
    36  // This is useful for using a log file with the logrotate tool.
    37  type AutoFile struct {
    38  	ID     string
    39  	Path   string
    40  	ticker *time.Ticker
    41  	mtx    sync.Mutex
    42  	file   *os.File
    43  }
    44  
    45  func OpenAutoFile(path string) (af *AutoFile, err error) {
    46  	af = &AutoFile{
    47  		ID:     cmn.RandStr(12) + ":" + path,
    48  		Path:   path,
    49  		ticker: time.NewTicker(autoFileOpenDuration),
    50  	}
    51  	if err = af.openFile(); err != nil {
    52  		return
    53  	}
    54  	go af.processTicks()
    55  	sighupWatchers.addAutoFile(af)
    56  	return
    57  }
    58  
    59  func (af *AutoFile) Close() error {
    60  	af.ticker.Stop()
    61  	err := af.closeFile()
    62  	sighupWatchers.removeAutoFile(af)
    63  	return err
    64  }
    65  
    66  func (af *AutoFile) processTicks() {
    67  	for {
    68  		_, ok := <-af.ticker.C
    69  		if !ok {
    70  			return // Done.
    71  		}
    72  		af.closeFile()
    73  	}
    74  }
    75  
    76  func (af *AutoFile) closeFile() (err error) {
    77  	af.mtx.Lock()
    78  	defer af.mtx.Unlock()
    79  
    80  	file := af.file
    81  	if file == nil {
    82  		return nil
    83  	}
    84  	af.file = nil
    85  	return file.Close()
    86  }
    87  
    88  func (af *AutoFile) Write(b []byte) (n int, err error) {
    89  	af.mtx.Lock()
    90  	defer af.mtx.Unlock()
    91  
    92  	if af.file == nil {
    93  		if err = af.openFile(); err != nil {
    94  			return
    95  		}
    96  	}
    97  
    98  	n, err = af.file.Write(b)
    99  	return
   100  }
   101  
   102  func (af *AutoFile) Sync() error {
   103  	af.mtx.Lock()
   104  	defer af.mtx.Unlock()
   105  
   106  	if af.file == nil {
   107  		if err := af.openFile(); err != nil {
   108  			return err
   109  		}
   110  	}
   111  	return af.file.Sync()
   112  }
   113  
   114  func (af *AutoFile) openFile() error {
   115  	file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	af.file = file
   120  	return nil
   121  }
   122  
   123  func (af *AutoFile) Size() (int64, error) {
   124  	af.mtx.Lock()
   125  	defer af.mtx.Unlock()
   126  
   127  	if af.file == nil {
   128  		err := af.openFile()
   129  		if err != nil {
   130  			if err == os.ErrNotExist {
   131  				return 0, nil
   132  			}
   133  			return -1, err
   134  		}
   135  	}
   136  	stat, err := af.file.Stat()
   137  	if err != nil {
   138  		return -1, err
   139  	}
   140  	return stat.Size(), nil
   141  
   142  }