diff --git a/.gitignore b/.gitignore index 44bcd13..48df202 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +# Log files +*.log + # GoCode debug file debug diff --git a/Makefile b/Makefile index 8eda388..c51bb8b 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,22 @@ -.PHONY: fmt vet get clean +TAGS= + +.PHONY: fmt vet get clean dev setdev all: fmt vet MovieNight MovieNight.exe static/main.wasm +setdev: + $(eval export TAGS=-tags "dev") + +dev: setdev all + MovieNight.exe: *.go common/*.go - GOOS=windows GOARCH=amd64 go build -o MovieNight.exe + GOOS=windows GOARCH=amd64 go build -o MovieNight.exe $(TAGS) MovieNight: *.go common/*.go - GOOS=linux GOARCH=386 go build -o MovieNight + GOOS=linux GOARCH=386 go build -o MovieNight $(TAGS) static/main.wasm: wasm/*.go common/*.go - GOOS=js GOARCH=wasm go build -o ./static/main.wasm wasm/*.go + GOOS=js GOARCH=wasm go build -o ./static/main.wasm $(TAGS) wasm/*.go clean: -rm MovieNight.exe MovieNight ./static/main.wasm @@ -23,5 +30,5 @@ get: go get golang.org/x/tools/cmd/goimports vet: - go vet ./... - GOOS=js GOARCH=wasm go vet ./... + go vet $(TAGS) ./... + GOOS=js GOARCH=wasm go vet $(TAGS) ./... diff --git a/chatclient.go b/chatclient.go index de07ff3..f0751cb 100644 --- a/chatclient.go +++ b/chatclient.go @@ -24,13 +24,13 @@ type Client struct { func (cl *Client) NewMsg(data common.ClientData) { switch data.Type { case common.CdAuth: - fmt.Printf("[chat|hidden] <%s> get auth level\n", cl.name) + LogChatf("[chat|hidden] <%s> get auth level\n", cl.name) err := cl.SendChatData(common.NewChatHiddenMessage(data.Type, cl.CmdLevel)) if err != nil { - fmt.Printf("Error sending auth level to client: %v\n", err) + LogErrorf("Error sending auth level to client: %v\n", err) } case common.CdUsers: - fmt.Printf("[chat|hidden] <%s> get list of users\n", cl.name) + common.LogChatf("[chat|hidden] <%s> get list of users\n", cl.name) names := chat.GetNames() idx := -1 @@ -42,7 +42,7 @@ func (cl *Client) NewMsg(data common.ClientData) { err := cl.SendChatData(common.NewChatHiddenMessage(data.Type, append(names[:idx], names[idx+1:]...))) if err != nil { - fmt.Printf("Error sending users to client: %v\n", err) + common.LogErrorf("Error sending chat data: %v\n", err) } case common.CdMessage: msg := html.EscapeString(data.Message) @@ -68,7 +68,7 @@ func (cl *Client) NewMsg(data common.ClientData) { common.CmdlUser, common.MsgCommandResponse)) if err != nil { - fmt.Printf("Error command results %v\n", err) + common.LogErrorf("Error command results %v\n", err) } return } @@ -79,7 +79,7 @@ func (cl *Client) NewMsg(data common.ClientData) { msg = msg[0:400] } - fmt.Printf("[chat] <%s> %q\n", cl.name, msg) + common.LogChatf("[chat] <%s> %q\n", cl.name, msg) // Enable links for mods and admins if cl.CmdLevel >= common.CmdlMod { diff --git a/chatcommands.go b/chatcommands.go index 81c9bc0..db8b867 100644 --- a/chatcommands.go +++ b/chatcommands.go @@ -61,19 +61,19 @@ var commands = &CommandControl{ if settings.AdminPassword == pw { cl.CmdLevel = common.CmdlAdmin cl.belongsTo.AddModNotice(cl.name + " used the admin password") - fmt.Printf("[auth] %s used the admin password\n", cl.name) + common.LogInfof("[auth] %s used the admin password\n", cl.name) return "Admin rights granted." } if cl.belongsTo.redeemModPass(pw) { cl.CmdLevel = common.CmdlMod cl.belongsTo.AddModNotice(cl.name + " used a mod password") - fmt.Printf("[auth] %s used a mod password\n", cl.name) + common.LogInfof("[auth] %s used a mod password\n", cl.name) return "Moderator privileges granted." } cl.belongsTo.AddModNotice(cl.name + " attempted to auth without success") - fmt.Printf("[auth] %s gave an invalid password\n", cl.name) + common.LogInfof("[auth] %s gave an invalid password\n", cl.name) return "Invalid password." }, }, @@ -217,7 +217,7 @@ var commands = &CommandControl{ } name := strings.TrimLeft(args[0], "@") - fmt.Printf("[ban] Attempting to ban %s\n", name) + common.LogInfof("[ban] Attempting to ban %s\n", name) return cl.belongsTo.Ban(name) }, }, @@ -229,7 +229,7 @@ var commands = &CommandControl{ return "missing name to unban." } name := strings.TrimLeft(args[0], "@") - fmt.Printf("[ban] Attempting to unban %s\n", name) + common.LogInfof("[ban] Attempting to unban %s\n", name) err := settings.RemoveBan(name) if err != nil { @@ -243,7 +243,7 @@ var commands = &CommandControl{ common.CNPurge.String(): Command{ HelpText: "Purge the chat.", Function: func(cl *Client, args []string) string { - fmt.Println("[purge] clearing chat") + common.LogInfoln("[purge] clearing chat") cl.belongsTo.AddCmdMsg(common.CmdPurgeChat, nil) return "" }, @@ -282,12 +282,12 @@ var commands = &CommandControl{ cl.SendServerMessage("Reloading emotes") num, err := common.LoadEmotes() if err != nil { - fmt.Printf("Unbale to reload emotes: %s\n", err) + common.LogErrorf("Unbale to reload emotes: %s\n", err) return fmt.Sprintf("ERROR: %s", err) } cl.belongsTo.AddModNotice(cl.name + " has reloaded emotes") - fmt.Printf("Loaded %d emotes\n", num) + common.LogInfof("Loaded %d emotes\n", num) return fmt.Sprintf("Emotes loaded: %d", num) }, }, @@ -302,17 +302,17 @@ var commands = &CommandControl{ }, common.CNIP.String(): Command{ - HelpText: "list users and IP in the server console", + HelpText: "List users and IP in the server console. Requires logging level to be set to info or above.", Function: func(cl *Client, args []string) string { cl.belongsTo.clientsMtx.Lock() - fmt.Println("Clients:") + common.LogInfoln("Clients:") for uuid, client := range cl.belongsTo.clients { - fmt.Printf(" [%s] %s %s\n", uuid, client.name, client.conn.Host()) + common.LogInfof(" [%s] %s %s\n", uuid, client.name, client.conn.Host()) } - fmt.Println("TmpConn:") + common.LogInfoln("TmpConn:") for uuid, conn := range cl.belongsTo.tempConn { - fmt.Printf(" [%s] %s\n", uuid, conn.Host()) + common.LogInfof(" [%s] %s\n", uuid, conn.Host()) } cl.belongsTo.clientsMtx.Unlock() return "see console for output" @@ -327,33 +327,33 @@ func (cc *CommandControl) RunCommand(command string, args []string, sender *Clie // Look for user command if userCmd, ok := cc.user[cmd]; ok { - fmt.Printf("[user] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[user] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return userCmd.Function(sender, args) } // Look for mod command if modCmd, ok := cc.mod[cmd]; ok { if sender.CmdLevel >= common.CmdlMod { - fmt.Printf("[mod] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[mod] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return modCmd.Function(sender, args) } - fmt.Printf("[mod REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[mod REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return "You are not a mod Jebaited" } // Look for admin command if adminCmd, ok := cc.admin[cmd]; ok { if sender.CmdLevel == common.CmdlAdmin { - fmt.Printf("[admin] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[admin] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return adminCmd.Function(sender, args) } - fmt.Printf("[admin REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[admin REJECTED] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return "You are not the admin Jebaited" } // Command not found - fmt.Printf("[cmd] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) + common.LogInfof("[cmd] %s /%s %s\n", sender.name, command, strings.Join(args, " ")) return "Invalid command." } @@ -416,31 +416,31 @@ var cmdColor = Command{ if strings.HasPrefix(args[0], "@") { name = strings.TrimLeft(args[0], "@") color = args[1] - fmt.Println("Found explicit name: ", name) + common.LogDebugln("[color:mod] Found explicit name: ", name) } else if strings.HasPrefix(args[1], "@") { name = strings.TrimLeft(args[1], "@") color = args[0] - fmt.Println("Found explicit name: ", name) + common.LogDebugln("[color:mod] Found explicit name: ", name) // Check for explicit color } else if strings.HasPrefix(args[0], "#") { name = strings.TrimPrefix(args[1], "@") // this shouldn't be needed, but just in case. color = args[0] - fmt.Println("Found explicit color: ", color) + common.LogDebugln("[color:mod] Found explicit color: ", color) } else if strings.HasPrefix(args[1], "#") { name = strings.TrimPrefix(args[0], "@") // this shouldn't be needed, but just in case. color = args[1] - fmt.Println("Found explicit color: ", color) + common.LogDebugln("[color:mod] Found explicit color: ", color) // Guess } else if common.IsValidColor(args[0]) { name = strings.TrimPrefix(args[1], "@") color = args[0] - fmt.Println("Guessed name: ", name, " and color: ", color) + common.LogDebugln("[color:mod] Guessed name: ", name, " and color: ", color) } else if common.IsValidColor(args[1]) { name = strings.TrimPrefix(args[0], "@") color = args[1] - fmt.Println("Guessed name: ", name, " and color: ", color) + common.LogDebugln("[color:mod] Guessed name: ", name, " and color: ", color) } if name == "" { @@ -451,12 +451,12 @@ var cmdColor = Command{ } if color == "" { - fmt.Printf("[color:mod] %s missing color\n", cl.name) + common.LogInfof("[color:mod] %s missing color\n", cl.name) return "Missing color" } if name == "" { - fmt.Printf("[color:mod] %s missing name\n", cl.name) + common.LogInfof("[color:mod] %s missing name\n", cl.name) return "Missing name" } @@ -469,7 +469,7 @@ var cmdColor = Command{ // Don't allow an unprivilaged user to change their color if // it was changed by a mod if cl.IsColorForced { - fmt.Printf("[color] %s tried to change a forced color\n", cl.name) + common.LogInfof("[color] %s tried to change a forced color\n", cl.name) return "You are not allowed to change your color." } @@ -484,7 +484,7 @@ var cmdColor = Command{ } cl.color = args[0] - fmt.Printf("[color] %s new color: %s\n", cl.name, cl.color) + common.LogInfof("[color] %s new color: %s\n", cl.name, cl.color) return "Color changed successfully." }, } diff --git a/chatroom.go b/chatroom.go index 7d0b4cd..1177ae5 100644 --- a/chatroom.go +++ b/chatroom.go @@ -45,7 +45,7 @@ func newChatRoom() (*ChatRoom, error) { if err != nil { return nil, fmt.Errorf("error loading emotes: %s", err) } - fmt.Printf("Loaded %d emotes\n", num) + common.LogInfof("Loaded %d emotes\n", num) //the "heartbeat" for broadcasting messages go cr.Broadcast() @@ -113,10 +113,10 @@ func (cr *ChatRoom) Join(name, uid string) (*Client, error) { cr.clients[uid] = client delete(cr.tempConn, uid) - fmt.Printf("[join] %s %s\n", host, name) + common.LogChatf("[join] %s %s\n", host, name) playingCommand, err := common.NewChatCommand(common.CmdPlaying, []string{cr.playing, cr.playingLink}).ToJSON() if err != nil { - fmt.Printf("Unable to encode playing command on join: %s\n", err) + common.LogErrorf("Unable to encode playing command on join: %s\n", err) } else { client.Send(playingCommand) } @@ -132,7 +132,7 @@ func (cr *ChatRoom) Leave(name, color string) { client, suid, err := cr.getClient(name) if err != nil { - fmt.Printf("[leave] Unable to get client suid %v\n", err) + common.LogErrorf("[leave] Unable to get client suid %v\n", err) return } host := client.Host() @@ -141,7 +141,7 @@ func (cr *ChatRoom) Leave(name, color string) { cr.delClient(suid) cr.AddEventMsg(common.EvLeave, name, color) - fmt.Printf("[leave] %s %s\n", host, name) + common.LogChatf("[leave] %s %s\n", host, name) } // kicked from the chatroom @@ -168,7 +168,7 @@ func (cr *ChatRoom) Kick(name string) string { cr.delClient(suid) cr.AddEventMsg(common.EvKick, name, color) - fmt.Printf("[kick] %s %s has been kicked\n", host, name) + common.LogInfof("[kick] %s %s has been kicked\n", host, name) return "" } @@ -178,7 +178,7 @@ func (cr *ChatRoom) Ban(name string) string { client, suid, err := cr.getClient(name) if err != nil { - fmt.Printf("[ban] Unable to get client for name %q\n", name) + common.LogErrorf("[ban] Unable to get client for name %q\n", name) return "Cannot find that name" } @@ -206,7 +206,7 @@ func (cr *ChatRoom) Ban(name string) string { err = settings.AddBan(host, names) if err != nil { - fmt.Printf("[BAN] Error banning %q: %s\n", name, err) + common.LogErrorf("[BAN] Error banning %q: %s\n", name, err) cr.AddEventMsg(common.EvKick, name, color) } else { cr.AddEventMsg(common.EvBan, name, color) @@ -229,7 +229,7 @@ func (cr *ChatRoom) AddMsg(from *Client, isAction, isServer bool, msg string) { select { case cr.queue <- common.NewChatMessage(from.name, from.color, msg, from.CmdLevel, t): default: - fmt.Println("Unable to queue chat message. Channel full.") + common.LogErrorln("Unable to queue chat message. Channel full.") } } @@ -237,7 +237,7 @@ func (cr *ChatRoom) AddCmdMsg(command common.CommandType, args []string) { select { case cr.queue <- common.NewChatCommand(command, args): default: - fmt.Println("Unable to queue command message. Channel full.") + common.LogErrorln("Unable to queue command message. Channel full.") } } @@ -245,7 +245,7 @@ func (cr *ChatRoom) AddModNotice(message string) { select { case cr.modqueue <- common.NewChatMessage("", "", message, common.CmdlUser, common.MsgNotice): default: - fmt.Println("Unable to queue notice. Channel full.") + common.LogErrorln("Unable to queue notice. Channel full.") } } @@ -253,7 +253,7 @@ func (cr *ChatRoom) AddEventMsg(event common.EventType, name, color string) { select { case cr.queue <- common.NewChatEvent(event, name, color): default: - fmt.Println("Unable to queue event message. Channel full.") + common.LogErrorln("Unable to queue event message. Channel full.") } } @@ -310,7 +310,7 @@ func (cr *ChatRoom) Broadcast() { send := func(data common.ChatData, client *Client) { err := client.SendChatData(data) if err != nil { - fmt.Printf("Error sending data to client: %v\n", err) + common.LogErrorf("Error sending data to client: %v\n", err) } } @@ -333,7 +333,7 @@ func (cr *ChatRoom) Broadcast() { data, err := msg.ToJSON() if err != nil { - fmt.Printf("Error converting ChatData to ChatDataJSON: %v\n", err) + common.LogErrorf("Error converting ChatData to ChatDataJSON: %v\n", err) cr.clientsMtx.Unlock() break } @@ -342,7 +342,7 @@ func (cr *ChatRoom) Broadcast() { go func(c *chatConnection, suid string) { err = c.WriteData(data) if err != nil { - fmt.Printf("Error writing data to connection: %v\n", err) + common.LogErrorf("Error writing data to connection: %v\n", err) delete(cr.tempConn, suid) } }(conn, uuid) @@ -484,7 +484,7 @@ func (cr *ChatRoom) changeName(oldName, newName string, forced bool) error { if currentClient != nil { currentClient.name = newName - fmt.Printf("%q -> %q\n", oldName, newName) + common.LogDebugf("%q -> %q\n", oldName, newName) if forced { cr.AddEventMsg(common.EvNameChangeForced, oldName+":"+newName, currentClient.color) diff --git a/common/emotes.go b/common/emotes.go index f2ff30b..b330c7b 100644 --- a/common/emotes.go +++ b/common/emotes.go @@ -47,14 +47,15 @@ func LoadEmotes() (int, error) { globbed_files := []string(emotePNGs) globbed_files = append(globbed_files, emoteGIFs...) - fmt.Println("Loading emotes...") + LogInfoln("Loading emotes...") + emInfo := []string{} for _, file := range globbed_files { file = filepath.Base(file) key := file[0 : len(file)-4] newEmotes[key] = file - fmt.Printf("%s ", key) + emInfo = append(emInfo, key) } Emotes = newEmotes - fmt.Println("") + LogInfoln(strings.Join(emInfo, " ")) return len(Emotes), nil } diff --git a/common/logging.go b/common/logging.go new file mode 100644 index 0000000..8bbd5c1 --- /dev/null +++ b/common/logging.go @@ -0,0 +1,211 @@ +package common + +import ( + "fmt" + "io" + "log" + "os" +) + +var loglevel LogLevel + +type LogLevel string + +const ( + LLError LogLevel = "error" // only log errors + LLChat LogLevel = "chat" // log chat and commands + LLInfo LogLevel = "info" // log info messages (not quite debug, but not chat) + LLDebug LogLevel = "debug" // log everything +) + +const ( + logPrefixError string = "[ERROR] " + logPrefixChat string = "[CHAT] " + logPrefixInfo string = "[INFO] " + logPrefixDebug string = "[DEBUG] " + logPrefixDev string = "[DEV] " +) + +var ( + logError *log.Logger + logChat *log.Logger + logInfo *log.Logger + logDebug *log.Logger + logDev *log.Logger +) + +//func ParseLogLevel(input string) LogLevel { +// switch LogLevel(input) { +// case LLError, LLChat, LLInfo, LLDebug: +// return LogLevel(input) +// default: +// return LLError +// } +//} + +func SetupLogging(level LogLevel, file string) error { + switch level { + case LLDebug: + if file == "" { + logError = log.New(os.Stderr, logPrefixError, log.LstdFlags) + logChat = log.New(os.Stdout, logPrefixChat, log.LstdFlags) + logDebug = log.New(os.Stdout, logPrefixDebug, log.LstdFlags) + logInfo = log.New(os.Stdout, logPrefixInfo, log.LstdFlags) + } else { + f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("Unable to open log file for writing: %s", err) + } + logError = log.New(io.MultiWriter(os.Stderr, f), logPrefixError, log.LstdFlags) + logChat = log.New(io.MultiWriter(os.Stdout, f), logPrefixChat, log.LstdFlags) + logInfo = log.New(io.MultiWriter(os.Stdout, f), logPrefixInfo, log.LstdFlags) + logDebug = log.New(io.MultiWriter(os.Stdout, f), logPrefixDebug, log.LstdFlags) + } + case LLChat: + logDebug = nil + if file == "" { + logError = log.New(os.Stderr, logPrefixError, log.LstdFlags) + logChat = log.New(os.Stdout, logPrefixChat, log.LstdFlags) + logInfo = log.New(os.Stdout, logPrefixInfo, log.LstdFlags) + } else { + f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("Unable to open log file for writing: %s", err) + } + logError = log.New(io.MultiWriter(os.Stderr, f), logPrefixError, log.LstdFlags) + logChat = log.New(io.MultiWriter(os.Stdout, f), logPrefixChat, log.LstdFlags) + logInfo = log.New(io.MultiWriter(os.Stdout, f), logPrefixInfo, log.LstdFlags) + } + + case LLInfo: + logDebug = nil + logChat = nil + if file == "" { + logError = log.New(os.Stderr, logPrefixError, log.LstdFlags) + logInfo = log.New(os.Stdout, logPrefixInfo, log.LstdFlags) + } else { + f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("Unable to open log file for writing: %s", err) + } + logError = log.New(io.MultiWriter(os.Stderr, f), logPrefixError, log.LstdFlags) + logInfo = log.New(io.MultiWriter(os.Stdout, f), logPrefixInfo, log.LstdFlags) + } + + // Default to error + default: + logChat = nil + logDebug = nil + logInfo = nil + if file == "" { + logError = log.New(os.Stderr, logPrefixError, log.LstdFlags) + } else { + f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return fmt.Errorf("Unable to open log file for writing: %s", err) + } + logError = log.New(io.MultiWriter(os.Stderr, f), logPrefixError, log.LstdFlags) + } + } + return nil +} + +func LogErrorf(format string, v ...interface{}) { + if logError == nil { + panic("Logging not setup!") + } + + logError.Printf(format, v...) +} + +func LogErrorln(v ...interface{}) { + if logError == nil { + panic("Logging not setup!") + } + + logError.Println(v...) +} + +func LogChatf(format string, v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging chat and commands is turned off. + if logChat == nil { + return + } + + logChat.Printf(format, v...) +} + +func LogChatln(v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging chat and commands is turned off. + if logChat == nil { + return + } + + logChat.Println(v...) +} + +func LogInfof(format string, v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging info is turned off. + if logInfo == nil { + return + } + + logInfo.Printf(format, v...) +} + +func LogInfoln(v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging info is turned off. + if logInfo == nil { + return + } + + logInfo.Println(v...) +} + +func LogDebugf(format string, v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging debug is turned off. + if logDebug == nil { + return + } + + logDebug.Printf(format, v...) +} + +func LogDebugln(v ...interface{}) { + // if logError isn't set to something, logging wasn't setup. + if logError == nil { + panic("Logging not setup!") + } + + // logging debug is turned off. + if logDebug == nil { + return + } + + logDebug.Println(v...) +} diff --git a/common/logging_dev.go b/common/logging_dev.go new file mode 100644 index 0000000..32c9646 --- /dev/null +++ b/common/logging_dev.go @@ -0,0 +1,19 @@ +// +build dev + +package common + +func LogDevf(format string, v ...interface{}) { + if logError == nil { + panic("Logging not setup!") + } + + logError.Printf(format, v...) +} + +func LogDevln(v ...interface{}) { + if logError == nil { + panic("Logging not setup!") + } + + logError.Println(v...) +} diff --git a/connection.go b/connection.go index 715b412..f2f8cc7 100644 --- a/connection.go +++ b/connection.go @@ -6,6 +6,7 @@ import ( "sync" "github.com/gorilla/websocket" + "github.com/zorchenhimer/MovieNight/common" ) type chatConnection struct { @@ -31,7 +32,7 @@ func (cc *chatConnection) WriteData(data interface{}) error { err := cc.WriteJSON(data) if err != nil { if operr, ok := err.(*net.OpError); ok { - fmt.Println("OpError: " + operr.Err.Error()) + common.LogDebugln("OpError: " + operr.Err.Error()) } return fmt.Errorf("Error writing data to %s %s: %v", cc.clientName, cc.Host(), err) } diff --git a/handlers.go b/handlers.go index 4a3ebfb..710535a 100644 --- a/handlers.go +++ b/handlers.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "html/template" "io" "net/http" @@ -58,7 +57,7 @@ func wsStaticFiles(w http.ResponseWriter, r *http.Request) { } goodPath := r.URL.Path[8:len(r.URL.Path)] - fmt.Printf("[static] serving %q from folder ./static/\n", goodPath) + common.LogDebugf("[static] serving %q from folder ./static/\n", goodPath) http.ServeFile(w, r, "./static/"+goodPath) } @@ -70,7 +69,7 @@ func wsWasmFile(w http.ResponseWriter, r *http.Request) { func wsImages(w http.ResponseWriter, r *http.Request) { base := filepath.Base(r.URL.Path) - fmt.Println("[img] ", base) + common.LogDebugln("[img] ", base) http.ServeFile(w, r, "./static/img/"+base) } @@ -91,7 +90,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - fmt.Println("Error upgrading to websocket:", err) + common.LogErrorln("Error upgrading to websocket:", err) return } @@ -107,7 +106,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { uid, err := chat.JoinTemp(chatConn) if err != nil { - fmt.Printf("[handler] could not do a temp join, %v\n", err) + common.LogErrorf("[handler] could not do a temp join, %v\n", err) conn.Close() } @@ -117,7 +116,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { var data common.ClientData err := chatConn.ReadData(&data) if err != nil { - fmt.Printf("[handler] Client closed connection: %s\n", conn.RemoteAddr().String()) + common.LogInfof("[handler] Client closed connection: %s\n", conn.RemoteAddr().String()) conn.Close() return } @@ -126,14 +125,14 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { if err != nil { switch err.(type) { case UserFormatError, UserTakenError: - fmt.Printf("[handler|%s] %v\n", errorName(err), err) + common.LogInfof("[handler|%s] %v\n", errorName(err), err) case BannedUserError: - fmt.Printf("[handler|%s] %v\n", errorName(err), err) + common.LogInfof("[handler|%s] %v\n", errorName(err), err) // close connection since banned users shouldn't be connecting conn.Close() default: // for now all errors not caught need to be warned - fmt.Printf("[handler|uncaught] %v\n", err) + common.LogErrorf("[handler|uncaught] %v\n", err) conn.Close() } } @@ -156,7 +155,7 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { func handleHelpTemplate(w http.ResponseWriter, r *http.Request) { t, err := template.ParseFiles("./static/base.html", "./static/help.html") if err != nil { - fmt.Printf("Error parsing template file, %v\n", err) + common.LogErrorf("Error parsing template file, %v\n", err) return } @@ -182,14 +181,14 @@ func handleHelpTemplate(w http.ResponseWriter, r *http.Request) { err = t.Execute(w, data) if err != nil { - fmt.Printf("Error executing file, %v", err) + common.LogErrorf("Error executing file, %v", err) } } func handleIndexTemplate(w http.ResponseWriter, r *http.Request) { t, err := template.ParseFiles("./static/base.html", "./static/main.html") if err != nil { - fmt.Printf("Error parsing template file, %v\n", err) + common.LogErrorf("Error parsing template file, %v\n", err) return } @@ -220,7 +219,7 @@ func handleIndexTemplate(w http.ResponseWriter, r *http.Request) { err = t.Execute(w, data) if err != nil { - fmt.Printf("Error executing file, %v", err) + common.LogErrorf("Error executing file, %v", err) } } @@ -228,22 +227,22 @@ func handlePublish(conn *rtmp.Conn) { streams, _ := conn.Streams() l.Lock() - fmt.Println("request string->", conn.URL.RequestURI()) + common.LogDebugln("request string->", conn.URL.RequestURI()) urlParts := strings.Split(strings.Trim(conn.URL.RequestURI(), "/"), "/") - fmt.Println("urlParts->", urlParts) + common.LogDebugln("urlParts->", urlParts) if len(urlParts) > 2 { - fmt.Println("Extra garbage after stream key") + common.LogErrorln("Extra garbage after stream key") return } if len(urlParts) != 2 { - fmt.Println("Missing stream key") + common.LogErrorln("Missing stream key") return } if urlParts[1] != settings.GetStreamKey() { - fmt.Println("Due to key not match, denied stream") + common.LogErrorln("Stream key is incorrect. Denying stream.") return //If key not match, deny stream } @@ -259,13 +258,13 @@ func handlePublish(conn *rtmp.Conn) { } l.Unlock() if ch == nil { - fmt.Println("Unable to start stream, channel is nil.") + common.LogErrorln("Unable to start stream, channel is nil.") return } - fmt.Println("Stream started") + common.LogInfoln("Stream started") avutil.CopyPackets(ch.que, conn) - fmt.Println("Stream finished") + common.LogInfoln("Stream finished") l.Lock() delete(channels, streamPath) @@ -303,7 +302,8 @@ func handleDefault(w http.ResponseWriter, r *http.Request) { avutil.CopyFile(muxer, cursor) } else { if r.URL.Path != "/" { - fmt.Println("[http 404] ", r.URL.Path) + // not really an error for the server, but for the client. + common.LogInfoln("[http 404] ", r.URL.Path) http.NotFound(w, r) } else { handleIndexTemplate(w, r) diff --git a/main.go b/main.go index 517ff4f..d5e86e5 100644 --- a/main.go +++ b/main.go @@ -2,13 +2,13 @@ package main import ( "flag" - "fmt" "net/http" "os" "os/signal" "github.com/nareix/joy4/format" "github.com/nareix/joy4/format/rtmp" + "github.com/zorchenhimer/MovieNight/common" ) var ( @@ -35,7 +35,7 @@ func main() { // Load emotes before starting server. var err error if chat, err = newChatRoom(); err != nil { - fmt.Println(err) + common.LogErrorln(err) os.Exit(1) } @@ -43,15 +43,17 @@ func main() { addr = settings.ListenAddress } + common.LogDevln("This should break things") + // A stream key was passed on the command line. Use it, but don't save // it over the stream key in the settings.json file. if sKey != "" { settings.SetTempKey(sKey) } - fmt.Println("Stream key: ", settings.GetStreamKey()) - fmt.Println("Admin password: ", settings.AdminPassword) - fmt.Println("Listen and serve ", addr) + common.LogInfoln("Stream key: ", settings.GetStreamKey()) + common.LogInfoln("Admin password: ", settings.AdminPassword) + common.LogInfoln("Listen and serve ", addr) go startServer() go startRmtpServer() @@ -66,7 +68,8 @@ func startRmtpServer() { } err := server.ListenAndServe() if err != nil { - fmt.Printf("Error trying to start server: %v\n", err) + // If the server cannot start, don't pretend we can continue. + panic("Error trying to start rtmp server: " + err.Error()) } } @@ -87,7 +90,8 @@ func startServer() { err := http.ListenAndServe(addr, nil) if err != nil { - fmt.Printf("Error trying to start rmtp server: %v\n", err) + // If the server cannot start, don't pretend we can continue. + panic("Error trying to start chat/http server: " + err.Error()) } } @@ -95,7 +99,7 @@ func handleInterrupt(exit chan bool) { ch := make(chan os.Signal) signal.Notify(ch, os.Interrupt) <-ch - fmt.Println("Closing server") + common.LogInfoln("Closing server") if settings.StreamStats { stats.Print() } diff --git a/settings.go b/settings.go index 7fa4626..dc47eb5 100644 --- a/settings.go +++ b/settings.go @@ -9,6 +9,8 @@ import ( "strings" "sync" "time" + + "github.com/zorchenhimer/MovieNight/common" ) var settings *Settings @@ -27,6 +29,8 @@ type Settings struct { StreamKey string ListenAddress string Bans []BanInfo + LogLevel common.LogLevel + LogFile string } type BanInfo struct { @@ -45,8 +49,8 @@ func init() { panic("Missing stream key is settings.json") } - if settings.TitleLength <= 0 { - settings.TitleLength = 50 + if err = common.SetupLogging(settings.LogLevel, settings.LogFile); err != nil { + panic("Unable to setup logger: " + err.Error()) } // Save admin password to file @@ -79,8 +83,14 @@ func LoadSettings(filename string) (*Settings, error) { if err != nil { return nil, fmt.Errorf("unable to generate admin password: %s", err) } + + // Don't use LogInfof() here. Log isn't setup yet when LoadSettings() is called from init(). fmt.Printf("Settings reloaded. New admin password: %s\n", s.AdminPassword) + if s.TitleLength <= 0 { + s.TitleLength = 50 + } + return s, nil } @@ -122,7 +132,7 @@ func (s *Settings) AddBan(host string, names []string) error { } settings.Bans = append(settings.Bans, b) - fmt.Printf("[BAN] %q (%s) has been banned.\n", strings.Join(names, ", "), host) + common.LogInfof("[BAN] %q (%s) has been banned.\n", strings.Join(names, ", "), host) return settings.Save() } @@ -136,7 +146,7 @@ func (s *Settings) RemoveBan(name string) error { for _, b := range s.Bans { for _, n := range b.Names { if n == name { - fmt.Printf("[ban] Removed ban for %s [%s]\n", b.IP, n) + common.LogInfof("[ban] Removed ban for %s [%s]\n", b.IP, n) } else { newBans = append(newBans, b) }