]> ruderich.org/simon Gitweb - safcm/safcm.git/commitdiff
safcm: move logEvent() to frontend package
authorSimon Ruderich <simon@ruderich.org>
Tue, 18 May 2021 16:51:47 +0000 (18:51 +0200)
committerSimon Ruderich <simon@ruderich.org>
Wed, 19 May 2021 05:54:14 +0000 (07:54 +0200)
cmd/safcm/sync.go
cmd/safcm/sync_test.go
frontend/log.go
frontend/log_test.go [new file with mode: 0644]

index 2f297c8dc69288aa4017f57d4e741562740784d9..65745af55a68367d1fd4a2625f44cf3901bdb6b2 100644 (file)
@@ -105,7 +105,7 @@ func MainSync(args []string) error {
        loop := &frontend.Loop{
                DebugConn: cfg.LogLevel >= safcm.LogDebug3,
                LogEventFunc: func(x frontend.Event, failed *bool) {
-                       logEvent(x, cfg.LogLevel, isTTY, failed)
+                       frontend.LogEvent(x, cfg.LogLevel, isTTY, failed)
                },
                SyncHostFunc: func(conn *rpc.Conn, host frontend.Host) error {
                        return host.(*Sync).Host(conn)
@@ -213,66 +213,6 @@ are only available after the hosts were contacted.
        return res, nil
 }
 
-func logEvent(x frontend.Event, level safcm.LogLevel, isTTY bool, failed *bool) {
-       // We have multiple event sources so this is somewhat ugly.
-       var prefix, data string
-       var color frontend.Color
-       if x.Error != nil {
-               prefix = "[error]"
-               data = x.Error.Error()
-               color = frontend.ColorRed
-               // We logged an error, tell the caller
-               *failed = true
-       } else if x.Log.Level != 0 {
-               if level < x.Log.Level {
-                       return
-               }
-               // LogError and LogDebug3 should not occur here
-               switch x.Log.Level {
-               case safcm.LogInfo:
-                       prefix = "[info]"
-               case safcm.LogVerbose:
-                       prefix = "[verbose]"
-               case safcm.LogDebug:
-                       prefix = "[debug]"
-               case safcm.LogDebug2:
-                       prefix = "[debug2]"
-               default:
-                       prefix = fmt.Sprintf("[INVALID=%d]", x.Log.Level)
-                       color = frontend.ColorRed
-               }
-               data = x.Log.Text
-       } else {
-               switch x.ConnEvent.Type {
-               case rpc.ConnEventStderr:
-                       prefix = "[stderr]"
-               case rpc.ConnEventDebug:
-                       prefix = "[debug3]"
-               case rpc.ConnEventUpload:
-                       if level < safcm.LogInfo {
-                               return
-                       }
-                       prefix = "[info]"
-                       x.ConnEvent.Data = "remote helper upload in progress"
-               default:
-                       prefix = fmt.Sprintf("[INVALID=%d]", x.ConnEvent.Type)
-                       color = frontend.ColorRed
-               }
-               data = x.ConnEvent.Data
-       }
-
-       host := x.Host.Name()
-       if color != 0 {
-               host = frontend.ColorString(isTTY, color, host)
-       }
-       // Make sure to escape control characters to prevent terminal
-       // injection attacks
-       if !x.Escaped {
-               data = frontend.EscapeControlCharacters(isTTY, data)
-       }
-       log.Printf("%-9s [%s] %s", prefix, host, data)
-}
-
 func (s *Sync) Name() string {
        return s.host.Name
 }
index 4744ad553cd45715bcb08313842ad2700d5af800..8cde5ab612e7d6aeaf967ee01943fea4ddc06c8f 100644 (file)
 package main
 
 import (
-       "bytes"
        "fmt"
-       "log"
        "os"
        "testing"
 
-       "ruderich.org/simon/safcm"
        "ruderich.org/simon/safcm/cmd/safcm/config"
-       "ruderich.org/simon/safcm/frontend"
-       "ruderich.org/simon/safcm/rpc"
        "ruderich.org/simon/safcm/testutil"
 )
 
@@ -195,356 +190,3 @@ are only available after the hosts were contacted.
                })
        }
 }
