shithub: hugo

Download patch

ref: 45e3ed517a17648d54e8ce33618a8f251cfec603
parent: 24a286791f37cbf6638b37f29386949045d0bba2
author: Bjørn Erik Pedersen <[email protected]>
date: Tue Jan 3 12:28:51 EST 2017

all: Refactor to non-global logger

Note that this looks like overkill for just the logger, and that is correct,
but this will make sense once we start with the template handling etc.

Updates #2701

--- a/commands/benchmark.go
+++ b/commands/benchmark.go
@@ -48,8 +48,8 @@
 }
 
 func benchmark(cmd *cobra.Command, args []string) error {
-	var err error
-	if err = InitializeConfig(benchmarkCmd); err != nil {
+	cfg, err := InitializeConfig(benchmarkCmd)
+	if err != nil {
 		return err
 	}
 
@@ -79,7 +79,7 @@
 
 	t := time.Now()
 	for i := 0; i < benchmarkTimes; i++ {
-		if err = resetAndBuildSites(false); err != nil {
+		if err = resetAndBuildSites(cfg, false); err != nil {
 			return err
 		}
 	}
--- a/commands/convert.go
+++ b/commands/convert.go
@@ -80,13 +80,13 @@
 	convertCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
 }
 
-func convertContents(mark rune) (err error) {
-	if err := InitializeConfig(); err != nil {
+func convertContents(mark rune) error {
+	cfg, err := InitializeConfig()
+	if err != nil {
 		return err
 	}
 
-	h, err := hugolib.NewHugoSitesFromConfiguration()
-
+	h, err := hugolib.NewHugoSitesFromConfiguration(cfg)
 	if err != nil {
 		return err
 	}
@@ -108,7 +108,7 @@
 	jww.FEEDBACK.Println("processing", len(site.Source.Files()), "content files")
 	for _, file := range site.Source.Files() {
 		jww.INFO.Println("Attempting to convert", file.LogicalName())
-		page, err := hugolib.NewPage(file.LogicalName())
+		page, err := site.NewPage(file.LogicalName())
 		if err != nil {
 			return err
 		}
@@ -157,5 +157,5 @@
 			}
 		}
 	}
-	return
+	return nil
 }
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -18,6 +18,7 @@
 import (
 	"fmt"
 	"io/ioutil"
+	"log"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -113,16 +114,17 @@
 
 Complete documentation is available at http://gohugo.io/.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
-		if err := InitializeConfig(); err != nil {
+		cfg, err := InitializeConfig()
+		if err != nil {
 			return err
 		}
 
 		if buildWatch {
 			viper.Set("disableLiveReload", true)
-			watchConfig()
+			watchConfig(cfg)
 		}
 
-		return build()
+		return build(cfg)
 	},
 }
 
@@ -266,9 +268,12 @@
 }
 
 // InitializeConfig initializes a config file with sensible default configuration flags.
-func InitializeConfig(subCmdVs ...*cobra.Command) error {
+func InitializeConfig(subCmdVs ...*cobra.Command) (hugolib.DepsCfg, error) {
+
+	var cfg hugolib.DepsCfg
+
 	if err := hugolib.LoadGlobalConfig(source, cfgFile); err != nil {
-		return err
+		return cfg, err
 	}
 
 	for _, cmdV := range append([]*cobra.Command{hugoCmdV}, subCmdVs...) {
@@ -333,37 +338,6 @@
 		viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", hugofs.Source()))
 	}
 
-	logFile := ioutil.Discard
-
-	if verboseLog || logging || (viper.IsSet("logFile") && viper.GetString("logFile") != "") {
-
-		var err error
-		if viper.IsSet("logFile") && viper.GetString("logFile") != "" {
-			path := viper.GetString("logFile")
-			logFile, err = os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
-			if err != nil {
-				return newSystemError("Failed to open log file:", path, err)
-			}
-		} else {
-			logFile, err = ioutil.TempFile(os.TempDir(), "hugo")
-			if err != nil {
-				return newSystemError(err)
-			}
-		}
-	}
-
-	jww.SetLogOutput(logFile)
-
-	if quiet {
-		jww.SetStdoutThreshold(jww.LevelError)
-	} else if viper.GetBool("verbose") {
-		jww.SetStdoutThreshold(jww.LevelInfo)
-	}
-
-	if verboseLog {
-		jww.SetLogThreshold(jww.LevelInfo)
-	}
-
 	jww.INFO.Println("Using config file:", viper.ConfigFileUsed())
 
 	// Init file systems. This may be changed at a later point.
@@ -372,7 +346,7 @@
 	themeDir := helpers.GetThemeDir()
 	if themeDir != "" {
 		if _, err := hugofs.Source().Stat(themeDir); os.IsNotExist(err) {
-			return newSystemError("Unable to find theme Directory:", themeDir)
+			return cfg, newSystemError("Unable to find theme Directory:", themeDir)
 		}
 	}
 
@@ -383,10 +357,51 @@
 			helpers.HugoReleaseVersion(), minVersion)
 	}
 
-	return nil
+	logger, err := createLogger()
+	if err != nil {
+		return cfg, err
+	}
 
+	cfg.Logger = logger
+
+	return cfg, nil
+
 }
 
