updated to use a common logging library and enable log level configuration

Change-Id: Ib473615f25318c3b40cd6cf3bd49248e8a3d4fb1
diff --git a/automation/Dockerfile b/automation/Dockerfile
index 22ce5f7..c864076 100644
--- a/automation/Dockerfile
+++ b/automation/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:alpine
+FROM golang:1.6-alpine
 
 RUN apk --update add openssh-client git
 
@@ -7,13 +7,9 @@
 ADD . /go/src/gerrit.opencord.org/maas/cord-maas-automation
 
 WORKDIR /go/src/gerrit.opencord.org/maas/cord-maas-automation
-RUN /go/bin/godep restore
+RUN /go/bin/godep restore || true
 
 WORKDIR /go
-
 RUN go install gerrit.opencord.org/maas/cord-maas-automation
 
-RUN mkdir -p /root/.ssh
-COPY ssh-config /root/.ssh/config
-
 ENTRYPOINT ["/go/bin/cord-maas-automation"]
diff --git a/automation/Godeps/Godeps.json b/automation/Godeps/Godeps.json
index 88da63d..c601bc6 100644
--- a/automation/Godeps/Godeps.json
+++ b/automation/Godeps/Godeps.json
@@ -24,6 +24,10 @@
                         "ImportPath": "github.com/kelseyhightower/envconfig",
                         "Comment": "1.1.0-17-g91921eb",
                         "Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
+                },
+                {
+                        "ImportPath": "github.com/Sirupsen/logrus",
+                        "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
                 }
 	]
 }
diff --git a/automation/maas-flow.go b/automation/maas-flow.go
index be9459f..7bacddb 100644
--- a/automation/maas-flow.go
+++ b/automation/maas-flow.go
@@ -3,8 +3,8 @@
 import (
 	"encoding/json"
 	"flag"
+	"github.com/Sirupsen/logrus"
 	"github.com/kelseyhightower/envconfig"
-	"log"
 	"net/url"
 	"os"
 	"strings"
@@ -35,6 +35,8 @@
 	PowerHelperScript string `default:"" envconfig:"POWER_HELPER_SCRIPT"`
 	ProvisionUrl      string `default:"" envconfig:"PROVISION_URL"`
 	ProvisionTtl      string `default:"1h" envconfig:"PROVISION_TTL"`
+	LogLevel          string `default:"warning" envconfig:"LOG_LEVEL"`
+	LogFormat         string `default:"text" envconfig:"LOG_FORMAT"`
 }
 
 var apiKey = flag.String("apikey", "", "key with which to access MAAS server")
@@ -44,7 +46,6 @@
 var preview = flag.Bool("preview", false, "displays the action that would be taken, but does not do the action, in this mode the nodes are processed only once")
 var mappings = flag.String("mappings", "{}", "the mac to name mappings")
 var always = flag.Bool("always-rename", true, "attempt to rename at every stage of workflow")
