2019-04-04 19:57:28 +08:00
|
|
|
package logs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
2019-04-19 22:36:52 +08:00
|
|
|
"sync"
|
2019-04-04 19:57:28 +08:00
|
|
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
var (
|
2019-04-23 22:02:31 +08:00
|
|
|
configureMutex = sync.Mutex{}
|
2019-04-19 22:36:52 +08:00
|
|
|
// loggingConfigured will be set once logging has been configured via invoking `ConfigureLogging`.
|
|
|
|
// Subsequent invocations of `ConfigureLogging` would be no-op
|
|
|
|
loggingConfigured = false
|
|
|
|
)
|
2019-04-04 19:57:28 +08:00
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
type Config struct {
|
|
|
|
LogLevel logrus.Level
|
2019-04-04 19:57:28 +08:00
|
|
|
LogFormat string
|
|
|
|
LogFilePath string
|
|
|
|
LogPipeFd string
|
|
|
|
}
|
|
|
|
|
|
|
|
func ForwardLogs(logPipe io.Reader) {
|
|
|
|
lineReader := bufio.NewReader(logPipe)
|
|
|
|
for {
|
|
|
|
line, err := lineReader.ReadBytes('\n')
|
|
|
|
if len(line) > 0 {
|
|
|
|
processEntry(line)
|
|
|
|
}
|
|
|
|
if err == io.EOF {
|
|
|
|
logrus.Debugf("log pipe has been closed: %+v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("log pipe read error: %+v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func processEntry(text []byte) {
|
|
|
|
type jsonLog struct {
|
|
|
|
Level string `json:"level"`
|
|
|
|
Msg string `json:"msg"`
|
|
|
|
}
|
|
|
|
|
|
|
|
var jl jsonLog
|
|
|
|
if err := json.Unmarshal(text, &jl); err != nil {
|
|
|
|
logrus.Errorf("failed to decode %q to json: %+v", text, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
lvl, err := logrus.ParseLevel(jl.Level)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("failed to parse log level %q: %v\n", jl.Level, err)
|
|
|
|
return
|
|
|
|
}
|
2019-04-22 20:57:35 +08:00
|
|
|
logrus.StandardLogger().Logf(lvl, jl.Msg)
|
2019-04-04 19:57:28 +08:00
|
|
|
}
|
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
func ConfigureLogging(config Config) error {
|
2019-04-23 22:02:31 +08:00
|
|
|
configureMutex.Lock()
|
|
|
|
defer configureMutex.Unlock()
|
2019-04-19 22:36:52 +08:00
|
|
|
|
2019-04-04 19:57:28 +08:00
|
|
|
if loggingConfigured {
|
2019-04-19 22:36:52 +08:00
|
|
|
logrus.Debug("logging has already been configured")
|
2019-04-04 19:57:28 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
logrus.SetLevel(config.LogLevel)
|
2019-04-04 19:57:28 +08:00
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
if config.LogPipeFd != "" {
|
|
|
|
logPipeFdInt, err := strconv.Atoi(config.LogPipeFd)
|
2019-04-04 19:57:28 +08:00
|
|
|
if err != nil {
|
2019-04-19 22:36:52 +08:00
|
|
|
return fmt.Errorf("failed to convert _LIBCONTAINER_LOGPIPE environment variable value %q to int: %v", config.LogPipeFd, err)
|
2019-04-04 19:57:28 +08:00
|
|
|
}
|
2019-04-19 22:36:52 +08:00
|
|
|
logrus.SetOutput(os.NewFile(uintptr(logPipeFdInt), "logpipe"))
|
|
|
|
} else if config.LogFilePath != "" {
|
|
|
|
f, err := os.OpenFile(config.LogFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0644)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logrus.SetOutput(f)
|
2019-04-04 19:57:28 +08:00
|
|
|
}
|
|
|
|
|
2019-04-19 22:36:52 +08:00
|
|
|
switch config.LogFormat {
|
2019-04-04 19:57:28 +08:00
|
|
|
case "text":
|
|
|
|
// retain logrus's default.
|
|
|
|
case "json":
|
|
|
|
logrus.SetFormatter(new(logrus.JSONFormatter))
|
|
|
|
default:
|
2019-04-19 22:36:52 +08:00
|
|
|
return fmt.Errorf("unknown log-format %q", config.LogFormat)
|
2019-04-04 19:57:28 +08:00
|
|
|
}
|
2019-04-19 22:36:52 +08:00
|
|
|
|
|
|
|
loggingConfigured = true
|
2019-04-04 19:57:28 +08:00
|
|
|
return nil
|
|
|
|
}
|