+func createLogger() (*jww.Notepad, error) {
+	var (
+		logHandle       = ioutil.Discard
+		outHandle       = os.Stdout
+		stdoutThreshold = jww.LevelError
+		logThreshold    = jww.LevelWarn
+	)
+
+	if verboseLog || logging || (viper.IsSet("logFile") && viper.GetString("logFile") != "") {
+
+		var err error
+		if viper.IsSet("logFile") && viper.GetString("logFile") != "" {
+			path := viper.GetString("logFile")
+			logHandle, err = os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
+			if err != nil {
+				return nil, newSystemError("Failed to open log file:", path, err)
+			}
+		} else {
+			logHandle, err = ioutil.TempFile(os.TempDir(), "hugo")
+			if err != nil {
+				return nil, newSystemError(err)
+			}
+		}
+	} else if !quiet && viper.GetBool("verbose") {
+		stdoutThreshold = jww.LevelInfo
+	}
+
+	if verboseLog {
+		logThreshold = jww.LevelInfo
+	}
+
+	return jww.NewNotepad(stdoutThreshold, logThreshold, outHandle, logHandle, "", log.Ldate|log.Ltime), nil
+}
+
 func initializeFlags(cmd *cobra.Command) {
 	persFlagKeys := []string{"verbose", "logFile"}
 	flagKeys := []string{
@@ -432,12 +447,12 @@
 	return flag.Changed
 }
 
-func watchConfig() {
+func watchConfig(cfg hugolib.DepsCfg) {
 	viper.WatchConfig()
 	viper.OnConfigChange(func(e fsnotify.Event) {
 		jww.FEEDBACK.Println("Config file changed:", e.Name)
 		// Force a full rebuild
-		utils.CheckErr(recreateAndBuildSites(true))
+		utils.CheckErr(recreateAndBuildSites(cfg, true))
 		if !viper.GetBool("disableLiveReload") {
 			// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
 			livereload.ForceRefresh()
@@ -445,7 +460,7 @@
 	})
 }
 
-func build(watches ...bool) error {
+func build(cfg hugolib.DepsCfg, watches ...bool) error {
 	// Hugo writes the output to memory instead of the disk.
 	// This is only used for benchmark testing. Cause the content is only visible
 	// in memory.
@@ -462,7 +477,7 @@
 	if len(watches) > 0 && watches[0] {
 		watch = true
 	}
-	if err := buildSites(buildWatch || watch); err != nil {
+	if err := buildSites(cfg, buildWatch || watch); err != nil {
 		return fmt.Errorf("Error building site: %s", err)
 	}
 
@@ -469,7 +484,7 @@
 	if buildWatch {
 		jww.FEEDBACK.Println("Watching for changes in", helpers.AbsPathify(viper.GetString("contentDir")))
 		jww.FEEDBACK.Println("Press Ctrl+C to stop")
-		utils.CheckErr(NewWatcher(0))
+		utils.CheckErr(newWatcher(cfg, 0))
 	}
 
 	return nil
@@ -644,8 +659,8 @@
 	return a
 }
 
-func recreateAndBuildSites(watching bool) (err error) {
-	if err := initSites(); err != nil {
+func recreateAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
+	if err := initSites(cfg); err != nil {
 		return err
 	}
 	if !quiet {
@@ -654,8 +669,8 @@
 	return Hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true, Watching: watching, PrintStats: !quiet})
 }
 
-func resetAndBuildSites(watching bool) (err error) {
-	if err := initSites(); err != nil {
+func resetAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
+	if err := initSites(cfg); err != nil {
 		return err
 	}
 	if !quiet {
@@ -664,12 +679,12 @@
 	return Hugo.Build(hugolib.BuildCfg{ResetState: true, Watching: watching, PrintStats: !quiet})
 }
 
-func initSites() error {
+func initSites(cfg hugolib.DepsCfg) error {
 	if Hugo != nil {
 		return nil
 	}
 
-	h, err := hugolib.NewHugoSitesFromConfiguration()
+	h, err := hugolib.NewHugoSitesFromConfiguration(cfg)
 
 	if err != nil {
 		return err
@@ -679,8 +694,8 @@
 	return nil
 }
 
-func buildSites(watching bool) (err error) {
-	if err := initSites(); err != nil {
+func buildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
+	if err := initSites(cfg); err != nil {
 		return err
 	}
 	if !quiet {
@@ -689,15 +704,15 @@
 	return Hugo.Build(hugolib.BuildCfg{Watching: watching, PrintStats: !quiet})
 }
 
-func rebuildSites(events []fsnotify.Event) error {
-	if err := initSites(); err != nil {
+func rebuildSites(cfg hugolib.DepsCfg, events []fsnotify.Event) error {
+	if err := initSites(cfg); err != nil {
 		return err
 	}
 	return Hugo.Build(hugolib.BuildCfg{PrintStats: !quiet, Watching: true}, events...)
 }
 
-// NewWatcher creates a new watcher to watch filesystem events.
-func NewWatcher(port int) error {
+// newWatcher creates a new watcher to watch filesystem events.
+func newWatcher(cfg hugolib.DepsCfg, port int) error {
 	if runtime.GOOS == "darwin" {
 		tweakLimit()
 	}
@@ -910,7 +925,7 @@
 					const layout = "2006-01-02 15:04 -0700"
 					jww.FEEDBACK.Println(time.Now().Format(layout))
 
-					rebuildSites(dynamicEvents)
+					rebuildSites(cfg, dynamicEvents)
 
 					if !buildWatch && !viper.GetBool("disableLiveReload") {
 						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
--- a/commands/import_jekyll.go
+++ b/commands/import_jekyll.go
@@ -83,8 +83,9 @@
 	}
 
 	forceImport, _ := cmd.Flags().GetBool("force")
-	if err := createSiteFromJekyll(jekyllRoot, targetDir, forceImport); err != nil {
-		return newUserError(err)
+	site, err := createSiteFromJekyll(jekyllRoot, targetDir, forceImport)
+	if err != nil {
+		return err
 	}
 
 	jww.FEEDBACK.Println("Importing...")
@@ -118,7 +119,7 @@
 		}
 
 		fileCount++
-		return convertJekyllPost(path, relPath, targetDir, draft)
+		return convertJekyllPost(site, path, relPath, targetDir, draft)
 	}
 
 	err = helpers.SymbolicWalk(hugofs.Os(), jekyllRoot, callback)
@@ -135,17 +136,17 @@
 }
 
 // TODO: Consider calling doNewSite() instead?
-func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) error {
+func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Site, error) {
 	fs := hugofs.Source()
 	if exists, _ := helpers.Exists(targetDir, fs); exists {
 		if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
-			return errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
+			return nil, errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
 		}
 
 		isEmpty, _ := helpers.IsEmpty(targetDir, fs)
 
 		if !isEmpty && !force {
-			return errors.New("Target path \"" + targetDir + "\" already exists and is not empty")
+			return nil, errors.New("Target path \"" + targetDir + "\" already exists and is not empty")
 		}
 	}
 
@@ -166,7 +167,7 @@
 		}
 	}
 	if !hasPostsOrDrafts {
-		return errors.New("Your Jekyll root contains neither posts nor drafts, aborting.")
+		return nil, errors.New("Your Jekyll root contains neither posts nor drafts, aborting.")
 	}
 
 	mkdir(targetDir, "layouts")
@@ -179,8 +180,9 @@
 	createConfigFromJekyll(targetDir, "yaml", jekyllConfig)
 
 	copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))
+	site := hugolib.NewSiteDefaultLang()
 
-	return nil
+	return site, nil
 }
 
 func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
@@ -379,7 +381,7 @@
 	return postDate, postName, nil
 }
 
-func convertJekyllPost(path, relPath, targetDir string, draft bool) error {
+func convertJekyllPost(s *hugolib.Site, path, relPath, targetDir string, draft bool) error {
 	jww.TRACE.Println("Converting", path)
 
 	filename := filepath.Base(path)
@@ -422,7 +424,7 @@
 	jww.TRACE.Println(newmetadata)
 	content := convertJekyllContent(newmetadata, string(psr.Content()))
 
-	page, err := hugolib.NewPage(filename)
+	page, err := s.NewPage(filename)
 	if err != nil {
 		jww.ERROR.Println("New page error", filename)
 		return err
--- a/commands/list.go
+++ b/commands/list.go
@@ -45,13 +45,14 @@
 	Long:  `List all of the drafts in your content directory.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
 
-		if err := InitializeConfig(); err != nil {
+		cfg, err := InitializeConfig()
+		if err != nil {
 			return err
 		}
 
 		viper.Set("buildDrafts", true)
 
-		sites, err := hugolib.NewHugoSitesFromConfiguration()
+		sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
 
 		if err != nil {
 			return newSystemError("Error creating sites", err)
@@ -80,13 +81,14 @@
 posted in the future.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
 
-		if err := InitializeConfig(); err != nil {
+		cfg, err := InitializeConfig()
+		if err != nil {
 			return err
 		}
 
 		viper.Set("buildFuture", true)
 
-		sites, err := hugolib.NewHugoSitesFromConfiguration()
+		sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
 
 		if err != nil {
 			return newSystemError("Error creating sites", err)
@@ -115,13 +117,14 @@
 expired.`,
 	RunE: func(cmd *cobra.Command, args []string) error {
 
-		if err := InitializeConfig(); err != nil {
+		cfg, err := InitializeConfig()
+		if err != nil {
 			return err
 		}
 
 		viper.Set("buildExpired", true)
 
-		sites, err := hugolib.NewHugoSitesFromConfiguration()
+		sites, err := hugolib.NewHugoSitesFromConfiguration(cfg)
 
 		if err != nil {
 			return newSystemError("Error creating sites", err)
--- a/commands/list_config.go
+++ b/commands/list_config.go
@@ -33,7 +33,7 @@
 }
 
 func config(cmd *cobra.Command, args []string) error {
-	if err := InitializeConfig(configCmd); err != nil {
+	if _, err := InitializeConfig(configCmd); err != nil {
 		return err
 	}
 
--- a/commands/new.go
+++ b/commands/new.go
@@ -84,7 +84,7 @@
 
 // NewContent adds new content to a Hugo site.
 func NewContent(cmd *cobra.Command, args []string) error {
-	if err := InitializeConfig(); err != nil {
+	if _, err := InitializeConfig(); err != nil {
 		return err
 	}
 
@@ -195,7 +195,7 @@
 
 // NewTheme creates a new Hugo theme.
 func NewTheme(cmd *cobra.Command, args []string) error {
-	if err := InitializeConfig(); err != nil {
+	if _, err := InitializeConfig(); err != nil {
 		return err
 	}
 
--- a/commands/server.go
+++ b/commands/server.go
@@ -104,7 +104,8 @@
 }
 
 func server(cmd *cobra.Command, args []string) error {
-	if err := InitializeConfig(serverCmd); err != nil {
+	cfg, err := InitializeConfig()
+	if err != nil {
 		return err
 	}
 
@@ -118,7 +119,7 @@
 
 	if viper.GetBool("watch") {
 		serverWatch = true
-		watchConfig()
+		watchConfig(cfg)
 	}
 
 	l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(serverPort)))
@@ -161,7 +162,7 @@
 		viper.Set("publishDir", "/")
 	}
 
-	if err := build(serverWatch); err != nil {
+	if err := build(cfg, serverWatch); err != nil {
 		return err
 	}
 
@@ -176,7 +177,7 @@
 		rootWatchDirs := strings.Join(helpers.UniqueStrings(helpers.ExtractRootPaths(watchDirs)), ",")
 
 		jww.FEEDBACK.Printf("Watching for changes in %s%s{%s}\n", baseWatchDir, helpers.FilePathSeparator, rootWatchDirs)
-		err := NewWatcher(serverPort)
+		err := newWatcher(cfg, serverPort)
 
 		if err != nil {
 			return err
--- a/commands/undraft.go
+++ b/commands/undraft.go
@@ -37,7 +37,7 @@
 // to false and setting its publish date to now. If the specified content is
 // not a draft, it will log an error.
 func Undraft(cmd *cobra.Command, args []string) error {
-	if err := InitializeConfig(); err != nil {
+	if _, err := InitializeConfig(); err != nil {
 		return err
 	}
 
--- a/create/content.go
+++ b/create/content.go
@@ -62,7 +62,9 @@
 		return err
 	}
 
-	page, err := hugolib.NewPage(name)
+	site := hugolib.NewSiteDefaultLang()
+
+	page, err := site.NewPage(name)
 	if err != nil {
 		return err
 	}
--- a/hugolib/alias_test.go
+++ b/hugolib/alias_test.go
@@ -33,7 +33,7 @@
 	writeSource(t, filepath.Join("content", "page.md"), pageWithAlias)
 	writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
 
-	if err := buildAndRenderSite(newSiteDefaultLang()); err != nil {
+	if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
 	}
 
@@ -49,7 +49,7 @@
 	writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
 	writeSource(t, filepath.Join("layouts", "alias.html"), aliasTemplate)
 
-	if err := buildAndRenderSite(newSiteDefaultLang()); err != nil {
+	if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
 	}
 
--- a/hugolib/case_insensitive_test.go
+++ b/hugolib/case_insensitive_test.go
@@ -168,7 +168,7 @@
 		t.Fatalf("Failed to load config: %s", err)
 	}
 
-	sites, err := NewHugoSitesFromConfiguration()
+	sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
 
 	if err != nil {
 		t.Fatalf("Failed to create sites: %s", err)
@@ -267,7 +267,7 @@
 		t.Fatalf("Failed to load config: %s", err)
 	}
 
-	sites, err := NewHugoSitesFromConfiguration()
+	sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
 
 	if err != nil {
 		t.Fatalf("Failed to create sites: %s", err)
--- a/hugolib/datafiles_test.go
+++ b/hugolib/datafiles_test.go
@@ -89,7 +89,7 @@
 	sources := []source.ByteSource{
 		{Name: filepath.FromSlash("test.roml"), Content: []byte("boo")},
 	}
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	err := s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}})
 	if err != nil {
 		t.Fatalf("Should not return an error")
@@ -97,7 +97,7 @@
 }
 
 func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) {
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	err := s.loadData(sources)
 	if err != nil {
 		t.Fatalf("Error loading data: %s", err)
--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -24,12 +24,18 @@
 	"strings"
 	"testing"
 
+	"io/ioutil"
+	"log"
+
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/tpl"
+	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/require"
 )
 
+var logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+
 const (
 	baseURL = "http://foo/bar"
 )
@@ -100,7 +106,7 @@
 			"(?s)^\n<div class=\"highlight\" style=\"background: #f0f0f0\"><pre style=\"line-height: 125%\">.*?void</span>.*?do</span>.*?().*?</pre></div>\n$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -144,7 +150,7 @@
 			"(?s)^\n<figure >.*?<img src=\"/img/hugo-logo.png\" />.*?<figcaption>.*?<p>.*?<a href=\"/img/hugo-logo.png\">.*?Hugo logo.*?</a>.*?</p>.*?</figcaption>.*?</figure>\n$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -169,7 +175,7 @@
 			"(?s)^<script async class='speakerdeck-embed' data-id='4e8126e72d853c0060001f97'.*?>.*?</script>$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -204,7 +210,7 @@
 			"(?s)^\n<div class=\"video\">.*?<iframe src=\"//www.youtube.com/embed/w7Ft2ymGmfc\\?autoplay=1\".*?allowfullscreen frameborder=\"0\">.*?</iframe>.*?</div>$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -239,7 +245,7 @@
 			"(?s)^<div class=\"video\">.*?<iframe src=\"//player.vimeo.com/video/146022717\" webkitallowfullscreen mozallowfullscreen allowfullscreen>.*?</iframe>.*?</div>$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -268,7 +274,7 @@
 			"(?s)^<script src=\"//gist.github.com/spf13/7896402.js\\?file=img.html\"></script>$",
 		},
 	} {
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		p, _ := pageFromString(simplePage, "simple.md")
 		output, err := HandleShortcodes(this.in, p, templ)
 
@@ -307,7 +313,7 @@
 			},
 		}
 
-		templ := tpl.New()
+		templ := tpl.New(logger)
 		templ.Lookup("").Funcs(tweetFuncMap)
 
 		p, _ := pageFromString(simplePage, "simple.md")
@@ -359,7 +365,7 @@
 t+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
-jOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
+jOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BMokmydjG-M/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A photo posted by Instagram (@instagram)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-11-10T15:02:28+00:00">Nov 10, 2016 at 7:02am PST</time></p></div></blockquote>
 <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>`,
 		},
--- a/hugolib/handler_page.go
+++ b/hugolib/handler_page.go
@@ -34,7 +34,7 @@
 type basicPageHandler Handle
 
 func (b basicPageHandler) Read(f *source.File, s *Site) HandledResult {
-	page, err := NewPage(f.Path())
+	page, err := s.NewPage(f.Path())
 
 	if err != nil {
 		return HandledResult{file: f, err: err}
@@ -43,8 +43,6 @@
 	if _, err := page.ReadFrom(f.Contents); err != nil {
 		return HandledResult{file: f, err: err}
 	}
-
-	page.Site = &s.Info
 
 	return HandledResult{file: f, page: page, err: err}
 }
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -15,6 +15,9 @@
 
 import (
 	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
 	"strings"
 	"sync"
 
@@ -35,11 +38,38 @@
 	runMode runmode
 
 	multilingual *Multilingual
+
+	*deps
 }
 
+// deps holds dependencies used by many.
+// TODO(bep) globals a better name.
+// There will be normally be only one instance of deps in play
+// at a given time.
+type deps struct {
+	// The logger to use.
+	log *jww.Notepad
+
+	// TODO(bep) next in line: Viper, hugofs, template
+}
+
+func newDeps(cfg DepsCfg) *deps {
+	logger := cfg.Logger
+
+	if logger == nil {
+		// TODO(bep) globals default log level
+		//logger = jww.NewNotepad(jww.LevelError, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+		logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+	}
+
+	return &deps{
+		log: logger,
+	}
+}
+
 // NewHugoSites creates a new collection of sites given the input sites, building
 // a language configuration based on those.
-func newHugoSites(sites ...*Site) (*HugoSites, error) {
+func newHugoSites(cfg DepsCfg, sites ...*Site) (*HugoSites, error) {
 	langConfig, err := newMultiLingualFromSites(sites...)
 
 	if err != nil {
@@ -46,21 +76,26 @@
 		return nil, err
 	}
 
-	h := &HugoSites{multilingual: langConfig, Sites: sites}
+	h := &HugoSites{
+		deps:         newDeps(cfg),
+		multilingual: langConfig,
+		Sites:        sites}
 
 	for _, s := range sites {
 		s.owner = h
+		s.deps = h.deps
 	}
 	return h, nil
 }
 
 // NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
-func NewHugoSitesFromConfiguration() (*HugoSites, error) {
+// TODO(bep) globals rename this when all the globals are gone.
+func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) {
 	sites, err := createSitesFromConfig()
 	if err != nil {
 		return nil, err
 	}
-	return newHugoSites(sites...)
+	return newHugoSites(cfg, sites...)
 }
 
 func createSitesFromConfig() ([]*Site, error) {
@@ -150,6 +185,15 @@
 	whatChanged *whatChanged
 }
 
+// DepsCfg contains configuration options that can be used to configure Hugo
+// on a global level, i.e. logging etc.
+// Nil values will be given default values.
+type DepsCfg struct {
+
+	// The Logger to use.
+	Logger *jww.Notepad
+}
+
 func (h *HugoSites) renderCrossSitesArtifacts() error {
 
 	if !h.multilingual.enabled() {
@@ -498,7 +542,7 @@
 	if s.PageCollections == nil {
 		s.PageCollections = newPageCollections()
 	}
-	sites, err := newHugoSites(s)
+	sites, err := newHugoSites(DepsCfg{}, s)
 	if err != nil {
 		return err
 	}
@@ -522,12 +566,15 @@
 	if len(languages) == 0 {
 		panic("Must provide at least one language")
 	}
+
+	cfg := DepsCfg{}
+
 	first := &Site{
 		Source:   &source.InMemorySource{ByteSource: input},
 		Language: languages[0],
 	}
 	if len(languages) == 1 {
-		return newHugoSites(first)
+		return newHugoSites(cfg, first)
 	}
 
 	sites := make([]*Site, len(languages))
@@ -536,7 +583,7 @@
 		sites[i] = &Site{Language: languages[i]}
 	}
 
-	return newHugoSites(sites...)
+	return newHugoSites(cfg, sites...)
 
 }
 
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -20,7 +20,6 @@
 
 	"github.com/fsnotify/fsnotify"
 	"github.com/spf13/hugo/helpers"
-	jww "github.com/spf13/jwalterweatherman"
 )
 
 // Build builds all sites. If filesystem events are provided,
@@ -60,7 +59,7 @@
 	}
 
 	if config.PrintStats {
-		jww.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
+		h.log.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
 	}
 
 	return nil
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -48,7 +48,6 @@
 }
 
 func TestMultiSitesMainLangInRoot(t *testing.T) {
-	//jww.SetStdoutThreshold(jww.LevelDebug)
 
 	for _, b := range []bool{true, false} {
 		doTestMultiSitesMainLangInRoot(t, b)
@@ -212,7 +211,7 @@
 	// Add some data
 	writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
 
-	sites, err := NewHugoSitesFromConfiguration()
+	sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
 
 	if err != nil {
 		t.Fatalf("Failed to create sites: %s", err)
@@ -1227,7 +1226,7 @@
 	// Add some data
 	writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
 
-	sites, err := NewHugoSitesFromConfiguration()
+	sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
 
 	if err != nil {
 		t.Fatalf("Failed to create sites: %s", err)
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -689,6 +689,7 @@
 	hugofs.InitMemFs()
 
 	return &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: pageSources},
 		Language: helpers.NewDefaultLanguage(),
 	}
--- a/hugolib/node_as_page_test.go
+++ b/hugolib/node_as_page_test.go
@@ -20,7 +20,6 @@
 	"testing"
 	"time"
 
-	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/require"
 )
@@ -41,8 +40,6 @@
 }
 
 func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
-	//jww.SetStdoutThreshold(jww.LevelDebug)
-	jww.SetStdoutThreshold(jww.LevelFatal)
 
 	/* Will have to decide what to name the node content files, but:
 
@@ -68,7 +65,7 @@
 	viper.Set("title", "Hugo Rocks")
 	viper.Set("rssURI", "customrss.xml")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -188,9 +185,6 @@
 }
 
 func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
-	//jww.SetStdoutThreshold(jww.LevelDebug)
-	jww.SetStdoutThreshold(jww.LevelFatal)
-
 	testCommonResetState()
 
 	writeLayoutsForNodeAsPageTests(t)
@@ -201,7 +195,7 @@
 	viper.Set("title", "Hugo Rocks!")
 	viper.Set("rssURI", "customrss.xml")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -321,7 +315,7 @@
 		t.Fatalf("Failed to load config: %s", err)
 	}
 
-	sites, err := NewHugoSitesFromConfiguration()
+	sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
 
 	if err != nil {
 		t.Fatalf("Failed to create sites: %s", err)
@@ -430,7 +424,7 @@
 	viper.Set("paginate", 1)
 	viper.Set("title", "Hugo Rocks!")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -474,7 +468,7 @@
 	viper.Set("paginate", 1)
 	viper.Set("title", "Hugo Rocks!")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -503,7 +497,7 @@
 	viper.Set("baseURL", "http://base/")
 	viper.Set("title", "Hugo Rocks!")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -528,7 +522,7 @@
 	viper.Set("paginate", 1)
 	viper.Set("title", "Hugo Rocks!")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
@@ -556,7 +550,7 @@
 	viper.Set("title", "Hugo Rocks!")
 	viper.Set("baseURL", "http://bep.is/base/")
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -41,7 +41,6 @@
 	"github.com/spf13/hugo/hugofs"
 	"github.com/spf13/hugo/source"
 	"github.com/spf13/hugo/tpl"
-	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 )
 
@@ -173,7 +172,7 @@
 	// isn't accomanied by one.
 	sections []string
 
-	site *Site
+	s *Site
 
 	// Pulled over from old Node. TODO(bep) reorg and group (embed)
 
@@ -538,7 +537,7 @@
 		p.renderingConfig = helpers.NewBlackfriday(p.Language())
 
 		if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil {
-			jww.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
+			p.s.log.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
 		}
 
 	})
@@ -546,7 +545,7 @@
 	return p.renderingConfig
 }
 
-func newPage(filename string) *Page {
+func (s *Site) newPage(filename string) *Page {
 	page := Page{
 		pageInit:    &pageInit{},
 		Kind:        kindFromFilename(filename),
@@ -558,7 +557,7 @@
 		sections:     sectionsFromFilename(filename),
 	}
 
-	jww.DEBUG.Println("Reading from", page.File.Path())
+	s.log.DEBUG.Println("Reading from", page.File.Path())
 	return &page
 }
 
@@ -589,16 +588,16 @@
 
 	switch p.Kind {
 	case KindHome:
-		return p.site.appendThemeTemplates([]string{"index.html", "_default/list.html"})
+		return p.s.appendThemeTemplates([]string{"index.html", "_default/list.html"})
 	case KindSection:
 		section := p.sections[0]
-		return p.site.appendThemeTemplates([]string{"section/" + section + ".html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"})
+		return p.s.appendThemeTemplates([]string{"section/" + section + ".html", "_default/section.html", "_default/list.html", "indexes/" + section + ".html", "_default/indexes.html"})
 	case KindTaxonomy:
-		singular := p.site.taxonomiesPluralSingular[p.sections[0]]
-		return p.site.appendThemeTemplates([]string{"taxonomy/" + singular + ".html", "indexes/" + singular + ".html", "_default/taxonomy.html", "_default/list.html"})
+		singular := p.s.taxonomiesPluralSingular[p.sections[0]]
+		return p.s.appendThemeTemplates([]string{"taxonomy/" + singular + ".html", "indexes/" + singular + ".html", "_default/taxonomy.html", "_default/list.html"})
 	case KindTaxonomyTerm:
-		singular := p.site.taxonomiesPluralSingular[p.sections[0]]
-		return p.site.appendThemeTemplates([]string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"})
+		singular := p.s.taxonomiesPluralSingular[p.sections[0]]
+		return p.s.appendThemeTemplates([]string{"taxonomy/" + singular + ".terms.html", "_default/terms.html", "indexes/indexes.html"})
 	}
 
 	// Regular Page handled below
@@ -628,7 +627,7 @@
 		section := p.sections[0]
 		return []string{"section/" + section + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
 	case KindTaxonomy:
-		singular := p.site.taxonomiesPluralSingular[p.sections[0]]
+		singular := p.s.taxonomiesPluralSingular[p.sections[0]]
 		return []string{"taxonomy/" + singular + ".rss.xml", "_default/rss.xml", "rss.xml", "_internal/_default/rss.xml"}
 	case KindTaxonomyTerm:
 	// No RSS for taxonomy terms
@@ -659,8 +658,8 @@
 	return
 }
 
-func NewPageFrom(buf io.Reader, name string) (*Page, error) {
-	p, err := NewPage(name)
+func (s *Site) NewPageFrom(buf io.Reader, name string) (*Page, error) {
+	p, err := s.NewPage(name)
 	if err != nil {
 		return p, err
 	}
@@ -669,13 +668,15 @@
 	return p, err
 }
 
-func NewPage(name string) (*Page, error) {
+func (s *Site) NewPage(name string) (*Page, error) {
 	if len(name) == 0 {
 		return nil, errors.New("Zero length page name")
 	}
 
 	// Create new page
-	p := newPage(name)
+	p := s.newPage(name)
+	p.s = s
+	p.Site = &s.Info
 
 	return p, nil
 }
@@ -683,7 +684,7 @@
 func (p *Page) ReadFrom(buf io.Reader) (int64, error) {
 	// Parse for metadata & body
 	if err := p.parse(buf); err != nil {
-		jww.ERROR.Print(err)
+		p.s.log.ERROR.Print(err)
 		return 0, err
 	}
 
@@ -738,7 +739,7 @@
 	p.pageURLInit.Do(func() {
 		u, err := p.createPermalink()
 		if err != nil {
-			jww.ERROR.Printf("Failed to create permalink for page %q: %s", p.FullFilePath(), err)
+			p.s.log.ERROR.Printf("Failed to create permalink for page %q: %s", p.FullFilePath(), err)
 			p.permalink = new(url.URL)
 			return
 		}
@@ -953,22 +954,22 @@
 		case "date":
 			p.Date, err = cast.ToTimeE(v)
 			if err != nil {
-				jww.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path())
+				p.s.log.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path())
 			}
 		case "lastmod":
 			p.Lastmod, err = cast.ToTimeE(v)
 			if err != nil {
-				jww.ERROR.Printf("Failed to parse lastmod '%v' in page %s", v, p.File.Path())
+				p.s.log.ERROR.Printf("Failed to parse lastmod '%v' in page %s", v, p.File.Path())
 			}
 		case "publishdate", "pubdate":
 			p.PublishDate, err = cast.ToTimeE(v)
 			if err != nil {
-				jww.ERROR.Printf("Failed to parse publishdate '%v' in page %s", v, p.File.Path())
+				p.s.log.ERROR.Printf("Failed to parse publishdate '%v' in page %s", v, p.File.Path())
 			}
 		case "expirydate", "unpublishdate":
 			p.ExpiryDate, err = cast.ToTimeE(v)
 			if err != nil {
-				jww.ERROR.Printf("Failed to parse expirydate '%v' in page %s", v, p.File.Path())
+				p.s.log.ERROR.Printf("Failed to parse expirydate '%v' in page %s", v, p.File.Path())
 			}
 		case "draft":
 			draft = new(bool)
@@ -1040,7 +1041,7 @@
 
 	if draft != nil && published != nil {
 		p.Draft = *draft
-		jww.ERROR.Printf("page %s has both draft and published settings in its frontmatter. Using draft.", p.File.Path())
+		p.s.log.ERROR.Printf("page %s has both draft and published settings in its frontmatter. Using draft.", p.File.Path())
 		return ErrHasDraftAndPublished
 	} else if draft != nil {
 		p.Draft = *draft
@@ -1109,7 +1110,7 @@
 		return v
 	}
 
-	jww.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
+	p.s.log.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
 	return nil
 }
 
@@ -1251,16 +1252,16 @@
 			menus, err := cast.ToStringMapE(ms)
 
 			if err != nil {
-				jww.ERROR.Printf("unable to process menus for %q\n", p.Title)
+				p.s.log.ERROR.Printf("unable to process menus for %q\n", p.Title)
 			}
 
 			for name, menu := range menus {
 				menuEntry := MenuEntry{Name: p.LinkTitle(), URL: link, Weight: p.Weight, Menu: name}
 				if menu != nil {
-					jww.DEBUG.Printf("found menu: %q, in %q\n", name, p.Title)
+					p.s.log.DEBUG.Printf("found menu: %q, in %q\n", name, p.Title)
 					ime, err := cast.ToStringMapE(menu)
 					if err != nil {
-						jww.ERROR.Printf("unable to process menus for %q: %s", p.Title, err)
+						p.s.log.ERROR.Printf("unable to process menus for %q: %s", p.Title, err)
 					}
 
 					menuEntry.marshallMap(ime)
@@ -1311,8 +1312,8 @@
 	meta, err := psr.Metadata()
 	if meta != nil {
 		if err != nil {
-			jww.ERROR.Printf("Error parsing page meta data for %s", p.File.Path())
-			jww.ERROR.Println(err)
+			p.s.log.ERROR.Printf("Error parsing page meta data for %s", p.File.Path())
+			p.s.log.ERROR.Println(err)
 			return err
 		}
 		if err = p.update(meta); err != nil {
@@ -1381,7 +1382,7 @@
 	if !filepath.IsAbs(inpath) {
 		inpath = helpers.AbsPathify(inpath)
 	}
-	jww.INFO.Println("creating", inpath)
+	p.s.log.INFO.Println("creating", inpath)
 
 	if safe {
 		err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
@@ -1707,7 +1708,7 @@
 
 		if language == nil {
 			// It can be a file named stefano.chiodino.md.
-			jww.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
+			p.s.log.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
 			language = ml.DefaultLang
 		}
 
@@ -1798,6 +1799,6 @@
 		p.URLPath.URL = "/" + path.Join(p.sections...) + "/"
 	}
 
-	p.site = s
+	p.s = s
 
 }
--- a/hugolib/pageGroup_test.go
+++ b/hugolib/pageGroup_test.go
@@ -40,7 +40,7 @@
 func preparePageGroupTestPages(t *testing.T) Pages {
 	var pages Pages
 	for _, s := range pageGroupTestSources {
-		p, err := NewPage(filepath.FromSlash(s.path))
+		p, err := pageTestSite.NewPage(filepath.FromSlash(s.path))
 		if err != nil {
 			t.Fatalf("failed to prepare test page %s", s.path)
 		}
@@ -237,7 +237,7 @@
 func TestGroupByParamCalledWithCapitalLetterString(t *testing.T) {
 	testStr := "TestString"
 	f := "/section1/test_capital.md"
-	p, err := NewPage(filepath.FromSlash(f))
+	p, err := pageTestSite.NewPage(filepath.FromSlash(f))
 	if err != nil {
 		t.Fatalf("failed to prepare test page %s", f)
 	}
--- a/hugolib/page_taxonomy_test.go
+++ b/hugolib/page_taxonomy_test.go
@@ -64,7 +64,7 @@
 		pageYamlWithTaxonomiesC,
 	} {
 
-		p, _ := NewPage("page/with/taxonomy")
+		p, _ := pageTestSite.NewPage("page/with/taxonomy")
 		_, err := p.ReadFrom(strings.NewReader(test))
 		if err != nil {
 			t.Fatalf("Failed parsing %q: %s", test, err)
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -465,6 +465,8 @@
 Hi.
 `
 
+var pageTestSite = NewSiteDefaultLang()
+
 func checkError(t *testing.T, err error, expected string) {
 	if err == nil {
 		t.Fatalf("err is nil.  Expected: %s", expected)
@@ -476,7 +478,7 @@
 
 func TestDegenerateEmptyPageZeroLengthName(t *testing.T) {
 
-	_, err := NewPage("")
+	_, err := pageTestSite.NewPage("")
 	if err == nil {
 		t.Fatalf("A zero length page name must return an error")
 	}
@@ -485,7 +487,7 @@
 }
 
 func TestDegenerateEmptyPage(t *testing.T) {
-	_, err := NewPageFrom(strings.NewReader(emptyPage), "test")
+	_, err := pageTestSite.NewPageFrom(strings.NewReader(emptyPage), "test")
 	if err != nil {
 		t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.")
 	}
@@ -626,7 +628,7 @@
 			writeSource(t, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1])
 		}
 
-		s := newSiteDefaultLang()
+		s := NewSiteDefaultLang()
 
 		if err := buildSiteSkipRender(s); err != nil {
 			t.Fatalf("Failed to build site: %s", err)
@@ -958,7 +960,7 @@
 	}
 
 	for _, test := range tests {
-		p, _ := NewPage("page")
+		p, _ := pageTestSite.NewPage("page")
 		if _, err := p.ReadFrom(strings.NewReader(test.r)); err != nil {
 			t.Errorf("Unable to parse page: %s", err)
 		}
@@ -974,7 +976,7 @@
 	}
 	for _, test := range tests {
 
-		p, _ := NewPage("invalid/front/matter/short/delim")
+		p, _ := pageTestSite.NewPage("invalid/front/matter/short/delim")
 		_, err := p.ReadFrom(strings.NewReader(test.r))
 		checkError(t, err, test.err)
 	}
@@ -997,7 +999,7 @@
 
 	for _, test := range tests {
 
-		p, _ := NewPage("render/front/matter")
+		p, _ := pageTestSite.NewPage("render/front/matter")
 		_, err := p.ReadFrom(strings.NewReader(test.text))
 		p = pageMust(p, err)
 		if p.IsRenderable() != test.render {
@@ -1008,13 +1010,13 @@
 
 // Issue #768
 func TestCalendarParamsVariants(t *testing.T) {
-	pageJSON, _ := NewPage("test/fileJSON.md")
+	pageJSON, _ := pageTestSite.NewPage("test/fileJSON.md")
 	_, _ = pageJSON.ReadFrom(strings.NewReader(pageWithCalendarJSONFrontmatter))
 
-	pageYAML, _ := NewPage("test/fileYAML.md")
+	pageYAML, _ := pageTestSite.NewPage("test/fileYAML.md")
 	_, _ = pageYAML.ReadFrom(strings.NewReader(pageWithCalendarYAMLFrontmatter))
 
-	pageTOML, _ := NewPage("test/fileTOML.md")
+	pageTOML, _ := pageTestSite.NewPage("test/fileTOML.md")
 	_, _ = pageTOML.ReadFrom(strings.NewReader(pageWithCalendarTOMLFrontmatter))
 
 	assert.True(t, compareObjects(pageJSON.Params, pageYAML.Params))
@@ -1023,7 +1025,7 @@
 }
 
 func TestDifferentFrontMatterVarTypes(t *testing.T) {
-	page, _ := NewPage("test/file1.md")
+	page, _ := pageTestSite.NewPage("test/file1.md")
 	_, _ = page.ReadFrom(strings.NewReader(pageWithVariousFrontmatterTypes))
 
 	dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z")
@@ -1052,7 +1054,7 @@
 }
 
 func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) {
-	p, _ := NewPage("invalid/front/matter/leading/ws")
+	p, _ := pageTestSite.NewPage("invalid/front/matter/leading/ws")
 	_, err := p.ReadFrom(strings.NewReader(invalidFrontmatterLadingWs))
 	if err != nil {
 		t.Fatalf("Unable to parse front matter given leading whitespace: %s", err)
@@ -1060,7 +1062,7 @@
 }
 
 func TestSectionEvaluation(t *testing.T) {
-	page, _ := NewPage(filepath.FromSlash("blue/file1.md"))
+	page, _ := pageTestSite.NewPage(filepath.FromSlash("blue/file1.md"))
 	page.ReadFrom(strings.NewReader(simplePage))
 	if page.Section() != "blue" {
 		t.Errorf("Section should be %s, got: %s", "blue", page.Section())
@@ -1105,7 +1107,7 @@
 		{simplePageTypeLayout, pathNoDirectory, L("barfoo/buzfoo.html", "_default/buzfoo.html")},
 	}
 	for _, test := range tests {
-		p, _ := NewPage(test.path)
+		p, _ := pageTestSite.NewPage(test.path)
 		_, err := p.ReadFrom(strings.NewReader(test.content))
 		if err != nil {
 			t.Fatalf("Unable to parse content:\n%s\n", test.content)
@@ -1165,7 +1167,7 @@
 	}
 
 	for _, test := range tests {
-		p, _ := NewPageFrom(strings.NewReader(test.content), filepath.FromSlash(test.path))
+		p, _ := pageTestSite.NewPageFrom(strings.NewReader(test.content), filepath.FromSlash(test.path))
 		info := newSiteInfo(siteBuilderCfg{language: helpers.NewDefaultLanguage()})
 		p.Site = &info
 
@@ -1195,7 +1197,7 @@
 `
 
 func TestDraftAndPublishedFrontMatterError(t *testing.T) {
-	_, err := NewPageFrom(strings.NewReader(pageWithDraftAndPublished), "content/post/broken.md")
+	_, err := pageTestSite.NewPageFrom(strings.NewReader(pageWithDraftAndPublished), "content/post/broken.md")
 	if err != ErrHasDraftAndPublished {
 		t.Errorf("expected ErrHasDraftAndPublished, was %#v", err)
 	}
@@ -1215,7 +1217,7 @@
 `
 
 func TestPublishedFrontMatter(t *testing.T) {
-	p, err := NewPageFrom(strings.NewReader(pagesWithPublishedFalse), "content/post/broken.md")
+	p, err := pageTestSite.NewPageFrom(strings.NewReader(pagesWithPublishedFalse), "content/post/broken.md")
 	if err != nil {
 		t.Fatalf("err during parse: %s", err)
 	}
@@ -1222,7 +1224,7 @@
 	if !p.Draft {
 		t.Errorf("expected true, got %t", p.Draft)
 	}
-	p, err = NewPageFrom(strings.NewReader(pageWithPublishedTrue), "content/post/broken.md")
+	p, err = pageTestSite.NewPageFrom(strings.NewReader(pageWithPublishedTrue), "content/post/broken.md")
 	if err != nil {
 		t.Fatalf("err during parse: %s", err)
 	}
@@ -1250,7 +1252,7 @@
 	for _, draft := range []bool{true, false} {
 		for i, templ := range pagesDraftTemplate {
 			pageContent := fmt.Sprintf(templ, draft)
-			p, err := NewPageFrom(strings.NewReader(pageContent), "content/post/broken.md")
+			p, err := pageTestSite.NewPageFrom(strings.NewReader(pageContent), "content/post/broken.md")
 			if err != nil {
 				t.Fatalf("err during parse: %s", err)
 			}
@@ -1310,7 +1312,7 @@
 	}
 
 	for i, c := range pagesParamsTemplate {
-		p, err := NewPageFrom(strings.NewReader(c), "content/post/params.md")
+		p, err := pageTestSite.NewPageFrom(strings.NewReader(c), "content/post/params.md")
 		require.NoError(t, err, "err during parse", "#%d", i)
 		assert.Equal(t, want, p.Params, "#%d", i)
 	}
@@ -1326,7 +1328,7 @@
 		{func(p *Page) bool { return strings.Join(p.PlainWords(), " ") == "Do Be Do Be Do" }},
 	} {
 
-		p, _ := NewPage("Test")
+		p, _ := pageTestSite.NewPage("Test")
 		p.Content = "<h1>Do Be Do Be Do</h1>"
 		if !this.assertFunc(p) {
 			t.Errorf("[%d] Page method error", i)
@@ -1460,7 +1462,7 @@
 	buf.ReadFrom(f)
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
-		page, _ := NewPage("bench")
+		page, _ := pageTestSite.NewPage("bench")
 		page.ReadFrom(bytes.NewReader(buf.Bytes()))
 	}
 }
--- a/hugolib/page_time_integration_test.go
+++ b/hugolib/page_time_integration_test.go
@@ -89,7 +89,7 @@
 )
 
 func TestDegenerateDateFrontMatter(t *testing.T) {
-	p, _ := NewPageFrom(strings.NewReader(pageWithInvalidDate), "page/with/invalid/date")
+	p, _ := pageTestSite.NewPageFrom(strings.NewReader(pageWithInvalidDate), "page/with/invalid/date")
 	if p.Date != *new(time.Time) {
 		t.Fatalf("Date should be set to time.Time zero value.  Got: %s", p.Date)
 	}
@@ -131,7 +131,7 @@
 		if e != nil {
 			t.Fatalf("Unable to parse date time (RFC3339) for running the test: %s", e)
 		}
-		p, err := NewPageFrom(strings.NewReader(test.buf), "page/with/date")
+		p, err := pageTestSite.NewPageFrom(strings.NewReader(test.buf), "page/with/date")
 		if err != nil {
 			t.Fatalf("Expected to be able to parse page.")
 		}
--- a/hugolib/pagesPrevNext_test.go
+++ b/hugolib/pagesPrevNext_test.go
@@ -52,7 +52,7 @@
 	w := WeightedPages{}
 
 	for _, s := range pagePNTestSources {
-		p, err := NewPage(s.path)
+		p, err := pageTestSite.NewPage(s.path)
 		if err != nil {
 			t.Fatalf("failed to prepare test page %s", s.path)
 		}
--- a/hugolib/pagination_test.go
+++ b/hugolib/pagination_test.go
@@ -225,7 +225,7 @@
 		viper.Set("paginate", -1)
 	}
 	pages := createTestPages(12)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	n1 := s.newHomePage()
 	n2 := s.newHomePage()
 	n1.Data["Pages"] = pages
@@ -253,7 +253,7 @@
 	samePaginator, _ := n1.Paginator()
 	assert.Equal(t, paginator1, samePaginator)
 
-	p, _ := NewPage("test")
+	p, _ := pageTestSite.NewPage("test")
 	_, err = p.Paginator()
 	assert.NotNil(t, err)
 }
@@ -262,7 +262,7 @@
 	testCommonResetState()
 
 	viper.Set("paginate", -1)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	_, err := s.newHomePage().Paginator()
 	assert.NotNil(t, err)
 }
@@ -284,7 +284,7 @@
 	}
 
 	pages := createTestPages(6)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	n1 := s.newHomePage()
 	n2 := s.newHomePage()
 
@@ -311,13 +311,13 @@
 	assert.Nil(t, err)
 	assert.Equal(t, paginator2, paginator1.Next())
 
-	p, _ := NewPage("test")
+	p, _ := pageTestSite.NewPage("test")
 	_, err = p.Paginate(pages)
 	assert.NotNil(t, err)
 }
 
 func TestInvalidOptions(t *testing.T) {
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	n1 := s.newHomePage()
 	_, err := n1.Paginate(createTestPages(1), 1, 2)
 	assert.NotNil(t, err)
@@ -331,7 +331,7 @@
 	testCommonResetState()
 
 	viper.Set("paginate", -1)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	_, err := s.newHomePage().Paginate(createTestPages(2))
 	assert.NotNil(t, err)
 }
@@ -353,7 +353,7 @@
 	testCommonResetState()
 
 	viper.Set("paginate", 10)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	n1 := s.newHomePage()
 	n2 := s.newHomePage()
 
@@ -371,7 +371,7 @@
 	testCommonResetState()
 
 	viper.Set("paginate", 10)
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	n1 := s.newHomePage()
 	n2 := s.newHomePage()
 
--- a/hugolib/path_separators_test.go
+++ b/hugolib/path_separators_test.go
@@ -26,7 +26,7 @@
 `
 
 func TestDegenerateMissingFolderInPageFilename(t *testing.T) {
-	p, err := NewPageFrom(strings.NewReader(simplePageYAML), filepath.Join("foobar"))
+	p, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageYAML), filepath.Join("foobar"))
 	if err != nil {
 		t.Fatalf("Error in NewPageFrom")
 	}
@@ -48,7 +48,7 @@
 	}
 
 	for i, el := range toCheck {
-		p, err := NewPageFrom(strings.NewReader(simplePageYAML), el.input)
+		p, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageYAML), el.input)
 		if err != nil {
 			t.Errorf("[%d] Reading from simplePageYAML resulted in an error: %s", i, err)
 		}
--- a/hugolib/permalinks_test.go
+++ b/hugolib/permalinks_test.go
@@ -71,7 +71,7 @@
 }
 
 func TestPermalinkExpansion(t *testing.T) {
-	page, err := NewPageFrom(strings.NewReader(simplePageJSON), "blue/test-page.md")
+	page, err := pageTestSite.NewPageFrom(strings.NewReader(simplePageJSON), "blue/test-page.md")
 	info := newSiteInfo(siteBuilderCfg{language: helpers.NewDefaultLanguage()})
 	page.Site = &info
 	if err != nil {
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_test.go
@@ -32,7 +32,7 @@
 		writeSource(t, filepath.Join("content", "sect", s.Name), string(s.Content))
 	}
 
-	if err := buildAndRenderSite(newSiteDefaultLang()); err != nil {
+	if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
 	}
 
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -27,7 +27,6 @@
 	bp "github.com/spf13/hugo/bufferpool"
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/hugo/tpl"
-	jww "github.com/spf13/jwalterweatherman"
 )
 
 // ShortcodeWithPage is the "." context in a shortcode template.
@@ -215,7 +214,7 @@
 	tmpl := getShortcodeTemplate(sc.name, t)
 
 	if tmpl == nil {
-		jww.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
+		p.s.log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
 		return ""
 	}
 
@@ -233,7 +232,7 @@
 			case shortcode:
 				inner += renderShortcode(innerData.(shortcode), data, p, t)
 			default:
-				jww.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
+				p.s.log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
 					sc.name, p.BaseFileName(), reflect.TypeOf(innerData))
 				return ""
 			}
@@ -287,7 +286,7 @@
 
 	if err != nil {
 		//  try to render what we have whilst logging the error
-		jww.ERROR.Println(err.Error())
+		p.s.log.ERROR.Println(err.Error())
 	}
 
 	// Save for reuse
@@ -585,8 +584,9 @@
 	err := tmpl.Execute(buffer, data)
 	isInnerShortcodeCache.RUnlock()
 	if err != nil {
-		jww.ERROR.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
-		jww.WARN.Println(data)
+		// TODO(bep) globals
+		data.Page.s.log.ERROR.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
+		data.Page.s.log.WARN.Println(data)
 	}
 	return buffer.String()
 }
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -33,7 +33,7 @@
 
 // TODO(bep) remove
 func pageFromString(in, filename string) (*Page, error) {
-	return NewPageFrom(strings.NewReader(in), filename)
+	return pageTestSite.NewPageFrom(strings.NewReader(in), filename)
 }
 
 func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
@@ -83,7 +83,7 @@
 }
 
 func TestShortcodeGoFuzzReports(t *testing.T) {
-	tem := tpl.New()
+	tem := tpl.New(logger)
 
 	tem.AddInternalShortcode("sc.html", `foo`)
 	p, _ := pageFromString(simplePage, "simple.md")
@@ -304,7 +304,7 @@
 	viper.Set("pygmentsStyle", "bw")
 	viper.Set("pygmentsUseClasses", false)
 
-	templ := tpl.New()
+	templ := tpl.New(logger)
 
 	code := `
 {{< highlight java >}}
@@ -380,7 +380,7 @@
 	} {
 
 		p, _ := pageFromString(simplePage, "simple.md")
-		tem := tpl.New()
+		tem := tpl.New(logger)
 		tem.AddInternalShortcode("tag.html", `tag`)
 		tem.AddInternalShortcode("sc1.html", `sc1`)
 		tem.AddInternalShortcode("sc2.html", `sc2`)
@@ -578,7 +578,7 @@
 
 	}
 
-	sites, err := newHugoSites(s)
+	sites, err := newHugoSites(DepsCfg{}, s)
 
 	if err != nil {
 		t.Fatalf("Failed to build site: %s", err)
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -42,7 +42,6 @@
 	"github.com/spf13/hugo/target"
 	"github.com/spf13/hugo/tpl"
 	"github.com/spf13/hugo/transform"
-	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/nitro"
 	"github.com/spf13/viper"
 )
@@ -105,22 +104,29 @@
 	expiredCount   int
 	Data           map[string]interface{}
 	Language       *helpers.Language
+
+	// Logger etc.
+	*deps
 }
 
 // reset returns a new Site prepared for rebuild.
 func (s *Site) reset() *Site {
-	return &Site{Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
+	return &Site{deps: s.deps, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
 }
 
 // newSite creates a new site in the given language.
 func newSite(lang *helpers.Language) *Site {
 	c := newPageCollections()
-	return &Site{Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})}
+	// TODO(bep) globals (also see other Site creation places)
+	deps := newDeps(DepsCfg{})
+	// TODO(bep) globals
+	viper.Set("currentContentLanguage", lang)
+	return &Site{deps: deps, Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})}
 
 }
 
-// newSite creates a new site in the default language.
-func newSiteDefaultLang() *Site {
+// NewSiteDefaultLang creates a new site in the default language.
+func NewSiteDefaultLang() *Site {
 	return newSite(helpers.NewDefaultLanguage())
 }
 
@@ -141,6 +147,7 @@
 	lang := helpers.NewDefaultLanguage()
 
 	return &Site{
+		deps:            newDeps(DepsCfg{}),
 		PageCollections: newPageCollections(),
 		Source:          &source.InMemorySource{ByteSource: sources},
 		Language:        lang,
@@ -487,7 +494,7 @@
 // It returns whetever the content source was changed.
 func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
 
-	jww.DEBUG.Printf("Rebuild for events %q", events)
+	s.log.DEBUG.Printf("Rebuild for events %q", events)
 
 	s.timerStep("initialize rebuild")
 
@@ -533,7 +540,7 @@
 
 	if len(i18nChanged) > 0 {
 		if err := s.readI18nSources(); err != nil {
-			jww.ERROR.Println(err)
+			s.log.ERROR.Println(err)
 		}
 	}
 
@@ -602,7 +609,7 @@
 		file, err := s.reReadFile(ev.Name)
 
 		if err != nil {
-			jww.ERROR.Println("Error reading file", ev.Name, ";", err)
+			s.log.ERROR.Println("Error reading file", ev.Name, ";", err)
 		}
 
 		if file != nil {
@@ -636,7 +643,7 @@
 	for i := 0; i < 2; i++ {
 		err := <-errs
 		if err != nil {
-			jww.ERROR.Println(err)
+			s.log.ERROR.Println(err)
 		}
 	}
 
@@ -650,7 +657,7 @@
 }
 
 func (s *Site) loadTemplates() {
-	s.owner.tmpl = tpl.InitializeT()
+	s.owner.tmpl = tpl.InitializeT(s.log)
 	s.owner.tmpl.LoadTemplates(s.absLayoutDir())
 	if s.hasTheme() {
 		s.owner.tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
@@ -672,7 +679,7 @@
 }
 
 func (s *Site) loadData(sources []source.Input) (err error) {
-	jww.DEBUG.Printf("Load Data from %q", sources)
+	s.log.DEBUG.Printf("Load Data from %q", sources)
 	s.Data = make(map[string]interface{})
 	var current map[string]interface{}
 	for _, currentSource := range sources {
@@ -688,7 +695,7 @@
 				}
 			}
 
-			data, err := readData(r)
+			data, err := s.readData(r)
 			if err != nil {
 				return fmt.Errorf("Failed to read data from %s: %s", filepath.Join(r.Path(), r.LogicalName()), err)
 			}
@@ -707,7 +714,7 @@
 						// this warning could happen if
 						// 1. A theme uses the same key; the main data folder wins
 						// 2. A sub folder uses the same key: the sub folder wins
-						jww.WARN.Printf("Data for key '%s' in path '%s' is overridden in subfolder", key, r.Path())
+						s.log.WARN.Printf("Data for key '%s' in path '%s' is overridden in subfolder", key, r.Path())
 					}
 					data[key] = value
 				}
@@ -721,7 +728,7 @@
 	return
 }
 
-func readData(f *source.File) (interface{}, error) {
+func (s *Site) readData(f *source.File) (interface{}, error) {
 	switch f.Extension() {
 	case "yaml", "yml":
 		return parser.HandleYAMLMetaData(f.Bytes())
@@ -730,7 +737,7 @@
 	case "toml":
 		return parser.HandleTOMLMetaData(f.Bytes())
 	default:
-		jww.WARN.Printf("Data not supported for extension '%s'", f.Extension())
+		s.log.WARN.Printf("Data not supported for extension '%s'", f.Extension())
 		return nil, nil
 	}
 }
@@ -862,7 +869,7 @@
 
 	// May be supplied in tests.
 	if s.Source != nil && len(s.Source.Files()) > 0 {
-		jww.DEBUG.Println("initialize: Source is already set")
+		s.log.DEBUG.Println("initialize: Source is already set")
 		return
 	}
 
@@ -988,7 +995,7 @@
 }
 
 func (s *Site) getI18nDir(path string) string {
-	return getRealDir(s.absI18nDir(), path)
+	return s.getRealDir(s.absI18nDir(), path)
 }
 
 func (s *Site) getThemeI18nDir(path string) string {
@@ -995,7 +1002,7 @@
 	if !s.hasTheme() {
 		return ""
 	}
-	return getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.i18nDir())), path)
+	return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.i18nDir())), path)
 }
 
 func (s *Site) isDataDirEvent(e fsnotify.Event) bool {
@@ -1006,7 +1013,7 @@
 }
 
 func (s *Site) getDataDir(path string) string {
-	return getRealDir(s.absDataDir(), path)
+	return s.getRealDir(s.absDataDir(), path)
 }
 
 func (s *Site) getThemeDataDir(path string) string {
@@ -1013,7 +1020,7 @@
 	if !s.hasTheme() {
 		return ""
 	}
-	return getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.dataDir())), path)
+	return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.dataDir())), path)
 }
 
 func (s *Site) themeDir() string {
@@ -1040,7 +1047,7 @@
 }
 
 func (s *Site) getLayoutDir(path string) string {
-	return getRealDir(s.absLayoutDir(), path)
+	return s.getRealDir(s.absLayoutDir(), path)
 }
 
 func (s *Site) getThemeLayoutDir(path string) string {
@@ -1047,7 +1054,7 @@
 	if !s.hasTheme() {
 		return ""
 	}
-	return getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.layoutDir())), path)
+	return s.getRealDir(helpers.AbsPathify(filepath.Join(s.themeDir(), s.layoutDir())), path)
 }
 
 func (s *Site) absContentDir() string {
@@ -1059,12 +1066,12 @@
 }
 
 func (s *Site) getContentDir(path string) string {
-	return getRealDir(s.absContentDir(), path)
+	return s.getRealDir(s.absContentDir(), path)
 }
 
 // getRealDir gets the base path of the given path, also handling the case where
 // base is a symlinked folder.
