diff --git a/internal/app/app.go b/internal/app/app.go index 9dec2848..9331f041 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,29 +1,20 @@ package app import ( - "errors" "flag" "fmt" "os" "os/exec" - "path/filepath" "runtime" "runtime/debug" - "strings" - - "github.com/AlexxIT/go2rtc/pkg/shell" - "github.com/AlexxIT/go2rtc/pkg/yaml" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) -var Version = "1.9.2" -var UserAgent = "go2rtc/" + Version - -var ConfigPath string -var Info = map[string]any{ - "version": Version, -} +var ( + Version string + UserAgent string + ConfigPath string + Info = make(map[string]any) +) const usage = `Usage of go2rtc: @@ -33,12 +24,12 @@ const usage = `Usage of go2rtc: ` func Init() { - var confs Config + var config flagConfig var daemon bool var version bool - flag.Var(&confs, "config", "") - flag.Var(&confs, "c", "") + flag.Var(&config, "config", "") + flag.Var(&config, "c", "") flag.BoolVar(&daemon, "daemon", false, "") flag.BoolVar(&daemon, "d", false, "") flag.BoolVar(&version, "version", false, "") @@ -69,118 +60,30 @@ func Init() { // Re-run the program in background and exit cmd := exec.Command(os.Args[0], args...) if err := cmd.Start(); err != nil { - log.Fatal().Err(err).Send() + fmt.Println(err) + os.Exit(1) } fmt.Println("Running in daemon mode with PID:", cmd.Process.Pid) os.Exit(0) } - if confs == nil { - confs = []string{"go2rtc.yaml"} - } - - for _, conf := range confs { - if len(conf) == 0 { - continue - } - if conf[0] == '{' { - // config as raw YAML or JSON - configs = append(configs, []byte(conf)) - } else if data := parseConfString(conf); data != nil { - configs = append(configs, data) - } else { - // config as file - if ConfigPath == "" { - ConfigPath = conf - } - - if data, _ = os.ReadFile(conf); data == nil { - continue - } - - data = []byte(shell.ReplaceEnvVars(string(data))) - configs = append(configs, data) - } - } - - if ConfigPath != "" { - if !filepath.IsAbs(ConfigPath) { - if cwd, err := os.Getwd(); err == nil { - ConfigPath = filepath.Join(cwd, ConfigPath) - } - } - Info["config_path"] = ConfigPath - } + UserAgent = "go2rtc/" + Version + Info["version"] = Version Info["revision"] = revision - var cfg struct { - Mod map[string]string `yaml:"log"` - } - - cfg.Mod = map[string]string{ - "format": "", // useless, but anyway - "level": "info", - "output": "stdout", // TODO: change to stderr someday - "time": zerolog.TimeFormatUnixMs, - } - - LoadConfig(&cfg) - - log.Logger = NewLogger(cfg.Mod) - - modules = cfg.Mod + initConfig(config) + initLogger() platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) - log.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc") - log.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build") + Logger.Info().Str("version", Version).Str("platform", platform).Str("revision", revision).Msg("go2rtc") + Logger.Debug().Str("version", runtime.Version()).Str("vcs.time", vcsTime).Msg("build") if ConfigPath != "" { - log.Info().Str("path", ConfigPath).Msg("config") - } - - migrateStore() -} - -func LoadConfig(v any) { - for _, data := range configs { - if err := yaml.Unmarshal(data, v); err != nil { - log.Warn().Err(err).Msg("[app] read config") - } + Logger.Info().Str("path", ConfigPath).Msg("config") } } -func PatchConfig(key string, value any, path ...string) error { - if ConfigPath == "" { - return errors.New("config file disabled") - } - - // empty config is OK - b, _ := os.ReadFile(ConfigPath) - - b, err := yaml.Patch(b, key, value, path...) - if err != nil { - return err - } - - return os.WriteFile(ConfigPath, b, 0644) -} - -// internal - -type Config []string - -func (c *Config) String() string { - return strings.Join(*c, " ") -} - -func (c *Config) Set(value string) error { - *c = append(*c, value) - return nil -} - -var configs [][]byte - func readRevisionTime() (revision, vcsTime string) { if info, ok := debug.ReadBuildInfo(); ok { for _, setting := range info.Settings { @@ -202,25 +105,3 @@ func readRevisionTime() (revision, vcsTime string) { } return } - -func parseConfString(s string) []byte { - i := strings.IndexByte(s, '=') - if i < 0 { - return nil - } - - items := strings.Split(s[:i], ".") - if len(items) < 2 { - return nil - } - - // `log.level=trace` => `{log: {level: trace}}` - var pre string - var suf = s[i+1:] - for _, item := range items { - pre += "{" + item + ": " - suf += "}" - } - - return []byte(pre + suf) -} diff --git a/internal/app/config.go b/internal/app/config.go new file mode 100644 index 00000000..8ae6d460 --- /dev/null +++ b/internal/app/config.go @@ -0,0 +1,109 @@ +package app + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "github.com/AlexxIT/go2rtc/pkg/shell" + "github.com/AlexxIT/go2rtc/pkg/yaml" +) + +func LoadConfig(v any) { + for _, data := range configs { + if err := yaml.Unmarshal(data, v); err != nil { + Logger.Warn().Err(err).Send() + } + } +} + +func PatchConfig(key string, value any, path ...string) error { + if ConfigPath == "" { + return errors.New("config file disabled") + } + + // empty config is OK + b, _ := os.ReadFile(ConfigPath) + + b, err := yaml.Patch(b, key, value, path...) + if err != nil { + return err + } + + return os.WriteFile(ConfigPath, b, 0644) +} + +type flagConfig []string + +func (c *flagConfig) String() string { + return strings.Join(*c, " ") +} + +func (c *flagConfig) Set(value string) error { + *c = append(*c, value) + return nil +} + +var configs [][]byte + +func initConfig(confs flagConfig) { + if confs == nil { + confs = []string{"go2rtc.yaml"} + } + + for _, conf := range confs { + if len(conf) == 0 { + continue + } + if conf[0] == '{' { + // config as raw YAML or JSON + configs = append(configs, []byte(conf)) + } else if data := parseConfString(conf); data != nil { + configs = append(configs, data) + } else { + // config as file + if ConfigPath == "" { + ConfigPath = conf + } + + if data, _ = os.ReadFile(conf); data == nil { + continue + } + + data = []byte(shell.ReplaceEnvVars(string(data))) + configs = append(configs, data) + } + } + + if ConfigPath != "" { + if !filepath.IsAbs(ConfigPath) { + if cwd, err := os.Getwd(); err == nil { + ConfigPath = filepath.Join(cwd, ConfigPath) + } + } + Info["config_path"] = ConfigPath + } +} + +func parseConfString(s string) []byte { + i := strings.IndexByte(s, '=') + if i < 0 { + return nil + } + + items := strings.Split(s[:i], ".") + if len(items) < 2 { + return nil + } + + // `log.level=trace` => `{log: {level: trace}}` + var pre string + var suf = s[i+1:] + for _, item := range items { + pre += "{" + item + ": " + suf += "}" + } + + return []byte(pre + suf) +} diff --git a/internal/app/log.go b/internal/app/log.go index 222f6f2b..094dfbbf 100644 --- a/internal/app/log.go +++ b/internal/app/log.go @@ -6,30 +6,49 @@ import ( "github.com/mattn/go-isatty" "github.com/rs/zerolog" - "github.com/rs/zerolog/log" ) var MemoryLog = newBuffer(16) -// NewLogger support: +func GetLogger(module string) zerolog.Logger { + if s, ok := modules[module]; ok { + lvl, err := zerolog.ParseLevel(s) + if err == nil { + return Logger.Level(lvl) + } + Logger.Warn().Err(err).Caller().Send() + } + + return Logger +} + +// initLogger support: // - output: empty (only to memory), stderr, stdout // - format: empty (autodetect color support), color, json, text // - time: empty (disable timestamp), UNIXMS, UNIXMICRO, UNIXNANO // - level: disabled, trace, debug, info, warn, error... -func NewLogger(config map[string]string) zerolog.Logger { +func initLogger() { + var cfg struct { + Mod map[string]string `yaml:"log"` + } + + cfg.Mod = modules // defaults + + LoadConfig(&cfg) + var writer io.Writer - switch config["output"] { + switch modules["output"] { case "stderr": writer = os.Stderr case "stdout": writer = os.Stdout } - timeFormat := config["time"] + timeFormat := modules["time"] if writer != nil { - if format := config["format"]; format != "json" { + if format := modules["format"]; format != "json" { console := &zerolog.ConsoleWriter{Out: writer} switch format { @@ -61,31 +80,24 @@ func NewLogger(config map[string]string) zerolog.Logger { writer = MemoryLog } - logger := zerolog.New(writer) + lvl, _ := zerolog.ParseLevel(modules["level"]) + Logger = zerolog.New(writer).Level(lvl) if timeFormat != "" { zerolog.TimeFieldFormat = timeFormat - logger = logger.With().Timestamp().Logger() + Logger = Logger.With().Timestamp().Logger() } - - lvl, _ := zerolog.ParseLevel(config["level"]) - return logger.Level(lvl) } -func GetLogger(module string) zerolog.Logger { - if s, ok := modules[module]; ok { - lvl, err := zerolog.ParseLevel(s) - if err == nil { - return log.Level(lvl) - } - log.Warn().Err(err).Caller().Send() - } - - return log.Logger -} +var Logger zerolog.Logger // modules log levels -var modules map[string]string +var modules = map[string]string{ + "format": "", // useless, but anyway + "level": "info", + "output": "stdout", // TODO: change to stderr someday + "time": zerolog.TimeFormatUnixMs, +} const chunkSize = 1 << 16 diff --git a/internal/app/migrate.go b/internal/app/migrate.go deleted file mode 100644 index 95c51c51..00000000 --- a/internal/app/migrate.go +++ /dev/null @@ -1,35 +0,0 @@ -package app - -import ( - "encoding/json" - "os" - - "github.com/rs/zerolog/log" -) - -func migrateStore() { - const name = "go2rtc.json" - - data, _ := os.ReadFile(name) - if data == nil { - return - } - - var store struct { - Streams map[string]string `json:"streams"` - } - - if err := json.Unmarshal(data, &store); err != nil { - log.Warn().Err(err).Caller().Send() - return - } - - for id, url := range store.Streams { - if err := PatchConfig(id, url, "streams"); err != nil { - log.Warn().Err(err).Caller().Send() - return - } - } - - _ = os.Remove(name) -} diff --git a/internal/dvrip/dvrip.go b/internal/dvrip/dvrip.go index 470e8afd..095372d2 100644 --- a/internal/dvrip/dvrip.go +++ b/internal/dvrip/dvrip.go @@ -12,7 +12,6 @@ import ( "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/dvrip" - "github.com/rs/zerolog/log" ) func Init() { @@ -92,10 +91,7 @@ func sendBroadcasts(conn *net.UDPConn) { for i := 0; i < 3; i++ { time.Sleep(100 * time.Millisecond) - - if _, err = conn.WriteToUDP(data, addr); err != nil { - log.Err(err).Caller().Send() - } + _, _ = conn.WriteToUDP(data, addr) } } diff --git a/internal/ffmpeg/ffmpeg.go b/internal/ffmpeg/ffmpeg.go index aeba85fb..062e5aaf 100644 --- a/internal/ffmpeg/ffmpeg.go +++ b/internal/ffmpeg/ffmpeg.go @@ -14,6 +14,7 @@ import ( "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/ffmpeg" + "github.com/rs/zerolog" ) func Init() { @@ -29,6 +30,8 @@ func Init() { app.LoadConfig(&cfg) + log = app.GetLogger("ffmpeg") + // zerolog levels: trace debug info warn error fatal panic disabled // FFmpeg levels: trace debug verbose info warning error fatal panic quiet if cfg.Log.Level == "warn" { @@ -145,6 +148,8 @@ var defaults = map[string]string{ "h265/videotoolbox": "-c:v hevc_videotoolbox -g 50 -bf 0 -profile:v main -level:v 5.1", } +var log zerolog.Logger + // configTemplate - return template from config (defaults) if exist or return raw template func configTemplate(template string) string { if s := defaults[template]; s != "" { diff --git a/internal/ffmpeg/hardware/hardware.go b/internal/ffmpeg/hardware/hardware.go index ebbdc4fa..39ce3323 100644 --- a/internal/ffmpeg/hardware/hardware.go +++ b/internal/ffmpeg/hardware/hardware.go @@ -7,8 +7,6 @@ import ( "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/pkg/ffmpeg" - - "github.com/rs/zerolog/log" ) const ( @@ -152,7 +150,6 @@ var cache = map[string]string{} func run(bin string, args string) bool { err := exec.Command(bin, strings.Split(args, " ")...).Run() - log.Printf("%v %v", args, err) return err == nil } diff --git a/internal/ffmpeg/version.go b/internal/ffmpeg/version.go index 976c92d0..717e08a4 100644 --- a/internal/ffmpeg/version.go +++ b/internal/ffmpeg/version.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/AlexxIT/go2rtc/pkg/ffmpeg" - "github.com/rs/zerolog/log" ) var verMu sync.Mutex diff --git a/internal/mjpeg/init.go b/internal/mjpeg/init.go index ea65e2d7..0bed95c6 100644 --- a/internal/mjpeg/init.go +++ b/internal/mjpeg/init.go @@ -10,6 +10,7 @@ import ( "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/api/ws" + "github.com/AlexxIT/go2rtc/internal/app" "github.com/AlexxIT/go2rtc/internal/ffmpeg" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/ascii" @@ -18,7 +19,7 @@ import ( "github.com/AlexxIT/go2rtc/pkg/mjpeg" "github.com/AlexxIT/go2rtc/pkg/tcp" "github.com/AlexxIT/go2rtc/pkg/y4m" - "github.com/rs/zerolog/log" + "github.com/rs/zerolog" ) func Init() { @@ -28,8 +29,12 @@ func Init() { api.HandleFunc("api/stream.y4m", apiStreamY4M) ws.HandleFunc("mjpeg", handlerWS) + + log = app.GetLogger("mjpeg") } +var log zerolog.Logger + func handlerKeyframe(w http.ResponseWriter, r *http.Request) { src := r.URL.Query().Get("src") stream := streams.Get(src) diff --git a/internal/mpegts/aac.go b/internal/mpegts/aac.go index 3008a658..867dc971 100644 --- a/internal/mpegts/aac.go +++ b/internal/mpegts/aac.go @@ -7,7 +7,6 @@ import ( "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/aac" "github.com/AlexxIT/go2rtc/pkg/tcp" - "github.com/rs/zerolog/log" ) func apiStreamAAC(w http.ResponseWriter, r *http.Request) { @@ -23,7 +22,6 @@ func apiStreamAAC(w http.ResponseWriter, r *http.Request) { cons.UserAgent = r.UserAgent() if err := stream.AddConsumer(cons); err != nil { - log.Error().Err(err).Caller().Send() http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/internal/mpegts/mpegts.go b/internal/mpegts/mpegts.go index 6f4f6ab2..6ef00ba1 100644 --- a/internal/mpegts/mpegts.go +++ b/internal/mpegts/mpegts.go @@ -7,7 +7,6 @@ import ( "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/mpegts" "github.com/AlexxIT/go2rtc/pkg/tcp" - "github.com/rs/zerolog/log" ) func Init() { @@ -36,7 +35,6 @@ func outputMpegTS(w http.ResponseWriter, r *http.Request) { cons.UserAgent = r.UserAgent() if err := stream.AddConsumer(cons); err != nil { - log.Error().Err(err).Caller().Send() http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/main.go b/main.go index 91bc9938..27490f6a 100644 --- a/main.go +++ b/main.go @@ -36,6 +36,8 @@ import ( ) func main() { + app.Version = "1.9.2" + // 1. Core modules: app, api/ws, streams app.Init() // init config and logs diff --git a/pkg/opus/opus.go b/pkg/opus/.opus.go similarity index 97% rename from pkg/opus/opus.go rename to pkg/opus/.opus.go index 9fe1d8b6..42043977 100644 --- a/pkg/opus/opus.go +++ b/pkg/opus/.opus.go @@ -5,7 +5,6 @@ import ( "github.com/AlexxIT/go2rtc/pkg/core" "github.com/pion/rtp" - "github.com/rs/zerolog/log" ) func Log(handler core.HandlerFunc) core.HandlerFunc { diff --git a/pkg/roborock/client.go b/pkg/roborock/client.go index 39caab88..6a3bf0e0 100644 --- a/pkg/roborock/client.go +++ b/pkg/roborock/client.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "net/rpc" "net/url" "strconv" @@ -138,7 +137,7 @@ func (c *Client) Connect() error { } offer := pc.LocalDescription() - log.Printf("[roborock] offer\n%s", offer.SDP) + //log.Printf("[roborock] offer\n%s", offer.SDP) if err = c.SendSDPtoRobot(offer); err != nil { return err } @@ -151,7 +150,7 @@ func (c *Client) Connect() error { time.Sleep(time.Second) if desc, _ := c.GetDeviceSDP(); desc != nil { - log.Printf("[roborock] answer\n%s", desc.SDP) + //log.Printf("[roborock] answer\n%s", desc.SDP) if err = c.conn.SetAnswer(desc.SDP); err != nil { return err } diff --git a/pkg/roborock/iot/client.go b/pkg/roborock/iot/client.go index 8773455d..c3b2d97f 100644 --- a/pkg/roborock/iot/client.go +++ b/pkg/roborock/iot/client.go @@ -6,12 +6,12 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/AlexxIT/go2rtc/pkg/mqtt" - "github.com/rs/zerolog/log" "net" "net/rpc" "net/url" "time" + + "github.com/AlexxIT/go2rtc/pkg/mqtt" ) type Codec struct { @@ -56,7 +56,7 @@ func (c *Codec) WriteRequest(r *rpc.Request, v any) error { return err } - log.Printf("[roborock] send: %s", payload) + //log.Printf("[roborock] send: %s", payload) payload = c.Encrypt(payload, ts, ts, ts) @@ -86,7 +86,7 @@ func (c *Codec) ReadResponseHeader(r *rpc.Response) error { continue } - log.Printf("[roborock] recv %s", payload) + //log.Printf("[roborock] recv %s", payload) // get content from response payload: // {"t":1676871268,"dps":{"102":"{\"id\":315003,\"result\":[\"ok\"]}"}}