package db
import (
"archive/zip"
"bufio"
"bytes"
"crypto/sha256"
"fmt"
"io"
"os"
"os/exec"
"path"
"strconv"
"time"
"github.com/goodrain/rainbond/util"
"github.com/sirupsen/logrus"
)
type ByteUnit int64
const (
B ByteUnit = 1
KB = 1000 * B
MB = 1000 * KB
)
type filePlugin struct {
homePath string
}
func (m *filePlugin) getStdFilePath(serviceID string) (string, error) {
apath := path.Join(m.homePath, GetServiceAliasID(serviceID))
_, err := os.Stat(apath)
if err != nil {
if os.IsNotExist(err) {
err := os.MkdirAll(apath, 0755)
if err != nil {
return "", err
}
} else {
return "", err
}
}
return apath, nil
}
func (m *filePlugin) SaveMessage(events []*EventLogMessage) error {
logrus.Info("init file plugin save message")
if len(events) == 0 {
return nil
}
logMaxSize := 10 * MB
if os.Getenv("LOG_MAX_SIZE") != "" {
if size, err := strconv.Atoi(os.Getenv("LOG_MAX_SIZE")); err == nil {
logMaxSize = ByteUnit(size) * MB
}
}
key := events[0].EventID
var logfile *os.File
filePathDir, err := m.getStdFilePath(key)
if err != nil {
return err
}
stdoutLogPath := path.Join(filePathDir, "stdout.log")
logFile, err := os.Stat(stdoutLogPath)
if err != nil {
if os.IsNotExist(err) {
logfile, err = os.Create(stdoutLogPath)
if err != nil {
return err
}
defer logfile.Close()
} else {
return err
}
} else {
if logFile.ModTime().Day() != time.Now().Day() {
err := MvLogFile(fmt.Sprintf("%s/%d-%d-%d.log.gz", filePathDir, logFile.ModTime().Year(), logFile.ModTime().Month(), logFile.ModTime().Day()), stdoutLogPath)
if err != nil {
return err
}
}
}
if logfile == nil {
logfile, err = os.OpenFile(stdoutLogPath, os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return err
}
if logfile != nil {
defer logfile.Close()
}
} else {
if logfile != nil {
defer logfile.Close()
}
}
var content [][]byte
for _, e := range events {
content = append(content, e.Content)
}
body := bytes.Join(content, []byte("\n"))
body = append(body, []byte("\n")...)
if logFile != nil && logFile.Size() > int64(logMaxSize) {
legacyLogPath := path.Join(filePathDir, "stdout-legacy.log")
err = os.Rename(stdoutLogPath, legacyLogPath)
if err != nil {
logrus.Errorf("[Savemessage]: Rename %v to %v failed %v", stdoutLogPath, legacyLogPath, err)
return err
}
if logfile != nil {
logfile.Close()
}
logfile, err = os.OpenFile(stdoutLogPath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
return err
}
logrus.Debugf("[SaveMessage]: Old log file size %v, Write content size %v", logFile.Size(), len(body))
_, err = logfile.Write(body)
return err
}
_, err = logfile.Write(body)
return err
}
func (m *filePlugin) GetMessages(serviceID, level string, length int) (interface{}, error) {
if length <= 0 {
return nil, nil
}
filePathDir, err := m.getStdFilePath(serviceID)
if err != nil {
return nil, err
}
filePath := path.Join(filePathDir, "stdout.log")
if ok, err := util.FileExists(filePath); !ok {
if err != nil {
logrus.Errorf("check file exist error %s", err.Error())
}
return nil, nil
}
f, err := exec.Command("tail", "-n", fmt.Sprintf("%d", length), filePath).Output()
if err != nil {
return nil, err
}
reader := bufio.NewReader(bytes.NewBuffer(f))
var lines []string
for {
line, _, err := reader.ReadLine()
if err != nil {
break
}
if len(line) == 0 {
continue
}
lines = append(lines, string(line))
}
return lines, nil
}
func (m *filePlugin) Close() error {
return nil
}
func GetServiceAliasID(ServiceID string) string {
if len(ServiceID) > 11 {
newWord := strconv.Itoa(int(ServiceID[10])) + ServiceID + strconv.Itoa(int(ServiceID[3])) + "log" + strconv.Itoa(int(ServiceID[2])/7)
ha := sha256.New224()
ha.Write([]byte(newWord))
return fmt.Sprintf("%x", ha.Sum(nil))[0:16]
}
return ServiceID
}
func MvLogFile(newName string, filePath string) error {
info, err := os.Stat(filePath)
if err != nil {
return err
}
reader, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
if err != nil {
return err
}
f, err := os.OpenFile(newName, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return err
}
defer f.Close()
zw := zip.NewWriter(f)
defer zw.Close()
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
writer, err := zw.CreateHeader(header)
if err != nil {
return err
}
_, err = io.Copy(writer, reader)
if err != nil {
return err
}
err = os.Remove(filePath)
if err != nil {
return err
}
new, err := os.Create(filePath)
if err != nil {
return err
}
defer new.Close()
return nil
}