diff --git a/api/client.go b/api/client.go
index 416c6e0feba27ae902fca569ebf15cf86d2cd8a2..034fe32935afdd46544cb79a9f1fc9d6662a3008 100644
--- a/api/client.go
+++ b/api/client.go
@@ -15,7 +15,6 @@ import (
 	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/pkg/errors"
-	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/bots"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
@@ -74,7 +73,7 @@ func NewTestClient(s globals.Storage, locA, locB string, ndfJSON *ndf.NetworkDef
 	case *testing.B:
 		break
 	default:
-		jww.FATAL.Panicf("GenerateId is restricted to testing only. Got %T", i)
+		globals.Log.FATAL.Panicf("GenerateId is restricted to testing only. Got %T", i)
 	}
 	return newClient(s, locA, locB, ndfJSON, sendFunc)
 }
diff --git a/cmd/root.go b/cmd/root.go
index 7edd3e7449d5e3f8e3e83b3ceac393871f639262..401f0ba6139f2bacda00b21371af4b55d31c7fbe 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -12,7 +12,6 @@ import (
 	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/spf13/cobra"
-	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"gitlab.com/elixxir/client/api"
 	"gitlab.com/elixxir/client/bots"
@@ -26,7 +25,6 @@ import (
 	"gitlab.com/elixxir/primitives/switchboard"
 	"gitlab.com/elixxir/primitives/utils"
 	"io/ioutil"
-	"log"
 	"os"
 	"strconv"
 	"strings"
@@ -57,6 +55,7 @@ var searchForUser string
 var waitForMessages uint
 var messageTimeout uint
 var messageCnt uint
+var logPath string = ""
 
 // Execute adds all child commands to the root command and sets flags
 // appropriately.  This is called by main.main(). It only needs to
@@ -343,9 +342,12 @@ func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...i
 	} else {
 		senderNick = sender.Username
 	}
-	fmt.Printf("Message from %v, %v Received: %s\n Timestamp: %s",
+	logMsg := fmt.Sprintf("Message from %v, %v Received: %s\n",
 		large.NewIntFromBytes(message.Sender[:]).Text(10),
-		senderNick, result.Message, message.Timestamp.String())
+		senderNick, result.Message)
+	globals.Log.INFO.Printf("%s -- Timestamp: %s\n", logMsg,
+		message.Timestamp.String())
+	fmt.Printf(logMsg)
 
 	atomic.AddInt64(&l.MessagesReceived, 1)
 }
