213 lines
5.5 KiB
Go
213 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/user"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
|
|
"github.com/kkyr/fig"
|
|
log "github.com/sirupsen/logrus"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
// Configurations relating to HTTP server.
|
|
type HTTPConfig struct {
|
|
BindAddr string `fig:"bind_addr"`
|
|
Port uint `fig:"port"`
|
|
Debug bool `fig:"debug"`
|
|
APIKey string `fig:"api_key"`
|
|
Enabled bool `fig:"enabled"`
|
|
}
|
|
|
|
// Configuration for logging.
|
|
type LogConfig struct {
|
|
// Limit the log output by the log level.
|
|
Level string `fig:"level" yaml:"level" enum:"debug,info,warn,error" default:"info"`
|
|
// How should the log output be formatted.
|
|
Type string `fig:"type" yaml:"type" enum:"json,console" default:"console"`
|
|
// The outputs that the log should go to. Output of `console` will
|
|
// go to the stderr. An file path, will log to the file. Using `default-file`
|
|
// it'll either save to `/var/log/name.log`, or to the same directory as the
|
|
// executable if the path is not writable, or on Windows.
|
|
Outputs []string `fig:"outputs" yaml:"outputs" default:"[console,default-file]"`
|
|
// Maximum size of the log file in megabytes before it gets rotated.
|
|
MaxSize int `fig:"max_size" yaml:"max_size" default:"1"`
|
|
// Maximum number of backups to save.
|
|
MaxBackups int `fig:"max_backups" yaml:"max_backups" default:"3"`
|
|
// Maximum number of days to retain old log files.
|
|
MaxAge int `fig:"max_age" yaml:"max_age" default:"0"`
|
|
// Use the logal system time instead of UTC for file names of rotated backups.
|
|
LocalTime *bool `fig:"local_time" yaml:"local_time" default:"true"`
|
|
// Should the rotated logs be compressed.
|
|
Compress *bool `fig:"compress" yaml:"compress" default:"true"`
|
|
}
|
|
|
|
// Apply log config.
|
|
func (l *LogConfig) Apply() {
|
|
// Apply level.
|
|
switch l.Level {
|
|
case "debug":
|
|
log.SetLevel(log.DebugLevel)
|
|
case "info":
|
|
log.SetLevel(log.InfoLevel)
|
|
case "warn":
|
|
log.SetLevel(log.WarnLevel)
|
|
default:
|
|
log.SetLevel(log.ErrorLevel)
|
|
}
|
|
|
|
// Apply type.
|
|
switch l.Type {
|
|
case "json":
|
|
log.SetFormatter(&log.JSONFormatter{})
|
|
default:
|
|
log.SetFormatter(&log.TextFormatter{})
|
|
}
|
|
|
|
// Change the outputs.
|
|
var outputs []io.Writer
|
|
for _, output := range l.Outputs {
|
|
// If output is console, add stderr and continue.
|
|
if output == "console" {
|
|
outputs = append(outputs, os.Stderr)
|
|
continue
|
|
}
|
|
|
|
// If default-file defined, find the default file.
|
|
if output == "default-file" {
|
|
var f *os.File
|
|
var err error
|
|
var logDir, logPath string
|
|
logName := fmt.Sprintf("%s.log", serviceName)
|
|
|
|
// On *nix, `/var/log/` should be default if writable.
|
|
if runtime.GOOS != "windows" {
|
|
logDir = "/var/log"
|
|
logPath = filepath.Join(logDir, logName)
|
|
|
|
// Verify we can write to log file.
|
|
f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
}
|
|
|
|
// If we could not open the file, then we should try the executable path.
|
|
if err != nil || f == nil {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
log.Println("Unable to find an writable log path to save log to.")
|
|
continue
|
|
} else {
|
|
logDir = filepath.Dir(exe)
|
|
logPath = filepath.Join(logDir, logName)
|
|
|
|
// Verify we can write to log file.
|
|
f, err = os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
|
|
if err != nil {
|
|
log.Println("Unable to find an writable log path to save log to.")
|
|
continue
|
|
} else {
|
|
f.Close()
|
|
}
|
|
}
|
|
} else {
|
|
// Close file.
|
|
f.Close()
|
|
}
|
|
|
|
// Update the config log path.
|
|
output = logPath
|
|
}
|
|
|
|
// Setup lumberjack log rotate for the output, and add to the list.
|
|
logFile := &lumberjack.Logger{
|
|
Filename: output,
|
|
MaxSize: l.MaxSize,
|
|
MaxBackups: l.MaxBackups,
|
|
MaxAge: l.MaxAge,
|
|
LocalTime: *l.LocalTime,
|
|
Compress: *l.Compress,
|
|
}
|
|
outputs = append(outputs, logFile)
|
|
}
|
|
|
|
// If there are outputs, set the outputs.
|
|
if len(outputs) != 0 {
|
|
mw := io.MultiWriter(outputs...)
|
|
log.SetOutput(mw)
|
|
}
|
|
}
|
|
|
|
// Configuration Structure.
|
|
type Config struct {
|
|
HTTP HTTPConfig `fig:"http"`
|
|
Log *LogConfig `fig:"log" yaml:"log"`
|
|
MidiRouters []*MidiRouter `fig:"midi_routers"`
|
|
}
|
|
|
|
// Load the configuration.
|
|
func (a *App) ReadConfig() {
|
|
usr, err := user.Current()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Configuration paths.
|
|
localConfig, _ := filepath.Abs("./config.yaml")
|
|
homeDirConfig := usr.HomeDir + "/.config/midi-request-trigger/config.yaml"
|
|
etcConfig := "/etc/midi-request-trigger/config.yaml"
|
|
|
|
// Determine which configuration to use.
|
|
var configFile string
|
|
if _, err := os.Stat(app.flags.ConfigPath); err == nil && app.flags.ConfigPath != "" {
|
|
configFile = app.flags.ConfigPath
|
|
} else if _, err := os.Stat(localConfig); err == nil {
|
|
configFile = localConfig
|
|
} else if _, err := os.Stat(homeDirConfig); err == nil {
|
|
configFile = homeDirConfig
|
|
} else if _, err := os.Stat(etcConfig); err == nil {
|
|
configFile = etcConfig
|
|
} else {
|
|
log.Println("Unable to find a configuration file.")
|
|
}
|
|
|
|
// Load the configuration file.
|
|
config := &Config{
|
|
HTTP: HTTPConfig{
|
|
BindAddr: "",
|
|
Port: 34936,
|
|
Debug: true,
|
|
Enabled: false,
|
|
},
|
|
Log: &LogConfig{},
|
|
}
|
|
|
|
// Load configuration.
|
|
filePath, fileName := path.Split(configFile)
|
|
err = fig.Load(config,
|
|
fig.File(fileName),
|
|
fig.Dirs(filePath),
|
|
)
|
|
if err != nil {
|
|
app.config = config
|
|
log.Printf("Error parsing configuration: %s\n", err)
|
|
return
|
|
}
|
|
|
|
// Flag Overrides.
|
|
if app.flags.HTTPBind != "" {
|
|
config.HTTP.BindAddr = app.flags.HTTPBind
|
|
}
|
|
if app.flags.HTTPPort != 0 {
|
|
config.HTTP.Port = app.flags.HTTPPort
|
|
}
|
|
|
|
// Apply log configs.
|
|
config.Log.Apply()
|
|
|
|
// Set global config structure.
|
|
app.config = config
|
|
}
|