-var verbose = flag.Bool("verbose", false, "display verbose logging")
 var filterSpec = flag.String("filter", strings.Map(func(r rune) rune {
 	if unicode.IsSpace(r) {
 		return -1
@@ -56,7 +57,7 @@
 // return false.
 func checkError(err error, message string, v ...interface{}) bool {
 	if err != nil {
-		log.Fatalf("[error] "+message, v)
+		log.Fatalf(message, v...)
 	}
 	return false
 }
@@ -65,7 +66,7 @@
 // return true, else return false.
 func checkWarn(err error, message string, v ...interface{}) bool {
 	if err != nil {
-		log.Printf("[warn] "+message, v)
+		log.Warningf(message, v...)
 		return true
 	}
 	return false
@@ -93,16 +94,19 @@
 	return nodes, nil
 }
 
+var log = logrus.New()
+
 func main() {
 
 	flag.Parse()
 	config := Config{}
 	err := envconfig.Process("AUTOMATION", &config)
-	checkError(err, "[error] unable to parse environment options : %s", err)
+	if err != nil {
+		log.Fatalf("Unable to parse configuration options : %s", err)
+	}
 
 	options := ProcessingOptions{
 		Preview:         *preview,
-		Verbose:         *verbose,
 		AlwaysRename:    *always,
 		Provisioner:     NewProvisioner(&ProvisionerConfig{Url: config.ProvisionUrl}),
 		ProvisionURL:    config.ProvisionUrl,
@@ -111,8 +115,24 @@
 		PowerHelperHost: config.PowerHelperHost,
 	}
 
+	switch config.LogFormat {
+	case "json":
+		log.Formatter = &logrus.JSONFormatter{}
+	default:
+		log.Formatter = &logrus.TextFormatter{
+			FullTimestamp: true,
+			ForceColors:   true,
+		}
+	}
+
+	level, err := logrus.ParseLevel(config.LogLevel)
+	if err != nil {
+		level = logrus.WarnLevel
+	}
+	log.Level = level
+
 	options.ProvisionTTL, err = time.ParseDuration(config.ProvisionTtl)
-	checkError(err, "[error] unable to parse specified duration of '%s' : %s", err)
+	checkError(err, "unable to parse specified duration of '%s' : %s", err)
 
 	// Determine the filter, this can either be specified on the the command
 	// line as a value or a file reference. If none is specified the default
@@ -121,17 +141,17 @@
 		if (*filterSpec)[0] == '@' {
 			name := os.ExpandEnv((*filterSpec)[1:])
 			file, err := os.OpenFile(name, os.O_RDONLY, 0)
-			checkError(err, "[error] unable to open file '%s' to load the filter : %s", name, err)
+			checkError(err, "unable to open file '%s' to load the filter : %s", name, err)
 			decoder := json.NewDecoder(file)
 			err = decoder.Decode(&options.Filter)
-			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
+			checkError(err, "unable to parse filter configuration from file '%s' : %s", name, err)
 		} else {
 			err := json.Unmarshal([]byte(*filterSpec), &options.Filter)
-			checkError(err, "[error] unable to parse filter specification: '%s' : %s", *filterSpec, err)
+			checkError(err, "unable to parse filter specification: '%s' : %s", *filterSpec, err)
 		}
 	} else {
 		err := json.Unmarshal([]byte(defaultFilter), &options.Filter)
-		checkError(err, "[error] unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
+		checkError(err, "unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
 	}
 
 	// Determine the mac to name mapping, this can either be specified on the the command
@@ -141,44 +161,45 @@
 		if (*mappings)[0] == '@' {
 			name := os.ExpandEnv((*mappings)[1:])
 			file, err := os.OpenFile(name, os.O_RDONLY, 0)
-			checkError(err, "[error] unable to open file '%s' to load the mac name mapping : %s", name, err)
+			checkError(err, "unable to open file '%s' to load the mac name mapping : %s", name, err)
 			decoder := json.NewDecoder(file)
 			err = decoder.Decode(&options.Mappings)
-			checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
+			checkError(err, "unable to parse filter configuration from file '%s' : %s", name, err)
 		} else {
 			err := json.Unmarshal([]byte(*mappings), &options.Mappings)
-			checkError(err, "[error] unable to parse mac name mapping: '%s' : %s", *mappings, err)
+			checkError(err, "unable to parse mac name mapping: '%s' : %s", *mappings, err)
 		}
 	} else {
 		err := json.Unmarshal([]byte(defaultMapping), &options.Mappings)
-		checkError(err, "[error] unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
+		checkError(err, "unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
 	}
 
 	// Verify the specified period for queries can be converted into a Go duration
 	period, err := time.ParseDuration(*queryPeriod)
-	checkError(err, "[error] unable to parse specified query period duration: '%s': %s", queryPeriod, err)
+	checkError(err, "unable to parse specified query period duration: '%s': %s", queryPeriod, err)
 
-	log.Printf(`Configuration:
+	log.Infof(`Configuration:
 	    MAAS URL:            %s
 	    MAAS API Version:    %s
 	    MAAS Query Interval: %s
 	    Node Filter:         %s
 	    Node Name Mappings:  %s
 	    Preview:             %v
-	    Verbose:             %v
 	    Always Rename:       %v
 	    Provision URL:       %s
 	    Provision TTL:       %s
 	    Power Helper:        %s
 	    Power Helper User:   %s
-	    Power Helper Host:   %s`,
+	    Power Helper Host:   %s
+	    Log Level:           %s
+	    Log Format:		 %s`,
 		*maasURL, *apiVersion, *queryPeriod, *filterSpec, *mappings, options.Preview,
-		options.Verbose, options.AlwaysRename, options.ProvisionURL, options.ProvisionTTL,
-		options.PowerHelper, options.PowerHelperUser, options.PowerHelperHost)
+		options.AlwaysRename, options.ProvisionURL, options.ProvisionTTL,
+		options.PowerHelper, options.PowerHelperUser, options.PowerHelperHost, config.LogLevel, config.LogFormat)
 
 	authClient, err := maas.NewAuthenticatedClient(*maasURL, *apiKey, *apiVersion)
 	if err != nil {
-		checkError(err, "[error] Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
+		checkError(err, "Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
 	}
 
 	// Create an object through which we will communicate with MAAS
@@ -197,7 +218,7 @@
 		// Create a ticker and fetch and process the nodes every "period"
 		ticker := time.NewTicker(period)
 		for t := range ticker.C {
-			log.Printf("[info] query server at %s", t)
+			log.Infof("query server at %s", t)
 			nodes, _ := fetchNodes(client)
 			ProcessAll(client, nodes, options)
 		}
diff --git a/automation/node.go b/automation/node.go
index 9e045e6..a54579a 100644
--- a/automation/node.go
+++ b/automation/node.go
@@ -2,8 +2,6 @@
 
 import (
 	"fmt"
-	"log"
-
 	maas "github.com/juju/gomaasapi"
 	"net/url"
 )
@@ -89,7 +87,7 @@
 	}
 	_, err := n.Update(values)
 	if err != nil {
-		log.Printf("[error] error updating power settings : %s", err.Error())
+		log.Errorf("error updating power settings : %s", err.Error())
 	}
 }
 
diff --git a/automation/state.go b/automation/state.go
index 7fe1760..5058d17 100644
--- a/automation/state.go
+++ b/automation/state.go
@@ -3,7 +3,6 @@
 import (
 	"encoding/json"
 	"fmt"
-	"log"
 	"net/url"
 	"os/exec"
 	"regexp"
@@ -44,7 +43,6 @@
 		}
 	}
 	Mappings        map[string]interface{}
-	Verbose         bool
 	Preview         bool
 	AlwaysRename    bool
 	Provisioner     Provisioner
@@ -130,7 +128,7 @@
 			if name, ok := entry.(map[string]interface{})["hostname"]; ok && current != name.(string) {
 				nodesObj := client.GetSubObject("nodes")
 				nodeObj := nodesObj.GetSubObject(node.ID())
-				log.Printf("RENAME '%s' to '%s'\n", node.Hostname(), name.(string))
+				log.Infof("RENAME '%s' to '%s'\n", node.Hostname(), name.(string))
 
 				if !options.Preview {
 					nodeObj.Update(url.Values{"hostname": []string{name.(string)}})
@@ -143,9 +141,7 @@
 
 // Reset we are at the target state, nothing to do
 var Reset = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	if options.Verbose {
-		log.Printf("RESET: %s", node.Hostname())
-	}
+	log.Debugf("RESET: %s", node.Hostname())
 
 	if options.AlwaysRename {
 		updateNodeName(client, node, options)
@@ -158,9 +154,7 @@
 
 // Provision we are at the target state, nothing to do
 var Provision = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	if options.Verbose {
-		log.Printf("CHECK PROVISION: %s", node.Hostname())
-	}
+	log.Debugf("CHECK PROVISION: %s", node.Hostname())
 
 	if options.AlwaysRename {
 		updateNodeName(client, node, options)
@@ -168,7 +162,7 @@
 
 	record, err := options.Provisioner.Get(node.ID())
 	if err != nil {
-		log.Printf("[warn] unable to retrieve provisioning state of node '%s' : %s", node.Hostname(), err)
+		log.Warningf("unable to retrieve provisioning state of node '%s' : %s", node.Hostname(), err)
 	} else if record == nil || record.Status == Failed {
 		var label string
 		if record == nil {
@@ -176,9 +170,7 @@
 		} else {
 			label = record.Status.String()
 		}
-		if options.Verbose {
-			log.Printf("[info] Current state of node '%s' is '%s'", node.Hostname(), label)
-		}
+		log.Debugf("Current state of node '%s' is '%s'", node.Hostname(), label)
 		ips := node.IPs()
 		ip := ""
 		if len(ips) > 0 {
@@ -189,10 +181,7 @@
 		if len(macs) > 0 {
 			mac = macs[0]
 		}
-		if options.Verbose {
-			log.Printf("[info] POSTing '%s' (%s) to '%s'", node.Hostname(),
-				node.ID(), options.ProvisionURL)
-		}
+		log.Debugf("POSTing '%s' (%s) to '%s'", node.Hostname(), node.ID(), options.ProvisionURL)
 		err = options.Provisioner.Provision(&ProvisionRequest{
 			Id:   node.ID(),
 			Name: node.Hostname(),
@@ -201,16 +190,16 @@
 		})
 
 		if err != nil {
-			log.Printf("[error] unable to provision '%s' (%s) : %s", node.ID, node.Hostname(), err)
+			log.Errorf("unable to provision '%s' (%s) : %s", node.ID, node.Hostname(), err)
 		}
 
 	} else if options.ProvisionTTL > 0 &&
 		record.Status == Running && time.Since(time.Unix(record.Timestamp, 0)) > options.ProvisionTTL {
-		log.Printf("[error] Provisioning of node '%s' has passed provisioning TTL of '%v'",
+		log.Errorf("Provisioning of node '%s' has passed provisioning TTL of '%v'",
 			node.Hostname(), options.ProvisionTTL)
 		options.Provisioner.Clear(node.ID())
-	} else if options.Verbose {
-		log.Printf("[info] Not invoking provisioning for '%s', current state is '%s'", node.Hostname(),
+	} else {
+		log.Debugf("Not invoking provisioning for '%s', current state is '%s'", node.Hostname(),
 			record.Status.String())
 	}
 
@@ -223,9 +212,7 @@
 	// log this fact unless we are in verbose mode. I suspect it would be
 	// nice to log it once when the device transitions from a non COMPLETE
 	// state to a complete state, but that would require keeping state.
-	if options.Verbose {
-		log.Printf("COMPLETE: %s", node.Hostname())
-	}
+	log.Debugf("COMPLETE: %s", node.Hostname())
 
 	if options.AlwaysRename {
 		updateNodeName(client, node, options)
@@ -236,7 +223,7 @@
 
 // Deploy cause a node to deploy
 var Deploy = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	log.Printf("DEPLOY: %s", node.Hostname())
+	log.Infof("DEPLOY: %s", node.Hostname())
 
 	if options.AlwaysRename {
 		updateNodeName(client, node, options)
@@ -249,7 +236,7 @@
 		// a parameter default
 		_, err := myNode.CallPost("start", url.Values{"distro_series": []string{"trusty"}})
 		if err != nil {
-			log.Printf("ERROR: DEPLOY '%s' : '%s'", node.Hostname(), err)
+			log.Errorf("DEPLOY '%s' : '%s'", node.Hostname(), err)
 			return err
 		}
 	}
@@ -258,7 +245,7 @@
 
 // Aquire aquire a machine to a specific operator
 var Aquire = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	log.Printf("AQUIRE: %s", node.Hostname())
+	log.Infof("AQUIRE: %s", node.Hostname())
 	nodesObj := client.GetSubObject("nodes")
 
 	if options.AlwaysRename {
@@ -352,7 +339,7 @@
 		_, err = nodesObj.CallPost("acquire",
 			url.Values{"name": []string{node.Hostname()}})
 		if err != nil {
-			log.Printf("ERROR: AQUIRE '%s' : '%s'", node.Hostname(), err)
+			log.Errorf("AQUIRE '%s' : '%s'", node.Hostname(), err)
 			return err
 		}
 	}
@@ -369,21 +356,21 @@
 	switch state {
 	case "on":
 		// Attempt to turn the node off
-		log.Printf("POWER DOWN: %s", node.Hostname())
+		log.Infof("POWER DOWN: %s", node.Hostname())
 		if !options.Preview {
 			//POST /api/1.0/nodes/{system_id}/ op=stop
 			nodesObj := client.GetSubObject("nodes")
 			nodeObj := nodesObj.GetSubObject(node.ID())
 			_, err := nodeObj.CallPost("stop", url.Values{"stop_mode": []string{"soft"}})
 			if err != nil {
-				log.Printf("ERROR: Commission '%s' : changing power start to off : '%s'", node.Hostname(), err)
+				log.Errorf("Commission '%s' : changing power start to off : '%s'", node.Hostname(), err)
 			}
 			return err
 		}
 		break
 	case "off":
 		// We are off so move to commissioning
-		log.Printf("COMISSION: %s", node.Hostname())
+		log.Infof("COMISSION: %s", node.Hostname())
 		if !options.Preview {
 			nodesObj := client.GetSubObject("nodes")
 			nodeObj := nodesObj.GetSubObject(node.ID())
@@ -392,14 +379,14 @@
 
 			_, err := nodeObj.CallPost("commission", url.Values{})
 			if err != nil {
-				log.Printf("ERROR: Commission '%s' : '%s'", node.Hostname(), err)
+				log.Errorf("Commission '%s' : '%s'", node.Hostname(), err)
 			}
 			return err
 		}
 		break
 	default:
 		// We are in a state from which we can't move forward.
-		log.Printf("[warn]: %s has invalid power state '%s'", node.Hostname(), state)
+		log.Warningf("%s has invalid power state '%s'", node.Hostname(), state)
 
 		// If a power helper script is set, we have an unknown power state, and
 		// we have not power type then attempt to use the helper script to discover
@@ -410,14 +397,14 @@
 					node.MACs()...)...)
 			stdout, err := cmd.Output()
 			if err != nil {
-				log.Printf("[error] Failed while executing power helper script '%s' : %s",
+				log.Errorf("Failed while executing power helper script '%s' : %s",
 					options.PowerHelper, err)
 				return err
 			}
 			power := Power{}
 			err = json.Unmarshal(stdout, &power)
 			if err != nil {
-				log.Printf("[error] Failed to parse output of power helper script '%s' : %s",
+				log.Errorf("Failed to parse output of power helper script '%s' : %s",
 					options.PowerHelper, err)
 				return err
 			}
@@ -430,7 +417,7 @@
 				}
 				node.UpdatePowerParameters(power.Name, params)
 			default:
-				log.Printf("[warn] Unsupported power type discovered '%s'", power.Name)
+				log.Warningf("Unsupported power type discovered '%s'", power.Name)
 			}
 		}
 		break
@@ -440,32 +427,32 @@
 
 // Wait a do nothing state, while work is being done
 var Wait = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	log.Printf("WAIT: %s", node.Hostname())
+	log.Infof("WAIT: %s", node.Hostname())
 	return nil
 }
 
 // Fail a state from which we cannot, currently, automatically recover
 var Fail = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	log.Printf("FAIL: %s", node.Hostname())
+	log.Infof("FAIL: %s", node.Hostname())
 	return nil
 }
 
 // AdminState an administrative state from which we should make no automatic transition
 var AdminState = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
-	log.Printf("ADMIN: %s", node.Hostname())
+	log.Infof("ADMIN: %s", node.Hostname())
 	return nil
 }
 
 func findActions(target string, current string) ([]Action, error) {
 	targets, ok := Transitions[target]
 	if !ok {
-		log.Printf("[warn] unable to find transitions to target state '%s'", target)
+		log.Warningf("unable to find transitions to target state '%s'", target)
 		return nil, fmt.Errorf("Could not find transition to target state '%s'", target)
 	}
 
 	actions, ok := targets[current]
 	if !ok {
-		log.Printf("[warn] unable to find transition from current state '%s' to target state '%s'",
+		log.Warningf("unable to find transition from current state '%s' to target state '%s'",
 			current, target)
 		return nil, fmt.Errorf("Could not find transition from current state '%s' to target state '%s'",
 			current, target)
@@ -479,7 +466,7 @@
 	var err error
 	for _, action := range actions {
 		if err = action(client, node, options); err != nil {
-			log.Printf("[error] Error while processing action for node '%s' : %s",
+			log.Errorf("Error while processing action for node '%s' : %s",
 				node.Hostname(), err)
 			break
 		}
@@ -554,16 +541,12 @@
 					errors[i] = nil
 				}
 			} else {
-				if options.Verbose {
-					log.Printf("[info] ignoring node '%s' as its zone '%s' didn't match include zone name filter '%v'",
-						node.Hostname(), node.Zone(), options.Filter.Zones.Include)
-				}
+				log.Debugf("ignoring node '%s' as its zone '%s' didn't match include zone name filter '%v'",
+					node.Hostname(), node.Zone(), options.Filter.Zones.Include)
 			}
 		} else {
-			if options.Verbose {
-				log.Printf("[info] ignoring node '%s' as it didn't match include hostname filter '%v'",
-					node.Hostname(), options.Filter.Hosts.Include)
-			}
+			log.Debugf("ignoring node '%s' as it didn't match include hostname filter '%v'",
+				node.Hostname(), options.Filter.Hosts.Include)
 		}
 	}
 	return errors
diff --git a/ip-allocator/Dockerfile b/ip-allocator/Dockerfile
index 870381c..4f62112 100644
--- a/ip-allocator/Dockerfile
+++ b/ip-allocator/Dockerfile
@@ -7,7 +7,7 @@
 ADD . /go/src/gerrit.opencord.org/maas/cord-ip-allocator
 
 WORKDIR /go/src/gerrit.opencord.org/maas/cord-ip-allocator
-RUN /go/bin/godep restore
+RUN /go/bin/godep restore || true
 
 WORKDIR /go
 
diff --git a/ip-allocator/Godeps/Godeps.json b/ip-allocator/Godeps/Godeps.json
index 8929877..8b2d186 100644
--- a/ip-allocator/Godeps/Godeps.json
+++ b/ip-allocator/Godeps/Godeps.json
@@ -20,6 +20,10 @@
 			"ImportPath": "github.com/kelseyhightower/envconfig",
 			"Comment": "1.1.0-17-g91921eb",
 			"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
-		}
+		},
+                {
+                        "ImportPath": "github.com/Sirupsen/logrus",
+                        "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+                }
 	]
 }
diff --git a/ip-allocator/allocator.go b/ip-allocator/allocator.go
index e5a64cb..27e237a 100644
--- a/ip-allocator/allocator.go
+++ b/ip-allocator/allocator.go
@@ -2,37 +2,60 @@
 
 import (
 	"fmt"
+	"github.com/Sirupsen/logrus"
 	"github.com/gorilla/mux"
 	"github.com/kelseyhightower/envconfig"
-	"log"
 	"net/http"
 )
 
 type Config struct {
-	Port    int    `default:"4242"`
-	Listen  string `default:"0.0.0.0"`
-	Network string `default:"10.0.0.0/24"`
-	Skip    int    `default:"1"`
+	Port      int    `default:"4242"`
+	Listen    string `default:"0.0.0.0"`
+	Network   string `default:"10.0.0.0/24"`
+	Skip      int    `default:"1"`
+	LogLevel  string `default:"warning" envconfig:"LOG_LEVEL"`
+	LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
 }
 
 type Context struct {
 	storage Storage
 }
 
+var log = logrus.New()
+
 func main() {
 	context := &Context{}
 
 	config := Config{}
 	err := envconfig.Process("ALLOCATE", &config)
 	if err != nil {
-		log.Fatalf("[error] Unable to parse configuration options : %s", err)
+		log.Fatalf("Unable to parse configuration options : %s", err)
 	}
 
-	log.Printf(`Configuration:
+	switch config.LogFormat {
+	case "json":
+		log.Formatter = &logrus.JSONFormatter{}
+	default:
+		log.Formatter = &logrus.TextFormatter{
+			FullTimestamp: true,
+			ForceColors:   true,
+		}
+	}
+
+	level, err := logrus.ParseLevel(config.LogLevel)
+	if err != nil {
+		level = logrus.WarnLevel
+	}
+	log.Level = level
+
+	log.Infof(`Configuration:
 	    Listen:       %s
 	    Port:         %d
 	    Network:      %s
-	    SKip:         %d`, config.Listen, config.Port, config.Network, config.Skip)
+	    SKip:         %d
+	    Log Level:    %s
+	    Log Format:   %s`, config.Listen, config.Port, config.Network, config.Skip,
+		config.LogLevel, config.LogFormat)
 
 	context.storage = &MemoryStorage{}
 	context.storage.Init(config.Network, config.Skip)
diff --git a/provisioner/Dockerfile b/provisioner/Dockerfile
index 429203a..b2721f0 100644
--- a/provisioner/Dockerfile
+++ b/provisioner/Dockerfile
@@ -39,7 +39,7 @@
 ADD . /go/src/gerrit.opencord.org/maas/cord-provisioner
 
 WORKDIR /go/src/gerrit.opencord.org/maas/cord-provisioner
-RUN $GOPATH/bin/godep restore
+RUN $GOPATH/bin/godep restore || true
 
 WORKDIR $GOPATH
 RUN go install gerrit.opencord.org/maas/cord-provisioner
diff --git a/provisioner/Godeps/Godeps.json b/provisioner/Godeps/Godeps.json
index a96e9e3..2870953 100644
--- a/provisioner/Godeps/Godeps.json
+++ b/provisioner/Godeps/Godeps.json
@@ -22,6 +22,10 @@
 			"ImportPath": "github.com/kelseyhightower/envconfig",
 			"Comment": "1.1.0-17-g91921eb",
 			"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
-		}
+		},
+                {
+                        "ImportPath": "github.com/Sirupsen/logrus",
+                        "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+                }
 	]
 }
diff --git a/provisioner/consul_storage.go b/provisioner/consul_storage.go
index 5701c72..0a0c62d 100644
--- a/provisioner/consul_storage.go
+++ b/provisioner/consul_storage.go
@@ -3,7 +3,6 @@
 import (
 	"encoding/json"
 	consul "github.com/hashicorp/consul/api"
-	"log"
 	"net/url"
 )
 
@@ -27,7 +26,7 @@
 		Scheme:  "http",
 	}
 
-	log.Printf("%+v", cfg)
+	log.Debugf("Consul config = %+v", cfg)
 
 	client, err := consul.NewClient(&cfg)
 	if err != nil {
diff --git a/provisioner/dispatcher.go b/provisioner/dispatcher.go
index d448e19..eaa65cb 100644
--- a/provisioner/dispatcher.go
+++ b/provisioner/dispatcher.go
@@ -1,7 +1,6 @@
 package main
 
 import (
-	"log"
 	"os/exec"
 	"time"
 )
@@ -51,7 +50,7 @@
 			case work := <-w.Work:
 				// Receive a work request.
 				w.StatusChan <- StatusMsg{&work, w.ID, Running, "", time.Now().Unix()}
-				log.Printf("[debug] RUN: %s %s %s %s %s %s",
+				log.Debugf("RUN: %s %s %s %s %s %s",
 					work.Script, work.Info.Id, work.Info.Name,
 					work.Info.Ip, work.Info.Mac, work.Role)
 				err := exec.Command(work.Script, work.Info.Id, work.Info.Name,
@@ -65,7 +64,7 @@
 				}
 			case <-w.QuitChan:
 				// We have been asked to stop.
-				log.Printf("worker%d stopping\n", w.ID)
+				log.Infof("worker%d stopping\n", w.ID)
 				return
 			}
 		}
@@ -112,7 +111,7 @@
 func (d *Dispatcher) Start() {
 	// Now, create all of our workers.
 	for i := 0; i < d.NumWorkers; i++ {
-		log.Printf("Creating worker %d", i)
+		log.Infof("Creating worker %d", i)
 		worker := NewWorker(i, d.WorkerQueue, d.StatusChan)
 		worker.Start()
 	}
@@ -121,24 +120,24 @@
 		for {
 			select {
 			case work := <-d.WorkQueue:
-				log.Println("[debug] Received work requeust")
+				log.Debugf("Received work requeust")
 				d.StatusChan <- StatusMsg{&work, -1, Pending, "", time.Now().Unix()}
 				go func() {
 					worker := <-d.WorkerQueue
 
-					log.Println("[debug] Dispatching work request")
+					log.Debugf("Dispatching work request")
 					worker <- work
 				}()
 			case update := <-d.StatusChan:
 				err := d.Storage.Put(update.Request.Info.Id, update)
 				if err != nil {
-					log.Printf("[error] Unable to update storage with status for '%s' : %s",
+					log.Errorf("Unable to update storage with status for '%s' : %s",
 						update.Request.Info.Id, err)
 				} else {
-					log.Printf("[debug] Storage updated for '%s'", update.Request.Info.Id)
+					log.Debugf("Storage updated for '%s'", update.Request.Info.Id)
 				}
 			case <-d.QuitChan:
-				log.Println("[info] Stopping dispatcher")
+				log.Infof("Stopping dispatcher")
 				return
 			}
 		}
diff --git a/provisioner/handlers.go b/provisioner/handlers.go
index 7fd53fa..bb93de4 100644
--- a/provisioner/handlers.go
+++ b/provisioner/handlers.go
@@ -4,7 +4,6 @@
 	"bufio"
 	"encoding/json"
 	"github.com/gorilla/mux"
-	"log"
 	"net/http"
 	"strings"
 )
@@ -59,20 +58,20 @@
 	decoder := json.NewDecoder(r.Body)
 	defer r.Body.Close()
 	if err := decoder.Decode(&info); err != nil {
-		log.Printf("[error] Unable to decode request to provision : %s", err)
+		log.Errorf("Unable to decode request to provision : %s", err)
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
 
 	if !c.validateData(&info) {
-		log.Printf("[error] Provisioning request not valid for '%s'", info.Name)
+		log.Errorf("Provisioning request not valid for '%s'", info.Name)
 		w.WriteHeader(http.StatusBadRequest)
 		return
 	}
 
 	role, err := c.GetRole(&info)
 	if err != nil {
-		log.Printf("[error] unable to get provisioning role for node '%s' : %s", info.Name, err)
+		log.Errorf("unable to get provisioning role for node '%s' : %s", info.Name, err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
@@ -84,7 +83,7 @@
 	}
 	err = c.dispatcher.Dispatch(&info, role, script)
 	if err != nil {
-		log.Printf("[error] unable to dispatch provisioning request for node '%s' : %s", info.Name, err)
+		log.Errorf("unable to dispatch provisioning request for node '%s' : %s", info.Name, err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
@@ -112,7 +111,7 @@
 
 	err := c.storage.Delete(id)
 	if err != nil {
-		log.Printf("[warn] Error while deleting status fo '%s' from storage : %s", id, err)
+		log.Errorf("Error while deleting status fo '%s' from storage : %s", id, err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
@@ -129,7 +128,7 @@
 	}
 	s, err := c.storage.Get(id)
 	if err != nil {
-		log.Printf("[warn] Error while retrieving status for '%s' from strorage : %s", id, err)
+		log.Errorf("Error while retrieving status for '%s' from strorage : %s", id, err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
@@ -139,7 +138,7 @@
 	}
 	bytes, err := json.Marshal(s)
 	if err != nil {
-		log.Printf("[error] Error while attempting to marshal status for '%s' from storage : %s", id, err)
+		log.Errorf("Error while attempting to marshal status for '%s' from storage : %s", id, err)
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
diff --git a/provisioner/provisioner.go b/provisioner/provisioner.go
index 41240b5..6c02c14 100644
--- a/provisioner/provisioner.go
+++ b/provisioner/provisioner.go
@@ -2,9 +2,9 @@
 
 import (
 	"fmt"
+	"github.com/Sirupsen/logrus"
 	"github.com/gorilla/mux"
 	"github.com/kelseyhightower/envconfig"
-	"log"
 	"net/http"
 )
 
@@ -15,6 +15,8 @@
 	DefaultRole     string `default:"compute-node" envconfig:"default_role"`
 	Script          string `default:"do-ansible"`
 	StorageURL      string `default:"memory:" envconfig:"storage_url"`
+	LogLevel        string `default:"warning" envconfig:"LOG_LEVEL"`
+	LogFormat       string `default:"text" envconfig:"LOG_FORMAT"`
 }
 
 type Context struct {
@@ -24,23 +26,44 @@
 	dispatcher *Dispatcher
 }
 
+var log = logrus.New()
+
 func main() {
 	context := &Context{}
 
 	err := envconfig.Process("PROVISION", &(context.config))
 	if err != nil {
-		log.Fatalf("[error] Unable to parse configuration options : %s", err)
+		log.Fatalf("[ERRO] Unable to parse configuration options : %s", err)
 	}
 
-	log.Printf(`Configuration:
+	switch context.config.LogFormat {
+	case "json":
+		log.Formatter = &logrus.JSONFormatter{}
+	default:
+		log.Formatter = &logrus.TextFormatter{
+			FullTimestamp: true,
+			ForceColors:   true,
+		}
+	}
+
+	level, err := logrus.ParseLevel(context.config.LogLevel)
+	if err != nil {
+		level = logrus.WarnLevel
+	}
+	log.Level = level
+
+	log.Infof(`Configuration:
 	    Listen:          %s
 	    Port:            %d
 	    RoleSelectorURL: %s
 	    DefaultRole:     %s
 	    Script:          %s
-	    StorageURL:      %s`,
+	    StorageURL:      %s
+	    Log Level:       %s
+	    Log Format:      %s`,
 		context.config.Listen, context.config.Port, context.config.RoleSelectorURL,
-		context.config.DefaultRole, context.config.Script, context.config.StorageURL)
+		context.config.DefaultRole, context.config.Script, context.config.StorageURL,
+		context.config.LogLevel, context.config.LogFormat)
 
 	context.storage, err = NewStorage(context.config.StorageURL)
 	if err != nil {
diff --git a/roles/maas/templates/automation-compose.yml.j2 b/roles/maas/templates/automation-compose.yml.j2
index a7e9f49..c6c9c28 100644
--- a/roles/maas/templates/automation-compose.yml.j2
+++ b/roles/maas/templates/automation-compose.yml.j2
@@ -12,6 +12,7 @@
       - "/etc/maas/automation/storage:/consul/data"
     network_mode: host
     command: agent --server --bind {{ mgmt_ip_address.stdout }} --client {{ mgmt_ip_address.stdout }} --bootstrap-expect=1
+    restart: unless-stopped
 
   allocator:
     image: "docker-registry:5000/cord-ip-allocator:{{ docker.image_version }}"
@@ -26,6 +27,8 @@
       - "ALLOCATE_LISTEN=0.0.0.0"
       - "ALLOCATE_NETWORK={{ networks.fabric }}"
       - "ALLOCATE_SKIP=2"
+      - "ALLOCATE_LOG_LEVEL=debug"
+      - "ALLOCATE_LOG_FORMAT=text"
     restart: unless-stopped
 
   provisioner:
@@ -36,6 +39,7 @@
       - "lab.component=provisioner"
     links:
       - allocator
+      - storage
     environment:
       # need to explicitly set the resolver, else go will skip the /etc/hosts file
       - "GODEBUG=netdns=go"
@@ -45,6 +49,8 @@
       - "PROVISION_DEFAULT_ROLE=compute-node"
       - "PROVISION_SCRIPT=/etc/maas/ansible/do-ansible"
       - "PROVISION_STORAGE_URL=consul://{{ mgmt_ip_address.stdout }}:8500"
+      - "PROVISION_LOG_LEVEL=debug"
+      - "PROVISION_LOG_FORMAT=text"
     volumes:
       - "/etc/maas/ansible:/etc/maas/ansible"
     restart: unless-stopped
@@ -63,6 +69,8 @@
       - "SWITCHQ_PROVISION_TTL=0s"
       - "SWITCHQ_DEFAULT_ROLE=fabric-switch"
       - "SWITCHQ_ADDRESS_URL=file:///switchq/dhcp/dhcp_harvest.inc"
+      - "SWITCHQ_LOG_LEVEL=debug"
+      - "SWITCHQ_LOG_FORMAT=text"
     volumes:
       - "/etc/bind/maas:/switchq/dhcp"
     restart: unless-stopped
@@ -85,11 +93,13 @@
       - "AUTOMATION_POWER_HELPER_USER={{ virtualbox.power_helper_user }}"
       - "AUTOMATION_POWER_HELPER_HOST={{ virtualbox_host }}"
 {% endif %}
+      - "AUTOMATION_LOG_FORMAT=text"
+      - "AUTOMATION_LOG_LEVEL=debug"
     volumes:
       - "/etc/maas:/mappings"
 {% if virtualbox_support is defined and virtualbox_support == "1" %}
       - "/etc/maas/virtualbox:/etc/maas/virtualbox"
 {% endif %}
-    command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ mgmt_ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename", "-verbose" ]
+    command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ mgmt_ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename" ]
     restart: unless-stopped
 
diff --git a/switchq/Dockerfile b/switchq/Dockerfile
index a0966e8..cf30306 100644
--- a/switchq/Dockerfile
+++ b/switchq/Dockerfile
@@ -42,7 +42,7 @@
 ADD . $GOPATH/src/gerrit.opencord.com/maas/switchq
 
 WORKDIR $GOPATH/src/gerrit.opencord.com/maas/switchq
-RUN $GOPATH/bin/godep restore
+RUN $GOPATH/bin/godep restore || true
 
 WORKDIR $GOPATH
 RUN go install gerrit.opencord.com/maas/switchq
diff --git a/switchq/Godeps/Godeps.json b/switchq/Godeps/Godeps.json
index 00c42a5..cc3867f 100644
--- a/switchq/Godeps/Godeps.json
+++ b/switchq/Godeps/Godeps.json
@@ -7,6 +7,10 @@
 			"ImportPath": "github.com/kelseyhightower/envconfig",
 			"Comment": "1.1.0-17-g91921eb",
 			"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
-		}
+		},
+                {
+                        "ImportPath": "github.com/Sirupsen/logrus",
+                        "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+                }
 	]
 }
diff --git a/switchq/switchq.go b/switchq/switchq.go
index b586d75..2c084ed 100644
--- a/switchq/switchq.go
+++ b/switchq/switchq.go
@@ -4,8 +4,8 @@
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"github.com/Sirupsen/logrus"
 	"github.com/kelseyhightower/envconfig"
-	"log"
 	"net/http"
 	"time"
 )
@@ -20,6 +20,8 @@
 	RoleSelectorURL string `default:"" envconfig:"role_selector_url"`
 	DefaultRole     string `default:"fabric-switch" envconfig:"default_role"`
 	Script          string `default:"do-ansible"`
+	LogLevel        string `default:"warning" envconfig:"LOG_LEVEL"`
+	LogFormat       string `default:"text" envconfig:"LOG_FORMAT"`
 
 	vendors       Vendors
 	storage       Storage
@@ -35,16 +37,16 @@
 }
 
 func (c *Config) getProvisionedState(rec AddressRec) (int, string, error) {
-	log.Printf("[debug] Fetching provisioned state of device '%s' (%s, %s)",
+	log.Debugf("Fetching provisioned state of device '%s' (%s, %s)",
 		rec.Name, rec.IP, rec.MAC)
 	resp, err := http.Get(c.ProvisionURL + rec.MAC)
 	if err != nil {
-		log.Printf("[error] Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
+		log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
 			rec.Name, rec.IP, rec.MAC, err)
 		return -1, "", err
 	}
 	if resp.StatusCode != 404 && int(resp.StatusCode/100) != 2 {
-		log.Printf("[error] Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
+		log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
 			rec.Name, rec.IP, rec.MAC, resp.Status)
 		return -1, "", fmt.Errorf(resp.Status)
 	}
@@ -54,7 +56,7 @@
 		var raw interface{}
 		err = decoder.Decode(&raw)
 		if err != nil {
-			log.Printf("[error] Unmarshal provisioning service response for device '%s (%s, %s)' : %s",
+			log.Errorf("Unmarshal provisioning service response for device '%s (%s, %s)' : %s",
 				rec.Name, rec.IP, rec.MAC, err)
 			return -1, "", err
 		}
@@ -68,7 +70,7 @@
 			return 3, status["message"].(string), nil
 		default:
 			err = fmt.Errorf("unknown provisioning status : %d", status["status"])
-			log.Printf("[error] received unknown provisioning status for device '%s (%s)' : %s",
+			log.Errorf("received unknown provisioning status for device '%s (%s)' : %s",
 				rec.Name, rec.MAC, err)
 			return -1, "", err
 		}
@@ -80,7 +82,7 @@
 }
 
 func (c *Config) provision(rec AddressRec) error {
-	log.Printf("[info] POSTing to '%s' for provisioning of '%s (%s)'", c.ProvisionURL, rec.Name, rec.MAC)
+	log.Infof("POSTing to '%s' for provisioning of '%s (%s)'", c.ProvisionURL, rec.Name, rec.MAC)
 	data := map[string]string{
 		"id":   rec.MAC,
 		"name": rec.Name,
@@ -101,25 +103,25 @@
 	var b []byte
 	b, err := json.Marshal(data)
 	if err != nil {
-		log.Printf("[error] Unable to marshal provisioning data : %s", err)
+		log.Errorf("Unable to marshal provisioning data : %s", err)
 		return err
 	}
 	req, err := http.NewRequest("POST", c.ProvisionURL, bytes.NewReader(b))
 	if err != nil {
-		log.Printf("[error] Unable to construct POST request to provisioner : %s", err)
+		log.Errorf("Unable to construct POST request to provisioner : %s", err)
 		return err
 	}
 
 	req.Header.Add("Content-Type", "application/json")
 	resp, err := hc.Do(req)
 	if err != nil {
-		log.Printf("[error] Unable to POST request to provisioner : %s", err)
+		log.Errorf("Unable to POST request to provisioner : %s", err)
 		return err
 	}
 
 	defer resp.Body.Close()
 	if resp.StatusCode != http.StatusAccepted {
-		log.Printf("[error] Provisioning request not accepted by provisioner : %s", resp.Status)
+		log.Errorf("Provisioning request not accepted by provisioner : %s", resp.Status)
 		return err
 	}
 
@@ -134,7 +136,7 @@
 
 	if !ok {
 		// Not something we care about
-		log.Printf("[debug] host with IP '%s' and MAC '%s' and named '%s' not a known switch type",
+		log.Debugf("host with IP '%s' and MAC '%s' and named '%s' not a known switch type",
 			rec.IP, rec.MAC, rec.Name)
 		return nil
 	}
@@ -145,10 +147,10 @@
 	}
 
 	if last == nil {
-		log.Printf("[debug] no TTL for device '%s' (%s, %s)",
+		log.Debugf("no TTL for device '%s' (%s, %s)",
 			rec.Name, rec.IP, rec.MAC)
 	} else {
-		log.Printf("[debug] TTL for device '%s' (%s, %s) is %v",
+		log.Debugf("TTL for device '%s' (%s, %s) is %v",
 			rec.Name, rec.IP, rec.MAC, *last)
 	}
 
@@ -157,23 +159,23 @@
 	state, message, err := c.getProvisionedState(rec)
 	switch state {
 	case 0, 1: // Pending or Running
-		log.Printf("[debug] device '%s' (%s, %s) is being provisioned",
+		log.Debugf("device '%s' (%s, %s) is being provisioned",
 			rec.Name, rec.IP, rec.MAC)
 		return nil
 	case 2: // Complete
-		log.Printf("[debug] device '%s' (%s, %s) has completed provisioning",
+		log.Debugf("device '%s' (%s, %s) has completed provisioning",
 			rec.Name, rec.IP, rec.MAC)
 		// If no last record then set the TTL
 		if last == nil {
 			now := time.Now()
 			last = &now
 			c.storage.MarkProvisioned(rec.MAC, last)
-			log.Printf("[debug] Storing TTL for device '%s' (%s, %s) as %v",
+			log.Debugf("Storing TTL for device '%s' (%s, %s) as %v",
 				rec.Name, rec.IP, rec.MAC, now)
 			return nil
 		}
 	case 3: // Failed
-		log.Printf("[debug] device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
+		log.Debugf("device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
 			rec.Name, rec.IP, rec.MAC, message)
 		c.storage.ClearProvisioned(rec.MAC)
 		last = nil
@@ -184,25 +186,46 @@
 	if last == nil || (c.ttl > 0 && time.Since(*last) > c.ttl) {
 		if last != nil {
 			c.storage.ClearProvisioned(rec.MAC)
-			log.Printf("[debug] device '%s' (%s, %s) TTL expired, reprovisioning",
+			log.Debugf("device '%s' (%s, %s) TTL expired, reprovisioning",
 				rec.Name, rec.IP, rec.MAC)
 		}
 		c.provision(rec)
 	} else if c.ttl == 0 {
-		log.Printf("[debug] device '%s' (%s, %s) has completed its one time provisioning, with a TTL set to %s",
+		log.Debugf("device '%s' (%s, %s) has completed its one time provisioning, with a TTL set to %s",
 			rec.Name, rec.IP, rec.MAC, c.ProvisionTTL)
 	} else {
-		log.Printf("[debug] device '%s' (%s, %s) has completed provisioning within the specified TTL of %s",
+		log.Debugf("device '%s' (%s, %s) has completed provisioning within the specified TTL of %s",
 			rec.Name, rec.IP, rec.MAC, c.ProvisionTTL)
 	}
 	return nil
 }
 
+var log = logrus.New()
+
 func main() {
 
 	var err error
 	config := Config{}
-	envconfig.Process("SWITCHQ", &config)
+	err = envconfig.Process("SWITCHQ", &config)
+	if err != nil {
+		log.Fatalf("Unable to parse configuration options : %s", err)
+	}
+
+	switch config.LogFormat {
+	case "json":
+		log.Formatter = &logrus.JSONFormatter{}
+	default:
+		log.Formatter = &logrus.TextFormatter{
+			FullTimestamp: true,
+			ForceColors:   true,
+		}
+	}
+
+	level, err := logrus.ParseLevel(config.LogLevel)
+	if err != nil {
+		level = logrus.WarnLevel
+	}
+	log.Level = level
 
 	config.vendors, err = NewVendors(config.VendorsURL)
 	checkError(err, "Unable to create known vendors list from specified URL '%s' : %s", config.VendorsURL, err)
@@ -219,7 +242,7 @@
 	config.ttl, err = time.ParseDuration(config.ProvisionTTL)
 	checkError(err, "Unable to parse specified provision TTL value of '%s' : %s", config.ProvisionTTL, err)
 
-	log.Printf(`Configuration:
+	log.Infof(`Configuration:
 		Vendors URL:       %s
 		Storage URL:       %s
 		Poll Interval:     %s
@@ -228,25 +251,28 @@
 		Provision URL:     %s
 		Role Selector URL: %s
 		Default Role:      %s
-		Script:            %s`,
+		Script:            %s
+		Log Level:         %s
+		Log Format:        %s`,
 		config.VendorsURL, config.StorageURL, config.PollInterval, config.AddressURL, config.ProvisionTTL,
-		config.ProvisionURL, config.RoleSelectorURL, config.DefaultRole, config.Script)
+		config.ProvisionURL, config.RoleSelectorURL, config.DefaultRole, config.Script,
+		config.LogLevel, config.LogFormat)
 
 	// We use two methods to attempt to find the MAC (hardware) address associated with an IP. The first
 	// is to look in the table. The second is to send an ARP packet.
 	for {
-		log.Printf("[info] Checking for switches @ %s", time.Now())
+		log.Infof("Checking for switches @ %s", time.Now())
 		addresses, err := config.addressSource.GetAddresses()
 
 		if err != nil {
-			log.Printf("[error] unable to read addresses from address source : %s", err)
+			log.Errorf("unable to read addresses from address source : %s", err)
 		} else {
-			log.Printf("[info] Queried %d addresses from address source", len(addresses))
+			log.Infof("Queried %d addresses from address source", len(addresses))
 
 			for _, rec := range addresses {
-				log.Printf("[debug] Processing %s(%s, %s)", rec.Name, rec.IP, rec.MAC)
+				log.Debugf("Processing %s(%s, %s)", rec.Name, rec.IP, rec.MAC)
 				if err := config.processRecord(rec); err != nil {
-					log.Printf("[error] Error when processing IP '%s' : %s", rec.IP, err)
+					log.Errorf("Error when processing IP '%s' : %s", rec.IP, err)
 				}
 			}
 		}
diff --git a/switchq/vendors.go b/switchq/vendors.go
index d2a0cc8..c0de108 100644
--- a/switchq/vendors.go
+++ b/switchq/vendors.go
@@ -2,9 +2,8 @@
 
 import (
 	"encoding/json"
-	"log"
-	"strings"
 	"net/http"
+	"strings"
 )
 
 type Vendors interface {
@@ -43,7 +42,7 @@
 	for _, rec := range data {
 		v.Vendors[rec.Prefix] = rec
 	}
-	log.Printf("[debug] %v", v.Vendors)
+	log.Debugf("known vendors %+v", v.Vendors)
 
 	return &v, nil
 }