-func getRealDir(base, path string) string {
+func (s *Site) getRealDir(base, path string) string {
 
 	if strings.HasPrefix(path, base) {
 		return base
@@ -1074,7 +1081,7 @@
 
 	if err != nil {
 		if !os.IsNotExist(err) {
-			jww.ERROR.Printf("Failed to get real path for %s: %s", path, err)
+			s.log.ERROR.Printf("Failed to get real path for %s: %s", path, err)
 		}
 		return ""
 	}
@@ -1099,7 +1106,7 @@
 
 // reReadFile resets file to be read from disk again
 func (s *Site) reReadFile(absFilePath string) (*source.File, error) {
-	jww.INFO.Println("rereading", absFilePath)
+	s.log.INFO.Println("rereading", absFilePath)
 	var file *source.File
 
 	reader, err := source.NewLazyFileReader(hugofs.Source(), absFilePath)
@@ -1120,7 +1127,7 @@
 		panic(fmt.Sprintf("s.Source not set %s", s.absContentDir()))
 	}
 
-	jww.DEBUG.Printf("Read %d pages from source", len(s.Source.Files()))
+	s.log.DEBUG.Printf("Read %d pages from source", len(s.Source.Files()))
 
 	errs := make(chan error)
 	if len(s.Source.Files()) < 1 {
@@ -1220,7 +1227,7 @@
 	if h != nil {
 		h.Read(file, s, results)
 	} else {
-		jww.ERROR.Println("Unsupported File Type", file.Path())
+		s.log.ERROR.Println("Unsupported File Type", file.Path())
 	}
 }
 
@@ -1361,17 +1368,17 @@
 		for name, menu := range menus {
 			m, err := cast.ToSliceE(menu)
 			if err != nil {
-				jww.ERROR.Printf("unable to process menus in site config\n")
-				jww.ERROR.Println(err)
+				s.log.ERROR.Printf("unable to process menus in site config\n")
+				s.log.ERROR.Println(err)
 			} else {
 				for _, entry := range m {
-					jww.DEBUG.Printf("found menu: %q, in site config\n", name)
+					s.log.DEBUG.Printf("found menu: %q, in site config\n", name)
 
 					menuEntry := MenuEntry{Menu: name}
 					ime, err := cast.ToStringMapE(entry)
 					if err != nil {
-						jww.ERROR.Printf("unable to process menus in site config\n")
-						jww.ERROR.Println(err)
+						s.log.ERROR.Printf("unable to process menus in site config\n")
+						s.log.ERROR.Println(err)
 					}
 
 					menuEntry.marshallMap(ime)
@@ -1443,7 +1450,7 @@
 
 		for name, me := range p.Menus() {
 			if _, ok := flat[twoD{name, me.KeyName()}]; ok {
-				jww.ERROR.Printf("Two or more menu items have the same name/identifier in Menu %q: %q.\nRename or set an unique identifier.\n", name, me.KeyName())
+				s.log.ERROR.Printf("Two or more menu items have the same name/identifier in Menu %q: %q.\nRename or set an unique identifier.\n", name, me.KeyName())
 				continue
 			}
 			flat[twoD{name, me.KeyName()}] = me
@@ -1486,7 +1493,7 @@
 
 	taxonomies := s.Language.GetStringMapString("taxonomies")
 
-	jww.INFO.Printf("found taxonomies: %#v\n", taxonomies)
+	s.log.INFO.Printf("found taxonomies: %#v\n", taxonomies)
 
 	for singular, plural := range taxonomies {
 		s.Taxonomies[plural] = make(Taxonomy)
@@ -1516,7 +1523,7 @@
 						s.taxonomiesOrigKey[fmt.Sprintf("%s-%s", plural, kp(v))] = v
 					}
 				} else {
-					jww.ERROR.Printf("Invalid %s in %s\n", plural, p.File.Path())
+					s.log.ERROR.Printf("Invalid %s in %s\n", plural, p.File.Path())
 				}
 			}
 		}
@@ -1654,18 +1661,18 @@
 // Stats prints Hugo builds stats to the console.
 // This is what you see after a successful hugo build.
 func (s *Site) Stats() {
-	jww.FEEDBACK.Printf("Built site for language %s:\n", s.Language.Lang)
-	jww.FEEDBACK.Println(s.draftStats())
-	jww.FEEDBACK.Println(s.futureStats())
-	jww.FEEDBACK.Println(s.expiredStats())
-	jww.FEEDBACK.Printf("%d regular pages created\n", len(s.RegularPages))
-	jww.FEEDBACK.Printf("%d other pages created\n", (len(s.Pages) - len(s.RegularPages)))
-	jww.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
-	jww.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
+	s.log.FEEDBACK.Printf("Built site for language %s:\n", s.Language.Lang)
+	s.log.FEEDBACK.Println(s.draftStats())
+	s.log.FEEDBACK.Println(s.futureStats())
+	s.log.FEEDBACK.Println(s.expiredStats())
+	s.log.FEEDBACK.Printf("%d regular pages created\n", len(s.RegularPages))
+	s.log.FEEDBACK.Printf("%d other pages created\n", (len(s.Pages) - len(s.RegularPages)))
+	s.log.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
+	s.log.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
 	taxonomies := s.Language.GetStringMapString("taxonomies")
 
 	for _, pl := range taxonomies {
-		jww.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
+		s.log.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
 	}
 
 }
@@ -1694,7 +1701,7 @@
 }
 
 func (s *Site) renderAndWriteXML(name string, dest string, d interface{}, layouts ...string) error {
-	jww.DEBUG.Printf("Render XML for %q to %q", name, dest)
+	s.log.DEBUG.Printf("Render XML for %q to %q", name, dest)
 	renderBuffer := bp.GetBuffer()
 	defer bp.PutBuffer(renderBuffer)
 	renderBuffer.WriteString("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n")
@@ -1786,7 +1793,7 @@
 
 	if outBuffer.Len() == 0 {
 
-		jww.WARN.Printf("%s is rendered empty\n", dest)
+		s.log.WARN.Printf("%s is rendered empty\n", dest)
 		if dest == "/" {
 			debugAddend := ""
 			if !viper.GetBool("verbose") {
@@ -1818,7 +1825,7 @@
 func (s *Site) renderForLayouts(name string, d interface{}, w io.Writer, layouts ...string) error {
 	layout, found := s.findFirstLayout(layouts...)
 	if !found {
-		jww.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
+		s.log.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
 		return nil
 	}
 
@@ -1922,12 +1929,12 @@
 }
 
 func (s *Site) writeDestFile(path string, reader io.Reader) (err error) {
-	jww.DEBUG.Println("creating file:", path)
+	s.log.DEBUG.Println("creating file:", path)
 	return s.fileTarget().Publish(path, reader)
 }
 
 func (s *Site) writeDestPage(path string, publisher target.Publisher, reader io.Reader) (err error) {
-	jww.DEBUG.Println("creating page:", path)
+	s.log.DEBUG.Println("creating page:", path)
 	return publisher.Publish(path, reader)
 }
 
@@ -1945,11 +1952,11 @@
 		}
 		permalink, err = helpers.GetRelativePath(permalink, path)
 		if err != nil {
-			jww.ERROR.Println("Failed to make a RelativeURL alias:", path, "redirecting to", permalink)
+			s.log.ERROR.Println("Failed to make a RelativeURL alias:", path, "redirecting to", permalink)
 		}
 		permalink = filepath.ToSlash(permalink)
 	}
-	jww.DEBUG.Println("creating alias:", path, "redirecting to", permalink)
+	s.log.DEBUG.Println("creating alias:", path, "redirecting to", permalink)
 	return aliasPublisher.Publish(path, permalink, p)
 }
 
@@ -2026,7 +2033,7 @@
 		Data:     make(map[string]interface{}),
 		Site:     &s.Info,
 		language: s.Language,
-		site:     s}
+		s:        s}
 }
 
 func (s *Site) newHomePage() *Page {
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -23,8 +23,6 @@
 	bp "github.com/spf13/hugo/bufferpool"
 	"github.com/spf13/hugo/helpers"
 	"github.com/spf13/viper"
-
-	jww "github.com/spf13/jwalterweatherman"
 )
 
 // renderPages renders pages each corresponding to a markdown file.
@@ -68,7 +66,7 @@
 	for p := range pages {
 		targetPath := p.TargetPath()
 		layouts := p.layouts()
-		jww.DEBUG.Printf("Render %s to %q with layouts %q", p.Kind, targetPath, layouts)
+		s.log.DEBUG.Printf("Render %s to %q with layouts %q", p.Kind, targetPath, layouts)
 
 		if err := s.renderAndWritePage("page "+p.FullFilePath(), targetPath, p, s.appendThemeTemplates(layouts)...); err != nil {
 			results <- err
@@ -90,7 +88,7 @@
 // renderPaginator must be run after the owning Page has been rendered.
 func (s *Site) renderPaginator(p *Page) error {
 	if p.paginator != nil {
-		jww.DEBUG.Printf("Render paginator for page %q", p.Path())
+		s.log.DEBUG.Printf("Render paginator for page %q", p.Path())
 		paginatePath := helpers.Config().GetString("paginatePath")
 
 		// write alias for page 1
@@ -270,13 +268,13 @@
 		mainLang := s.owner.multilingual.DefaultLang.Lang
 		if s.Info.defaultContentLanguageInSubdir {
 			mainLangURL := s.Info.pathSpec.AbsURL(mainLang, false)
-			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+			s.log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
 			if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
 				return err
 			}
 		} else {
 			mainLangURL := s.Info.pathSpec.AbsURL("", false)
-			jww.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+			s.log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
 			if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
 				return err
 			}
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -58,6 +58,7 @@
 	sources := []source.ByteSource{}
 
 	s := &Site{
+		deps:    newDeps(DepsCfg{}),
 		Source:  &source.InMemorySource{ByteSource: sources},
 		targets: targetList{page: &target.PagePub{UglyURLs: true}},
 	}
@@ -102,14 +103,13 @@
 }
 
 func TestRenderWithInvalidTemplate(t *testing.T) {
-	jww.ResetLogCounters()
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 	if err := buildAndRenderSite(s, "missing", templateMissingFunc); err != nil {
 		t.Fatalf("Got build error: %s", err)
 	}
 
-	if jww.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError) != 1 {
+	if s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError) != 1 {
 		t.Fatalf("Expecting the template to log an ERROR")
 	}
 }
@@ -127,6 +127,7 @@
 
 	siteSetup := func(t *testing.T) *Site {
 		s := &Site{
+			deps:     newDeps(DepsCfg{}),
 			Source:   &source.InMemorySource{ByteSource: sources},
 			Language: helpers.NewDefaultLanguage(),
 		}
@@ -185,6 +186,7 @@
 
 	siteSetup := func(t *testing.T) *Site {
 		s := &Site{
+			deps:     newDeps(DepsCfg{}),
 			Source:   &source.InMemorySource{ByteSource: sources},
 			Language: helpers.NewDefaultLanguage(),
 		}
@@ -276,6 +278,7 @@
 	}
 
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: sources},
 		targets:  targetList{page: &target.PagePub{UglyURLs: uglyURLs}},
 		Language: helpers.NewDefaultLanguage(),
@@ -343,6 +346,7 @@
 	}
 
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: sources},
 		targets:  targetList{page: &target.PagePub{UglyURLs: uglyURLs, PublishDir: "public"}},
 		Language: helpers.NewDefaultLanguage(),