-
-func TestLogEvent(t *testing.T) {
-       // Restore default logger
-       defer log.SetFlags(log.Flags())
-       defer log.SetOutput(os.Stderr)
-
-       tests := []struct {
-               name      string
-               event     frontend.Event
-               level     safcm.LogLevel
-               isTTY     bool
-               exp       string
-               expFailed bool
-       }{
-
-               {
-                       "Error",
-                       frontend.Event{
-                               Error: fmt.Errorf("fake error"),
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[error]   [fake-host] fake error\n",
-                       true,
-               },
-               {
-                       "Error (tty)",
-                       frontend.Event{
-                               Error: fmt.Errorf("fake error"),
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[error]   [\x1b[31mfake-host\x1b[0m] fake error\n",
-                       true,
-               },
-               {
-                       "Error: escape",
-                       frontend.Event{
-                               Error: fmt.Errorf("\x00"),
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[error]   [fake-host] \\x00\n",
-                       true,
-               },
-               {
-                       "Error: escape (tty)",
-                       frontend.Event{
-                               Error: fmt.Errorf("\x00"),
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[error]   [\x1b[31mfake-host\x1b[0m] \x1b[35m\\x00\x1b[0m\n",
-                       true,
-               },
-
-               {
-                       "Log: info",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "info log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[info]    [fake-host] info log\n",
-                       false,
-               },
-               {
-                       "Log: info (tty)",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "info log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[info]    [fake-host] info log\n",
-                       false,
-               },
-               {
-                       "Log: verbose",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogVerbose,
-                                       Text:  "verbose log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[verbose] [fake-host] verbose log\n",
-                       false,
-               },
-               {
-                       "Log: debug",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogDebug,
-                                       Text:  "debug log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[debug]   [fake-host] debug log\n",
-                       false,
-               },
-               {
-                       "Log: debug2",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogDebug2,
-                                       Text:  "debug2 log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[debug2]  [fake-host] debug2 log\n",
-                       false,
-               },
-               {
-                       "Log: debug3",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogDebug3,
-                                       Text:  "debug3 log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n",
-                               safcm.LogDebug3),
-                       false,
-               },
-               {
-                       "Log: debug3 (tty)",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogDebug3,
-                                       Text:  "debug3 log",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n",
-                               safcm.LogDebug3),
-                       false,
-               },
-               {
-                       "Log: escape",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "\x00",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[info]    [fake-host] \\x00\n",
-                       false,
-               },
-               {
-                       "Log: escape (tty)",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "\x00",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[info]    [fake-host] \x1b[35m\\x00\x1b[0m\n",
-                       false,
-               },
-
-               {
-                       "ConnEvent: stderr",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventStderr,
-                                       Data: "fake stderr",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[stderr]  [fake-host] fake stderr\n",
-                       false,
-               },
-               {
-                       "ConnEvent: stderr (tty)",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventStderr,
-                                       Data: "fake stderr",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[stderr]  [fake-host] fake stderr\n",
-                       false,
-               },
-               {
-                       "ConnEvent: debug",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventDebug,
-                                       Data: "conn debug",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[debug3]  [fake-host] conn debug\n",
-                       false,
-               },
-               {
-                       "ConnEvent: upload",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventUpload,
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[info]    [fake-host] remote helper upload in progress\n",
-                       false,
-               },
-               {
-                       "ConnEvent: upload (ignored)",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventUpload,
-                               },
-                       },
-                       safcm.LogError,
-                       false,
-                       "",
-                       false,
-               },
-               {
-                       "ConnEvent: invalid",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: 42,
-                                       Data: "invalid",
-                               },
-                       },
-                       safcm.LogError,
-                       false,
-                       "[INVALID=42] [fake-host] invalid\n",
-                       false,
-               },
-               {
-                       "ConnEvent: invalid (tty)",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: 42,
-                                       Data: "invalid",
-                               },
-                       },
-                       safcm.LogError,
-                       true,
-                       "[INVALID=42] [\x1b[31mfake-host\x1b[0m] invalid\n",
-                       false,
-               },
-               {
-                       "ConnEvent: escape",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventStderr,
-                                       Data: "\x00",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[stderr]  [fake-host] \\x00\n",
-                       false,
-               },
-               {
-                       "ConnEvent: escape (tty)",
-                       frontend.Event{
-                               ConnEvent: rpc.ConnEvent{
-                                       Type: rpc.ConnEventDebug,
-                                       Data: "\x01",
-                               },
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[debug3]  [fake-host] \x1b[35m\\x01\x1b[0m\n",
-                       false,
-               },
-
-               {
-                       "Escaped",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "\x00",
-                               },
-                               Escaped: true,
-                       },
-                       safcm.LogDebug3,
-                       false,
-                       "[info]    [fake-host] \x00\n",
-                       false,
-               },
-               {
-                       "Escaped (tty)",
-                       frontend.Event{
-                               Log: frontend.Log{
-                                       Level: safcm.LogInfo,
-                                       Text:  "\x00",
-                               },
-                               Escaped: true,
-                       },
-                       safcm.LogDebug3,
-                       true,
-                       "[info]    [fake-host] \x00\n",
-                       false,
-               },
-
-               {
-                       "empty (invalid)",
-                       frontend.Event{},
-                       safcm.LogDebug3,
-                       false,
-                       "[INVALID=0] [fake-host] \n",
-                       false,
-               },
-       }
-
-       for _, tc := range tests {
-               t.Run(tc.name, func(t *testing.T) {
-                       tc.event.Host = &Sync{
-                               host: &config.Host{
-                                       Name: "fake-host",
-                               },
-                       }
-
-                       var buf bytes.Buffer
-                       log.SetFlags(0)
-                       log.SetOutput(&buf)
-
-                       var failed bool
-                       logEvent(tc.event, tc.level, tc.isTTY, &failed)
-
-                       testutil.AssertEqual(t, "log",
-                               buf.String(), tc.exp)
-                       testutil.AssertEqual(t, "failed",
-                               failed, tc.expFailed)
-               })
-       }
-}
index 33fa1d0e24e68e15211b489067dfb77dfc15b40e..aed57dddca4be9afde6f0c690fdc2bf0d0a970d8 100644 (file)
@@ -18,6 +18,9 @@
 package frontend
 
 import (
+       "fmt"
+       "log"
+
        "ruderich.org/simon/safcm"
        "ruderich.org/simon/safcm/rpc"
 )
@@ -50,3 +53,67 @@ func (l *Loop) Log(host Host, level safcm.LogLevel, escaped bool,
                Escaped: escaped,
        }
 }
+
+// LogEvent logs events using the log package. It can be used to implement
+// Loop.LogEventFunc. It's used by cmd/safcm to log events.
+func LogEvent(x Event, level safcm.LogLevel, isTTY bool,
+       failed *bool) {
+
+       // We have multiple event sources so this is somewhat ugly.
+       var prefix, data string
+       var color Color
+       if x.Error != nil {
+               prefix = "[error]"
+               data = x.Error.Error()
+               color = ColorRed
+               // We logged an error, tell the caller
+               *failed = true
+       } else if x.Log.Level != 0 {
+               if level < x.Log.Level {
+                       return
+               }
+               // LogError and LogDebug3 should not occur here
+               switch x.Log.Level {
+               case safcm.LogInfo:
+                       prefix = "[info]"
+               case safcm.LogVerbose:
+                       prefix = "[verbose]"
+               case safcm.LogDebug:
+                       prefix = "[debug]"
+               case safcm.LogDebug2:
+                       prefix = "[debug2]"
+               default:
+                       prefix = fmt.Sprintf("[INVALID=%d]", x.Log.Level)
+                       color = ColorRed
+               }
+               data = x.Log.Text
+       } else {
+               switch x.ConnEvent.Type {
+               case rpc.ConnEventStderr:
+                       prefix = "[stderr]"
+               case rpc.ConnEventDebug:
+                       prefix = "[debug3]"
+               case rpc.ConnEventUpload:
+                       if level < safcm.LogInfo {
+                               return
+                       }
+                       prefix = "[info]"
+                       x.ConnEvent.Data = "remote helper upload in progress"
+               default:
+                       prefix = fmt.Sprintf("[INVALID=%d]", x.ConnEvent.Type)
+                       color = ColorRed
+               }
+               data = x.ConnEvent.Data
+       }
+
+       host := x.Host.Name()
+       if color != 0 {
+               host = ColorString(isTTY, color, host)
+       }
+       // Make sure to escape control characters to prevent terminal
+       // injection attacks
+       if !x.Escaped {
+               data = EscapeControlCharacters(isTTY, data)
+       }
+       log.Printf("%-9s [%s] %s", prefix, host, data)
+}
diff --git a/frontend/log_test.go b/frontend/log_test.go
new file mode 100644 (file)
index 0000000..ebdd3ae
--- /dev/null
@@ -0,0 +1,390 @@
+// Copyright (C) 2021  Simon Ruderich
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package frontend
+
+import (
+       "bytes"
+       "fmt"
+       "log"
+       "os"
+       "testing"
+
+       "ruderich.org/simon/safcm"
+       "ruderich.org/simon/safcm/rpc"
+       "ruderich.org/simon/safcm/testutil"
+)
+
+type TestHost struct {
+       name string
+}
+
+func (th *TestHost) Name() string {
+       return th.name
+}
+func (th *TestHost) Dial(*rpc.Conn) error {
+       return fmt.Errorf("not implemented")
+}
+
+func TestLogEvent(t *testing.T) {
+       // Restore default logger
+       defer log.SetFlags(log.Flags())
+       defer log.SetOutput(os.Stderr)
+
+       tests := []struct {
+               name      string
+               event     Event
+               level     safcm.LogLevel
+               isTTY     bool
+               exp       string
+               expFailed bool
+       }{
+
+               {
+                       "Error",
+                       Event{
+                               Error: fmt.Errorf("fake error"),
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[error]   [fake-host] fake error\n",
+                       true,
+               },
+               {
+                       "Error (tty)",
+                       Event{
+                               Error: fmt.Errorf("fake error"),
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[error]   [\x1b[31mfake-host\x1b[0m] fake error\n",
+                       true,
+               },
+               {
+                       "Error: escape",
+                       Event{
+                               Error: fmt.Errorf("\x00"),
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[error]   [fake-host] \\x00\n",
+                       true,
+               },
+               {
+                       "Error: escape (tty)",
+                       Event{
+                               Error: fmt.Errorf("\x00"),
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[error]   [\x1b[31mfake-host\x1b[0m] \x1b[35m\\x00\x1b[0m\n",
+                       true,
+               },
+
+               {
+                       "Log: info",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "info log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[info]    [fake-host] info log\n",
+                       false,
+               },
+               {
+                       "Log: info (tty)",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "info log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[info]    [fake-host] info log\n",
+                       false,
+               },
+               {
+                       "Log: verbose",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogVerbose,
+                                       Text:  "verbose log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[verbose] [fake-host] verbose log\n",
+                       false,
+               },
+               {
+                       "Log: debug",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogDebug,
+                                       Text:  "debug log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[debug]   [fake-host] debug log\n",
+                       false,
+               },
+               {
+                       "Log: debug2",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogDebug2,
+                                       Text:  "debug2 log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[debug2]  [fake-host] debug2 log\n",
+                       false,
+               },
+               {
+                       "Log: debug3",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogDebug3,
+                                       Text:  "debug3 log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n",
+                               safcm.LogDebug3),
+                       false,
+               },
+               {
+                       "Log: debug3 (tty)",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogDebug3,
+                                       Text:  "debug3 log",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n",
+                               safcm.LogDebug3),
+                       false,
+               },
+               {
+                       "Log: escape",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "\x00",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[info]    [fake-host] \\x00\n",
+                       false,
+               },
+               {
+                       "Log: escape (tty)",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "\x00",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[info]    [fake-host] \x1b[35m\\x00\x1b[0m\n",
+                       false,
+               },
+
+               {
+                       "ConnEvent: stderr",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventStderr,
+                                       Data: "fake stderr",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[stderr]  [fake-host] fake stderr\n",
+                       false,
+               },
+               {
+                       "ConnEvent: stderr (tty)",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventStderr,
+                                       Data: "fake stderr",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[stderr]  [fake-host] fake stderr\n",
+                       false,
+               },
+               {
+                       "ConnEvent: debug",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventDebug,
+                                       Data: "conn debug",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[debug3]  [fake-host] conn debug\n",
+                       false,
+               },
+               {
+                       "ConnEvent: upload",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventUpload,
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[info]    [fake-host] remote helper upload in progress\n",
+                       false,
+               },
+               {
+                       "ConnEvent: upload (ignored)",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventUpload,
+                               },
+                       },
+                       safcm.LogError,
+                       false,
+                       "",
+                       false,
+               },
+               {
+                       "ConnEvent: invalid",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: 42,
+                                       Data: "invalid",
+                               },
+                       },
+                       safcm.LogError,
+                       false,
+                       "[INVALID=42] [fake-host] invalid\n",
+                       false,
+               },
+               {
+                       "ConnEvent: invalid (tty)",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: 42,
+                                       Data: "invalid",
+                               },
+                       },
+                       safcm.LogError,
+                       true,
+                       "[INVALID=42] [\x1b[31mfake-host\x1b[0m] invalid\n",
+                       false,
+               },
+               {
+                       "ConnEvent: escape",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventStderr,
+                                       Data: "\x00",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[stderr]  [fake-host] \\x00\n",
+                       false,
+               },
+               {
+                       "ConnEvent: escape (tty)",
+                       Event{
+                               ConnEvent: rpc.ConnEvent{
+                                       Type: rpc.ConnEventDebug,
+                                       Data: "\x01",
+                               },
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[debug3]  [fake-host] \x1b[35m\\x01\x1b[0m\n",
+                       false,
+               },
+
+               {
+                       "Escaped",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "\x00",
+                               },
+                               Escaped: true,
+                       },
+                       safcm.LogDebug3,
+                       false,
+                       "[info]    [fake-host] \x00\n",
+                       false,
+               },
+               {
+                       "Escaped (tty)",
+                       Event{
+                               Log: Log{
+                                       Level: safcm.LogInfo,
+                                       Text:  "\x00",
+                               },
+                               Escaped: true,
+                       },
+                       safcm.LogDebug3,
+                       true,
+                       "[info]    [fake-host] \x00\n",
+                       false,
+               },
+
+               {
+                       "empty (invalid)",
+                       Event{},
+                       safcm.LogDebug3,
+                       false,
+                       "[INVALID=0] [fake-host] \n",
+                       false,
+               },
+       }
+
+       for _, tc := range tests {
+               t.Run(tc.name, func(t *testing.T) {
+                       tc.event.Host = &TestHost{
+                               name: "fake-host",
+                       }
+
+                       var buf bytes.Buffer
+                       log.SetFlags(0)
+                       log.SetOutput(&buf)
+
+                       var failed bool
+                       LogEvent(tc.event, tc.level, tc.isTTY, &failed)
+
+                       testutil.AssertEqual(t, "log",
+                               buf.String(), tc.exp)
+                       testutil.AssertEqual(t, "failed",
+                               failed, tc.expFailed)
+               })
+       }
+}