github.com/jiasir/deis@v1.12.2/logger/storage/file/adapter.go (about)

     1  package file
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"path"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  )
    12  
    13  type adapter struct {
    14  	logRoot string
    15  	files   map[string]*os.File
    16  	mutex   sync.Mutex
    17  }
    18  
    19  // NewStorageAdapter returns a pointer to a new instance of a file-based storage.Adapter.
    20  func NewStorageAdapter(logRoot string) (*adapter, error) {
    21  	src, err := os.Stat(logRoot)
    22  	if err != nil {
    23  		return nil, fmt.Errorf("Directory %s does not exist", logRoot)
    24  	}
    25  	if !src.IsDir() {
    26  		return nil, fmt.Errorf("%s is not a directory", logRoot)
    27  	}
    28  	return &adapter{logRoot: logRoot, files: make(map[string]*os.File)}, nil
    29  }
    30  
    31  // Write adds a log message to to an app-specific log file
    32  func (a *adapter) Write(app string, message string) error {
    33  	// Check first if we might actually have to add to the map of file pointers so we can avoid
    34  	// waiting for / obtaining a lock unnecessarily
    35  	f, ok := a.files[app]
    36  	if !ok {
    37  		// Ensure only one goroutine at a time can be adding a file pointer to the map of file
    38  		// pointers
    39  		a.mutex.Lock()
    40  		defer a.mutex.Unlock()
    41  		f, ok = a.files[app]
    42  		if !ok {
    43  			var err error
    44  			f, err = a.getFile(app)
    45  			if err != nil {
    46  				return err
    47  			}
    48  			a.files[app] = f
    49  		}
    50  	}
    51  	if _, err := f.WriteString(message + "\n"); err != nil {
    52  		return err
    53  	}
    54  	return nil
    55  }
    56  
    57  // Read retrieves a specified number of log lines from an app-specific log file
    58  func (a *adapter) Read(app string, lines int) ([]string, error) {
    59  	if lines <= 0 {
    60  		return []string{}, nil
    61  	}
    62  	filePath := a.getFilePath(app)
    63  	exists, err := fileExists(filePath)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	if !exists {
    68  		return nil, fmt.Errorf("Could not find logs for '%s'", app)
    69  	}
    70  	logBytes, err := exec.Command("tail", "-n", strconv.Itoa(lines), filePath).Output()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	logStrs := strings.Split(string(logBytes), "\n")
    75  	return logStrs[:len(logStrs)-1], nil
    76  }
    77  
    78  // Destroy deletes stored logs for the specified application
    79  func (a *adapter) Destroy(app string) error {
    80  	// Check first if the map of file pointers even contains the file pointer we want so we can avoid
    81  	// waiting for / obtaining a lock unnecessarily
    82  	f, ok := a.files[app]
    83  	if ok {
    84  		// Ensure no other goroutine is trying to modify the file pointer map while we're trying to
    85  		// clean up
    86  		a.mutex.Lock()
    87  		defer a.mutex.Unlock()
    88  		exists, err := fileExists(f.Name())
    89  		if err != nil {
    90  			return err
    91  		}
    92  		if exists {
    93  			if err := os.Remove(f.Name()); err != nil {
    94  				return err
    95  			}
    96  		}
    97  		delete(a.files, app)
    98  	}
    99  	return nil
   100  }
   101  
   102  func (a *adapter) Reopen() error {
   103  	// Ensure no other goroutine is trying to add a file pointer to the map of file pointers while
   104  	// we're trying to clear it out
   105  	a.mutex.Lock()
   106  	defer a.mutex.Unlock()
   107  	a.files = make(map[string]*os.File)
   108  	return nil
   109  }
   110  
   111  func (a *adapter) getFile(app string) (*os.File, error) {
   112  	filePath := a.getFilePath(app)
   113  	exists, err := fileExists(filePath)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	// return a new file or the existing file for appending
   118  	var file *os.File
   119  	if exists {
   120  		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0644)
   121  	} else {
   122  		file, err = os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644)
   123  	}
   124  	return file, err
   125  }
   126  
   127  func (a *adapter) getFilePath(app string) string {
   128  	return path.Join(a.logRoot, app+".log")
   129  }
   130  
   131  func fileExists(path string) (bool, error) {
   132  	_, err := os.Stat(path)
   133  	if err == nil {
   134  		return true, nil
   135  	}
   136  	if os.IsNotExist(err) {
   137  		return false, nil
   138  	}
   139  	return false, err
   140  }