@@ -393,7 +397,6 @@
 
 // Issue #1176
 func TestSectionNaming(t *testing.T) {
-	//jww.SetStdoutThreshold(jww.LevelDebug)
 
 	for _, canonify := range []bool{true, false} {
 		for _, uglify := range []bool{true, false} {
@@ -431,7 +434,7 @@
 		writeSource(t, filepath.Join("content", source.Name), string(source.Content))
 	}
 
-	s := newSiteDefaultLang()
+	s := NewSiteDefaultLang()
 
 	if err := buildAndRenderSite(s,
 		"_default/single.html", "{{.Content}}",
@@ -482,6 +485,7 @@
 	viper.Set("canonifyURLs", true)
 	viper.Set("baseURL", "http://auth/bub")
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: sources},
 		targets:  targetList{page: &target.PagePub{UglyURLs: true}},
 		Language: helpers.NewDefaultLanguage(),
@@ -537,6 +541,7 @@
 			viper.Set("canonifyURLs", canonify)
 			viper.Set("baseURL", baseURL)
 			s := &Site{
+				deps:     newDeps(DepsCfg{}),
 				Source:   &source.InMemorySource{ByteSource: sources},
 				targets:  targetList{page: &target.PagePub{UglyURLs: true}},
 				Language: helpers.NewDefaultLanguage(),
@@ -633,6 +638,7 @@
 
 	viper.Set("baseURL", "http://auth/bub")
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: weightedSources},
 		Language: helpers.NewDefaultLanguage(),
 	}
@@ -702,6 +708,7 @@
 
 	viper.Set("baseURL", "http://auth/bub")
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: groupedSources},
 		Language: helpers.NewDefaultLanguage(),
 	}