@@ -374,8 +376,14 @@ var rootCmd = &cobra.Command{
 	Short: "Runs a client for cMix anonymous communication platform",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
+		if !verbose && viper.Get("verbose") != nil {
+			verbose = viper.GetBool("verbose")
+		}
+		if logPath == "" && viper.Get("logPath") != nil {
+			logPath = viper.GetString("logPath")
+		}
+		globals.Log = globals.InitLog(verbose, logPath)
 		// Main client run function
-
 		if showVer {
 			printVersion()
 			return
@@ -414,7 +422,6 @@ var rootCmd = &cobra.Command{
 			globals.Log.FATAL.Panicf("Could not initialize receivers: %s\n", err)
 		}
 
-
 		err = client.StartMessageReceiver(cb)
 
 		if err != nil {
@@ -425,7 +432,7 @@ var rootCmd = &cobra.Command{
 		if username != "" {
 			err := client.RegisterWithUDB(username, 2*time.Minute)
 			if err != nil {
-				jww.ERROR.Printf("Could not register with UDB: %+v", err)
+				globals.Log.ERROR.Printf("%+v", err)
 			}
 		}
 
@@ -469,8 +476,15 @@ var rootCmd = &cobra.Command{
 				wireOut := api.FormatTextMessage(message)
 
 				for i := uint(0); i < messageCnt; i++ {
-					fmt.Printf("Sending Message to %s, %v: %s\n", base64.StdEncoding.EncodeToString(recipientId.Bytes()),
+					logMsg := fmt.Sprintf(
+						"Sending Message to "+
+							"%s, %v: %s\n",
+						large.NewIntFromBytes(
+							recipientId[:]).Text(
+							10),
 						recipientNick, message)
+					globals.Log.INFO.Printf(logMsg)
+					fmt.Printf(logMsg)
 					if i != 0 {
 						time.Sleep(1 * time.Second)
 					}
@@ -563,7 +577,7 @@ func init() {
 	// There is one init in each sub command. Do not put variable declarations
 	// here, and ensure all the Flags are of the *P variety, unless there's a
 	// very good reason not to have them as local params to sub command."
-	cobra.OnInitialize(initConfig, initLog)
+	cobra.OnInitialize(initConfig)
 
 	// Here you will define your flags and configuration settings.
 	// Cobra supports persistent flags, which, if defined here,
@@ -655,6 +669,9 @@ func init() {
 	rootCmd.Flags().StringVarP(&searchForUser, "SearchForUser", "s", "",
 		"Sets the email to search for to find a user with user discovery")
 
+	rootCmd.Flags().StringVarP(&logPath, "log", "l", "",
+		"Print logs to specified log file, not stdout")
+
 	rootCmd.Flags().UintVarP(&messageTimeout, "messageTimeout",
 		"t", 45, "The number of seconds to wait for "+
 			"'waitForMessages' messages to arrive")
@@ -665,28 +682,3 @@ func init() {
 
 // initConfig reads in config file and ENV variables if set.
 func initConfig() {}
-
-// initLog initializes logging thresholds and the log path.
-func initLog() {
-	globals.Log = jww.NewNotepad(jww.LevelError, jww.LevelInfo, os.Stdout,
-		ioutil.Discard, "CLIENT", log.Ldate|log.Ltime)
-	// If verbose flag set then log more info for debugging
-	if verbose || viper.GetBool("verbose") {
-		globals.Log.SetLogThreshold(jww.LevelDebug)
-		globals.Log.SetStdoutThreshold(jww.LevelDebug)
-		globals.Log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
-	} else {
-		globals.Log.SetLogThreshold(jww.LevelInfo)
-		globals.Log.SetStdoutThreshold(jww.LevelInfo)
-	}
-	if viper.Get("logPath") != nil {
-		// Create log file, overwrites if existing
-		logPath := viper.GetString("logPath")
-		logFile, err := os.Create(logPath)
-		if err != nil {
-			globals.Log.WARN.Println("Invalid or missing log path, default path used.")
-		} else {
-			globals.Log.SetLogOutput(logFile)
-		}
-	}
-}
diff --git a/cmd/udb.go b/cmd/udb.go
index a18ffc1c033e3e311c7d95b895062de112df5ef7..3b234d6e06ff687e219ccfff9c4c22e4ab1a1e83 100644
--- a/cmd/udb.go
+++ b/cmd/udb.go
@@ -7,9 +7,8 @@
 package cmd
 
 import (
-	"fmt"
-	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/api"
+	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/primitives/id"
 	"strings"
 	"time"
@@ -19,11 +18,11 @@ type callbackSearch struct{}
 
 func (cs callbackSearch) Callback(userID, pubKey []byte, err error) {
 	if err != nil {
-		fmt.Printf("UDB search failed: %v\n", err.Error())
+		globals.Log.INFO.Printf("UDB search failed: %v\n", err.Error())
 	} else if len(pubKey) == 0 {
-		fmt.Printf("Public Key returned is empty\n")
+		globals.Log.INFO.Printf("Public Key returned is empty\n")
 	} else {
-		fmt.Printf("UDB search successful. Returned user %v\n",
+		globals.Log.INFO.Printf("UDB search successful. Returned user %v\n",
 			*id.NewUserFromBytes(userID))
 	}
 }
@@ -35,7 +34,7 @@ func parseUdbMessage(msg string, client *api.Client) {
 	// Split the message on spaces
 	args := strings.Fields(msg)
 	if len(args) < 3 {
-		jww.ERROR.Printf("UDB command must have at least three arguments!")
+		globals.Log.ERROR.Printf("UDB command must have at least three arguments!")
 	}
 	// The first arg is the command
 	// the second is the valueType
@@ -45,8 +44,8 @@ func parseUdbMessage(msg string, client *api.Client) {
 	if strings.EqualFold(keyword, "SEARCH") {
 		client.SearchForUser(args[2], searchCallback, 2*time.Minute)
 	} else if strings.EqualFold(keyword, "REGISTER") {
-		jww.ERROR.Printf("UDB REGISTER not allowed, it is already done during user registration")
+		globals.Log.ERROR.Printf("UDB REGISTER not allowed, it is already done during user registration")
 	} else {
-		jww.ERROR.Printf("UDB command not recognized!")
+		globals.Log.ERROR.Printf("UDB command not recognized!")
 	}
 }
diff --git a/globals/log.go b/globals/log.go
index bb092c64074e1cfe5e6a197aa508bfb630c08837..e9bfc4a45df68296c87ff7eebc42727fd8f73a35 100644
--- a/globals/log.go
+++ b/globals/log.go
@@ -8,12 +8,47 @@ package globals
 
 import (
 	jww "github.com/spf13/jwalterweatherman"
+	"io"
 	"io/ioutil"
 	"log"
 	"os"
 )
 
-// We're now logging everything to this notepad so that the CUI can replace it
+// Log is logging everything to this notepad so that the CUI can replace it
 // with its own notepad and get logging statements from the client
 var Log = jww.NewNotepad(jww.LevelInfo, jww.LevelInfo, os.Stdout,
 	ioutil.Discard, "CLIENT", log.Ldate|log.Ltime)
+
+// InitLog initializes logging thresholds and the log path.
+// verbose turns on debug logging, setting the log path to nil
+// uses std out.
+func InitLog(verbose bool, logPath string) *jww.Notepad {
+	logLevel := jww.LevelInfo
+	logFlags := (log.Ldate | log.Ltime)
+	stdOut := io.Writer(os.Stdout)
+	logFile := ioutil.Discard
+
+	// If the verbose flag is set, print all logs and
+	// print microseconds as well
+	if verbose {
+		logLevel = jww.LevelDebug
+		logFlags = (log.Ldate | log.Ltime | log.Lmicroseconds)
+	}
+	// If the logpath is empty or not set to - (stdout),
+	// set up the log file and do not log to stdout
+	if logPath != "" && logPath != "-" {
+		// Create log file, overwrites if existing
+		lF, err := os.OpenFile(logPath,
+			os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+		if err != nil {
+			Log.WARN.Println("Invalid or missing log path," +
+				" stdout used.")
+		} else {
+			logFile = io.Writer(lF)
+			stdOut = ioutil.Discard
+		}
+	}
+
+	return jww.NewNotepad(logLevel, logLevel, stdOut, logFile,
+		"CLIENT", logFlags)
+}
diff --git a/go.mod b/go.mod
index fcf1ec6ffd384942f9e08a1e1fb24b0ac2b37e8d..d895081aaf9e4086715312a245148eb7790b75c0 100644
--- a/go.mod
+++ b/go.mod
@@ -18,10 +18,10 @@ require (
 	github.com/spf13/viper v1.6.2
 	gitlab.com/elixxir/comms v0.0.0-20200206201144-aa6e356b3770
 	gitlab.com/elixxir/crypto v0.0.0-20200206203107-b8926242da23
-	gitlab.com/elixxir/primitives v0.0.0-20200131183153-e93c6b75019f
-	golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a
+	gitlab.com/elixxir/primitives v0.0.0-20200207225613-9a4445ddec16
+	golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678
 	golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect
-	google.golang.org/genproto v0.0.0-20200205142000-a86caf926a67 // indirect
+	google.golang.org/genproto v0.0.0-20200211111953-2dc5924e3898 // indirect
 	google.golang.org/grpc v1.27.1 // indirect
 	gopkg.in/ini.v1 v1.52.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 0c7f60f8ac2d037a3b3403196ce10460aa3c8877..371033d01b7991f7ca57edc1972f70ea5b22c874 100644
--- a/go.sum
+++ b/go.sum
@@ -239,6 +239,8 @@ gitlab.com/elixxir/primitives v0.0.0-20191028233752-882c08b8f095/go.mod h1:+UiRR
 gitlab.com/elixxir/primitives v0.0.0-20200106183011-a68f1e6f188e/go.mod h1:g9v3S34ZUeqGRiOTV7esByK8a5TovJ3YgTv/328ny6w=
 gitlab.com/elixxir/primitives v0.0.0-20200131183153-e93c6b75019f h1:F0YwFZz4umoXOJ+xX34WIRrucuLgHCSyKxWOKGaEt5g=
 gitlab.com/elixxir/primitives v0.0.0-20200131183153-e93c6b75019f/go.mod h1:REJMcwIcyxh74VSHqy4S9yYiaEsQYObOPglRExDpk14=
+gitlab.com/elixxir/primitives v0.0.0-20200207225613-9a4445ddec16 h1:ifJ/7gl7odnp8iz09ranziiSmH+ZI4CalQW2PQn0W6M=
+gitlab.com/elixxir/primitives v0.0.0-20200207225613-9a4445ddec16/go.mod h1:REJMcwIcyxh74VSHqy4S9yYiaEsQYObOPglRExDpk14=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -261,6 +263,8 @@ golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi
 golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a h1:aczoJ0HPNE92XKa7DrIzkNN6esOKO2TBwiiYoKcINhA=
 golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678 h1:wCWoJcFExDgyYx2m2hpHgwz8W3+FPdfldvIgzqDIhyg=
+golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -358,6 +362,8 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb h1:ADPHZzpzM4tk4V4
 google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
 google.golang.org/genproto v0.0.0-20200205142000-a86caf926a67 h1:MBO9fkVSrTpJ8vgHLPi5gb+ZWXEy7/auJN8yqyu9EiE=
 google.golang.org/genproto v0.0.0-20200205142000-a86caf926a67/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
+google.golang.org/genproto v0.0.0-20200211111953-2dc5924e3898 h1:bKX1IGaSj2XD9yfWNts9HKRdQRH0lOZ0S7Nb8meQSlY=
+google.golang.org/genproto v0.0.0-20200211111953-2dc5924e3898/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0=
diff --git a/rekey/rekey.go b/rekey/rekey.go
index c67d42a8ec215cb4fba3930c7c794d9e21e0b33e..674723f744e31107b0487d6732c5f3fd4bb19a67 100644
--- a/rekey/rekey.go
+++ b/rekey/rekey.go
@@ -167,7 +167,7 @@ func rekeyProcess(rt rekeyType, partner *id.User, data []byte) error {
 	if ctx == nil {
 		if rt == RekeyTrigger {
 			privKeyCyclic = e2egrp.RandomCoprime(e2egrp.NewInt(1))
-			fmt.Println("Private key actual: ", privKeyCyclic.Text(16))
+			globals.Log.DEBUG.Println("Private key actual: ", privKeyCyclic.Text(16))
 			pubKeyCyclic = e2egrp.ExpG(privKeyCyclic, e2egrp.NewInt(1))
 			// Get Current Partner Public Key from RekeyKeys
 			partnerPubKeyCyclic = keys.CurrPubKey