@@ -887,6 +894,7 @@
 	viper.Set("baseURL", "http://auth/bub")
 	viper.Set("taxonomies", taxonomies)
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: sources},
 		Language: helpers.NewDefaultLanguage(),
 	}
@@ -956,6 +964,7 @@
 			"sourceRelativeLinksProjectFolder": "/docs"})
 
 	site := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: sources},
 		Language: helpers.NewDefaultLanguage(),
 	}
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -62,7 +62,7 @@
 		{"http://base.com", "http://base.com"}} {
 
 		viper.Set("baseURL", this.in)
-		s := newSiteDefaultLang()
+		s := NewSiteDefaultLang()
 		s.initializeSiteInfo()
 
 		if s.Info.BaseURL != template.URL(this.expected) {
@@ -79,6 +79,7 @@
 	viper.Set("uglyURLs", false)
 	viper.Set("paginate", 10)
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: urlFakeSource},
 		Language: helpers.NewDefaultLanguage(),
 	}
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -46,6 +46,7 @@
 	viper.Set("baseURL", "http://auth/bub/")
 
 	s := &Site{
+		deps:     newDeps(DepsCfg{}),
 		Source:   &source.InMemorySource{ByteSource: weightedSources},
 		Language: helpers.NewDefaultLanguage(),
 	}
--- a/hugolib/taxonomy_test.go
+++ b/hugolib/taxonomy_test.go
@@ -33,7 +33,7 @@
 
 	writeSource(t, filepath.Join("content", "page.md"), pageYamlWithTaxonomiesA)
 
-	site := newSiteDefaultLang()
+	site := NewSiteDefaultLang()
 
 	if err := buildSiteSkipRender(site); err != nil {
 		t.Fatalf("Failed to build site: %s", err)
--- a/hugolib/template_test.go
+++ b/hugolib/template_test.go
@@ -135,7 +135,7 @@
 `)
 		this.setup(t)
 
-		if err := buildAndRenderSite(newSiteDefaultLang()); err != nil {
+		if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
 			t.Fatalf("[%d] Failed to build site: %s", i, err)
 		}
 
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -31,8 +31,10 @@
 )
 
 var localTemplates *template.Template
-var tmpl Template
 
+// TODO(bep) globals get rid of the reset of the jww.ERR etc.
+var tmpl *GoHTMLTemplate
+
 // TODO(bep) an interface with hundreds of methods ... remove it.
 // And unexport most of these methods.
 type Template interface {
@@ -66,30 +68,25 @@
 	overlays map[string]*template.Template
 
 	errors []*templateErr
-}
 
-// T is the "global" template system
-func T() Template {
-	if tmpl == nil {
-		tmpl = New()
-	}
-
-	return tmpl
+	// TODO(bep) globals template
+	log *jww.Notepad
 }
 
 // InitializeT resets the internal template state to its initial state
-func InitializeT() Template {
-	tmpl = New()
+func InitializeT(logger *jww.Notepad) *GoHTMLTemplate {
+	tmpl = New(logger)
 	return tmpl
 }
 
 // New returns a new Hugo Template System
 // with all the additional features, templates & functions
-func New() Template {
+func New(logger *jww.Notepad) *GoHTMLTemplate {
 	var templates = &GoHTMLTemplate{
 		Template: *template.New(""),
 		overlays: make(map[string]*template.Template),
 		errors:   make([]*templateErr, 0),
+		log:      logger,
 	}
 
 	localTemplates = &templates.Template
@@ -139,8 +136,8 @@
 		}
 	}
 	if !worked {
-		jww.ERROR.Println("Unable to render", layouts)
-		jww.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
+		tmpl.log.ERROR.Println("Unable to render", layouts)
+		tmpl.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
 	}
 }
 
@@ -152,7 +149,7 @@
 }
 
 func Lookup(name string) *template.Template {
-	return (tmpl.(*GoHTMLTemplate)).Lookup(name)
+	return tmpl.Lookup(name)
 }
 
 func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
@@ -361,7 +358,7 @@
 			return err
 		}
 
-		jww.DEBUG.Printf("Add template file from path %s", path)
+		t.log.DEBUG.Printf("Add template file from path %s", path)
 
 		return t.AddTemplate(name, string(b))
 	}
@@ -391,25 +388,25 @@
 }
 
 func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
-	jww.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
+	t.log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
 	walker := func(path string, fi os.FileInfo, err error) error {
 		if err != nil {
 			return nil
 		}
-		jww.DEBUG.Println("Template path", path)
+		t.log.DEBUG.Println("Template path", path)
 		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
 			link, err := filepath.EvalSymlinks(absPath)
 			if err != nil {
-				jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
+				t.log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
 				return nil
 			}
 			linkfi, err := hugofs.Source().Stat(link)
 			if err != nil {
-				jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
+				t.log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
 				return nil
 			}
 			if !linkfi.Mode().IsRegular() {
-				jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
+				t.log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
 			}
 			return nil
 		}
@@ -492,7 +489,7 @@
 			}
 
 			if err := t.AddTemplateFile(tplName, baseTemplatePath, path); err != nil {
-				jww.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
+				t.log.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
 			}
 
 		}
@@ -499,7 +496,7 @@
 		return nil
 	}
 	if err := helpers.SymbolicWalk(hugofs.Source(), absPath, walker); err != nil {
-		jww.ERROR.Printf("Failed to load templates: %s", err)
+		t.log.ERROR.Printf("Failed to load templates: %s", err)
 	}
 }
 
@@ -526,6 +523,6 @@
 
 func (t *GoHTMLTemplate) PrintErrors() {
 	for _, e := range t.errors {
-		jww.ERROR.Println(e.err)
+		t.log.ERROR.Println(e.err)
 	}
 }
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -33,13 +33,20 @@
 
 	"github.com/spf13/hugo/helpers"
 
+	"io/ioutil"
+	"log"
+	"os"
+
 	"github.com/spf13/afero"
 	"github.com/spf13/cast"
 	"github.com/spf13/hugo/hugofs"
+	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 )
 
+var logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+
 type tstNoStringer struct {
 }
 
@@ -237,7 +244,7 @@
 `
 
 	var b bytes.Buffer
-	templ, err := New().New("test").Parse(in)
+	templ, err := New(logger).New("test").Parse(in)
 	var data struct {
 		Title   string
 		Section string
@@ -2371,7 +2378,7 @@
 		{map[string]string{"foo": "dog"}, `{{ default "nope" .foo "extra" }}`, ``, false},
 		{map[string]interface{}{"images": []string{}}, `{{ default "default.jpg" (index .images 0) }}`, `default.jpg`, true},
 	} {
-		tmpl, err := New().New("test").Parse(this.tpl)
+		tmpl, err := New(logger).New("test").Parse(this.tpl)
 		if err != nil {
 			t.Errorf("[%d] unable to create new html template %q: %s", i, this.tpl, err)
 			continue
@@ -2773,7 +2780,7 @@
 	data.Params = map[string]interface{}{"langCode": "en"}
 
 	tstInitTemplates()
-	InitializeT()
+	InitializeT(logger)
 	for i, tc := range testCases {
 		var tmp string
 		if tc.variant != "" {
@@ -2782,7 +2789,7 @@
 			tmp = tc.tmpl
 		}
 
-		tmpl, err := New().New("testroot").Parse(tmp)
+		tmpl, err := New(logger).New("testroot").Parse(tmp)
 		if err != nil {
 			t.Fatalf("[%d] unable to create new html template: %s", i, err)
 		}
@@ -2824,8 +2831,8 @@
 }
 
 func BenchmarkPartial(b *testing.B) {
-	InitializeT()
-	tmpl, err := New().New("testroot").Parse(`{{ partial "bench1" . }}`)
+	InitializeT(logger)
+	tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
 	}
@@ -2844,8 +2851,8 @@
 }
 
 func BenchmarkPartialCached(b *testing.B) {
-	InitializeT()
-	tmpl, err := New().New("testroot").Parse(`{{ partialCached "bench1" . }}`)
+	InitializeT(logger)
+	tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
 	}
@@ -2864,8 +2871,8 @@
 }
 
 func BenchmarkPartialCachedVariants(b *testing.B) {
-	InitializeT()
-	tmpl, err := New().New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
+	InitializeT(logger)
+	tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
 	if err != nil {
 		b.Fatalf("unable to create new html template: %s", err)
 	}
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -55,7 +55,7 @@
 
 		for _, root := range []string{"", os.TempDir()} {
 
-			templ := New()
+			templ := New(logger)
 
 			basePath := this.basePath
 			innerPath := this.innerPath
@@ -124,7 +124,7 @@
 	} {
 
 		hugofs.InitMemFs()
-		templ := New()
+		templ := New(logger)
 		overlayTplName := "ot"
 		masterTplName := "mt"
 		finalTplName := "tp"
@@ -245,7 +245,7 @@
 		// Issue #1095
 		{"{{apply .C \"urlize\" " +
 			"\".\"}}", 2}} {
-		templ := New()
+		templ := New(logger)
 
 		d := &Data{
 			A: 42,