浏览代码

Add logger to all pipeline items

Signed-off-by: Robert Lin <robertlin1@gmail.com>
Robert Lin 6 年之前
父节点
当前提交
f42a9ba993

+ 8 - 0
core.go

@@ -81,6 +81,8 @@ const (
 	ConfigPipelineCommits = core.ConfigPipelineCommits
 	// ConfigTickSize is the number of hours per 'tick'
 	ConfigTickSize = plumbing.ConfigTicksSinceStartTickSize
+	// ConfigLogger is used to set the logger in all pipeline items.
+	ConfigLogger = core.ConfigLogger
 )
 
 // NewPipeline initializes a new instance of Pipeline struct.
@@ -174,3 +176,9 @@ func PathifyFlagValue(flag *pflag.Flag) {
 func EnablePathFlagTypeMasquerade() {
 	core.EnablePathFlagTypeMasquerade()
 }
+
+// Logger is the Hercules logging interface
+type Logger core.Logger
+
+// NewLogger returns an instance of the default Hercules logger
+func NewLogger() core.Logger { return core.NewLogger() }

+ 3 - 0
internal/core/logger.go

@@ -5,6 +5,9 @@ import (
 	"os"
 )
 
+// ConfigLogger is the key for the pipeline's logger
+const ConfigLogger = "Core.Logger"
+
 // Logger defines the output interface used by Hercules components.
 type Logger interface {
 	Info(...interface{})

+ 5 - 3
internal/core/pipeline.go

@@ -336,9 +336,6 @@ func NewPipeline(repository *git.Repository) *Pipeline {
 	}
 }
 
-// SetLogger updates the pipeline's logger.
-func (pipeline *Pipeline) SetLogger(l Logger) { pipeline.l = l }
-
 // GetFact returns the value of the fact with the specified name.
 func (pipeline *Pipeline) GetFact(name string) interface{} {
 	return pipeline.facts[name]
@@ -658,6 +655,11 @@ func (pipeline *Pipeline) Initialize(facts map[string]interface{}) error {
 	if facts == nil {
 		facts = map[string]interface{}{}
 	}
+	if l, exists := facts[ConfigLogger].(Logger); exists {
+		pipeline.l = l
+	} else {
+		facts[ConfigLogger] = pipeline.l
+	}
 	if _, exists := facts[ConfigPipelineCommits]; !exists {
 		var err error
 		facts[ConfigPipelineCommits], err = pipeline.Commits(false)

+ 18 - 11
internal/plumbing/blob_cache.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"log"
 
 	"github.com/pkg/errors"
 	"gopkg.in/src-d/go-git.v4"
@@ -87,6 +86,8 @@ type BlobCache struct {
 
 	repository *git.Repository
 	cache      map[plumbing.Hash]*CachedBlob
+
+	l core.Logger
 }
 
 const (
@@ -133,6 +134,11 @@ func (blobCache *BlobCache) ListConfigurationOptions() []core.ConfigurationOptio
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (blobCache *BlobCache) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		blobCache.l = l
+	} else {
+		blobCache.l = core.NewLogger()
+	}
 	if val, exists := facts[ConfigBlobCacheFailOnMissingSubmodules].(bool); exists {
 		blobCache.FailOnMissingSubmodules = val
 	}
@@ -142,6 +148,7 @@ func (blobCache *BlobCache) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (blobCache *BlobCache) Initialize(repository *git.Repository) error {
+	blobCache.l = core.NewLogger()
 	blobCache.repository = repository
 	blobCache.cache = map[plumbing.Hash]*CachedBlob{}
 	return nil
@@ -161,7 +168,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 	for _, change := range changes {
 		action, err := change.Action()
 		if err != nil {
-			log.Printf("no action in %s\n", change.To.TreeEntry.Hash)
+			blobCache.l.Errorf("no action in %s\n", change.To.TreeEntry.Hash)
 			return nil, err
 		}
 		var exists bool
@@ -172,7 +179,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 			newCache[change.To.TreeEntry.Hash] = &CachedBlob{}
 			blob, err = blobCache.getBlob(&change.To, commit.File)
 			if err != nil {
-				log.Printf("file to %s %s: %v\n", change.To.Name, change.To.TreeEntry.Hash, err)
+				blobCache.l.Errorf("file to %s %s: %v\n", change.To.Name, change.To.TreeEntry.Hash, err)
 			} else {
 				cb := &CachedBlob{Blob: *blob}
 				err = cb.Cache()
@@ -180,7 +187,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 					cache[change.To.TreeEntry.Hash] = cb
 					newCache[change.To.TreeEntry.Hash] = cb
 				} else {
-					log.Printf("file to %s %s: %v\n", change.To.Name, change.To.TreeEntry.Hash, err)
+					blobCache.l.Errorf("file to %s %s: %v\n", change.To.Name, change.To.TreeEntry.Hash, err)
 				}
 			}
 		case merkletrie.Delete:
@@ -191,7 +198,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 				blob, err = blobCache.getBlob(&change.From, commit.File)
 				if err != nil {
 					if err.Error() != plumbing.ErrObjectNotFound.Error() {
-						log.Printf("file from %s %s: %v\n", change.From.Name,
+						blobCache.l.Errorf("file from %s %s: %v\n", change.From.Name,
 							change.From.TreeEntry.Hash, err)
 					} else {
 						blob, err = internal.CreateDummyBlob(change.From.TreeEntry.Hash)
@@ -203,7 +210,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 					if err == nil {
 						cache[change.From.TreeEntry.Hash] = cb
 					} else {
-						log.Printf("file from %s %s: %v\n", change.From.Name,
+						blobCache.l.Errorf("file from %s %s: %v\n", change.From.Name,
 							change.From.TreeEntry.Hash, err)
 					}
 				}
@@ -213,7 +220,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 			cache[change.To.TreeEntry.Hash] = &CachedBlob{}
 			newCache[change.To.TreeEntry.Hash] = &CachedBlob{}
 			if err != nil {
-				log.Printf("file to %s: %v\n", change.To.Name, err)
+				blobCache.l.Errorf("file to %s: %v\n", change.To.Name, err)
 			} else {
 				cb := &CachedBlob{Blob: *blob}
 				err = cb.Cache()
@@ -221,7 +228,7 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 					cache[change.To.TreeEntry.Hash] = cb
 					newCache[change.To.TreeEntry.Hash] = cb
 				} else {
-					log.Printf("file to %s: %v\n", change.To.Name, err)
+					blobCache.l.Errorf("file to %s: %v\n", change.To.Name, err)
 				}
 			}
 			cache[change.From.TreeEntry.Hash], exists =
@@ -230,14 +237,14 @@ func (blobCache *BlobCache) Consume(deps map[string]interface{}) (map[string]int
 				cache[change.From.TreeEntry.Hash] = &CachedBlob{}
 				blob, err = blobCache.getBlob(&change.From, commit.File)
 				if err != nil {
-					log.Printf("file from %s: %v\n", change.From.Name, err)
+					blobCache.l.Errorf("file from %s: %v\n", change.From.Name, err)
 				} else {
 					cb := &CachedBlob{Blob: *blob}
 					err = cb.Cache()
 					if err == nil {
 						cache[change.From.TreeEntry.Hash] = cb
 					} else {
-						log.Printf("file from %s: %v\n", change.From.Name, err)
+						blobCache.l.Errorf("file from %s: %v\n", change.From.Name, err)
 					}
 				}
 			}
@@ -279,7 +286,7 @@ func (blobCache *BlobCache) getBlob(entry *object.ChangeEntry, fileGetter FileGe
 
 	if err != nil {
 		if err.Error() != plumbing.ErrObjectNotFound.Error() {
-			log.Printf("getBlob(%s)\n", entry.TreeEntry.Hash.String())
+			blobCache.l.Errorf("getBlob(%s)\n", entry.TreeEntry.Hash.String())
 			return nil, err
 		}
 		if entry.TreeEntry.Mode != 0160000 {

+ 8 - 0
internal/plumbing/diff.go

@@ -18,6 +18,8 @@ type FileDiff struct {
 	core.NoopMerger
 	CleanupDisabled  bool
 	WhitespaceIgnore bool
+
+	l core.Logger
 }
 
 const (
@@ -84,6 +86,11 @@ func (diff *FileDiff) ListConfigurationOptions() []core.ConfigurationOption {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (diff *FileDiff) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		diff.l = l
+	} else {
+		diff.l = core.NewLogger()
+	}
 	if val, exists := facts[ConfigFileDiffDisableCleanup].(bool); exists {
 		diff.CleanupDisabled = val
 	}
@@ -96,6 +103,7 @@ func (diff *FileDiff) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (diff *FileDiff) Initialize(repository *git.Repository) error {
+	diff.l = core.NewLogger()
 	return nil
 }
 

+ 8 - 0
internal/plumbing/identity/identity.go

@@ -21,6 +21,8 @@ type Detector struct {
 	PeopleDict map[string]int
 	// ReversedPeopleDict maps developer id -> description
 	ReversedPeopleDict []string
+
+	l core.Logger
 }
 
 const (
@@ -84,6 +86,11 @@ func (detector *Detector) ListConfigurationOptions() []core.ConfigurationOption
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (detector *Detector) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		detector.l = l
+	} else {
+		detector.l = core.NewLogger()
+	}
 	if val, exists := facts[FactIdentityDetectorPeopleDict].(map[string]int); exists {
 		detector.PeopleDict = val
 	}
@@ -116,6 +123,7 @@ func (detector *Detector) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (detector *Detector) Initialize(repository *git.Repository) error {
+	detector.l = core.NewLogger()
 	return nil
 }
 

+ 8 - 0
internal/plumbing/languages.go

@@ -15,6 +15,8 @@ import (
 // LanguagesDetection run programming language detection over the changed files.
 type LanguagesDetection struct {
 	core.NoopMerger
+
+	l core.Logger
 }
 
 const (
@@ -50,12 +52,18 @@ func (langs *LanguagesDetection) ListConfigurationOptions() []core.Configuration
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (langs *LanguagesDetection) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		langs.l = l
+	} else {
+		langs.l = core.NewLogger()
+	}
 	return nil
 }
 
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (langs *LanguagesDetection) Initialize(repository *git.Repository) error {
+	langs.l = core.NewLogger()
 	return nil
 }
 

+ 6 - 0
internal/plumbing/line_stats.go

@@ -14,6 +14,8 @@ import (
 // LinesStatsCalculator measures line statistics for each text file in the commit.
 type LinesStatsCalculator struct {
 	core.NoopMerger
+
+	l core.Logger
 }
 
 // LineStats holds the numbers of inserted, deleted and changed lines.
@@ -60,12 +62,16 @@ func (lsc *LinesStatsCalculator) ListConfigurationOptions() []core.Configuration
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (lsc *LinesStatsCalculator) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		lsc.l = l
+	}
 	return nil
 }
 
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (lsc *LinesStatsCalculator) Initialize(repository *git.Repository) error {
+	lsc.l = core.NewLogger()
 	return nil
 }
 

+ 9 - 5
internal/plumbing/renames.go

@@ -1,7 +1,6 @@
 package plumbing
 
 import (
-	"log"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -30,6 +29,8 @@ type RenameAnalysis struct {
 	SimilarityThreshold int
 
 	repository *git.Repository
+
+	l core.Logger
 }
 
 const (
@@ -92,6 +93,9 @@ func (ra *RenameAnalysis) ListConfigurationOptions() []core.ConfigurationOption
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (ra *RenameAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ra.l = l
+	}
 	if val, exists := facts[ConfigRenameAnalysisSimilarityThreshold].(int); exists {
 		ra.SimilarityThreshold = val
 	}
@@ -101,8 +105,9 @@ func (ra *RenameAnalysis) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (ra *RenameAnalysis) Initialize(repository *git.Repository) error {
+	ra.l = core.NewLogger()
 	if ra.SimilarityThreshold < 0 || ra.SimilarityThreshold > 100 {
-		log.Printf("Warning: adjusted the similarity threshold to %d\n",
+		ra.l.Warnf("adjusted the similarity threshold to %d\n",
 			RenameAnalysisDefaultThreshold)
 		ra.SimilarityThreshold = RenameAnalysisDefaultThreshold
 	}
@@ -384,9 +389,8 @@ func (ra *RenameAnalysis) blobsAreClose(blob1 *CachedBlob, blob2 *CachedBlob) (b
 	cleanReturn := false
 	defer func() {
 		if !cleanReturn {
-			log.Println()
-			log.Println(blob1.Hash.String())
-			log.Println(blob2.Hash.String())
+			ra.l.Warnf("unclean return detected for blobs '%s' and '%s'\n",
+				blob1.Hash.String(), blob2.Hash.String())
 		}
 	}()
 	_, err1 := blob1.CountLines()

+ 7 - 3
internal/plumbing/ticks.go

@@ -1,7 +1,6 @@
 package plumbing
 
 import (
-	"log"
 	"time"
 
 	"gopkg.in/src-d/go-git.v4"
@@ -20,6 +19,8 @@ type TicksSinceStart struct {
 	tick0        *time.Time
 	previousTick int
 	commits      map[int][]plumbing.Hash
+
+	l core.Logger
 }
 
 const (
@@ -73,6 +74,9 @@ func (ticks *TicksSinceStart) ListConfigurationOptions() []core.ConfigurationOpt
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (ticks *TicksSinceStart) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ticks.l = l
+	}
 	if val, exists := facts[ConfigTicksSinceStartTickSize].(int); exists {
 		ticks.TickSize = time.Duration(val) * time.Hour
 	} else {
@@ -89,6 +93,7 @@ func (ticks *TicksSinceStart) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (ticks *TicksSinceStart) Initialize(repository *git.Repository) error {
+	ticks.l = core.NewLogger()
 	if ticks.TickSize == 0 {
 		ticks.TickSize = DefaultTicksSinceStartTickSize * time.Hour
 	}
@@ -120,8 +125,7 @@ func (ticks *TicksSinceStart) Consume(deps map[string]interface{}) (map[string]i
 		// our precision is 1 day
 		*ticks.tick0 = commit.Committer.When
 		if ticks.tick0.Unix() < 631152000 { // 01.01.1990, that was 30 years ago
-			log.Println()
-			log.Printf("Warning: suspicious committer timestamp in %s > %s: %d",
+			ticks.l.Warnf("suspicious committer timestamp in %s > %s: %d",
 				ticks.remote, commit.Hash.String(), ticks.tick0.Unix())
 		}
 	}

+ 6 - 0
internal/plumbing/tree_diff.go

@@ -29,6 +29,8 @@ type TreeDiff struct {
 	previousTree   *object.Tree
 	previousCommit plumbing.Hash
 	repository     *git.Repository
+
+	l core.Logger
 }
 
 const (
@@ -120,6 +122,9 @@ func (treediff *TreeDiff) ListConfigurationOptions() []core.ConfigurationOption
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (treediff *TreeDiff) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		treediff.l = l
+	}
 	if val, exists := facts[ConfigTreeDiffEnableBlacklist].(bool); exists && val {
 		treediff.SkipFiles = facts[ConfigTreeDiffBlacklistedPrefixes].([]string)
 	}
@@ -142,6 +147,7 @@ func (treediff *TreeDiff) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (treediff *TreeDiff) Initialize(repository *git.Repository) error {
+	treediff.l = core.NewLogger()
 	treediff.previousTree = nil
 	treediff.repository = repository
 	if treediff.Languages == nil {

+ 6 - 0
internal/plumbing/uast/diff_refiner.go

@@ -18,6 +18,8 @@ import (
 // optimal, choose the one which touches less AST nodes.
 type FileDiffRefiner struct {
 	core.NoopMerger
+
+	l core.Logger
 }
 
 // Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc.
@@ -54,12 +56,16 @@ func (ref *FileDiffRefiner) ListConfigurationOptions() []core.ConfigurationOptio
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (ref *FileDiffRefiner) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ref.l = l
+	}
 	return nil
 }
 
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (ref *FileDiffRefiner) Initialize(repository *git.Repository) error {
+	ref.l = core.NewLogger()
 	return nil
 }
 

+ 21 - 4
internal/plumbing/uast/uast.go

@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
-	"log"
 	"os"
 	"path"
 	"runtime"
@@ -16,7 +15,7 @@ import (
 
 	"github.com/Jeffail/tunny"
 	"github.com/gogo/protobuf/proto"
-	"gopkg.in/bblfsh/client-go.v3"
+	bblfsh "gopkg.in/bblfsh/client-go.v3"
 	"gopkg.in/bblfsh/sdk.v2/uast/nodes"
 	"gopkg.in/bblfsh/sdk.v2/uast/nodes/nodesproto"
 	"gopkg.in/src-d/go-git.v4"
@@ -41,6 +40,8 @@ type Extractor struct {
 
 	clients []*bblfsh.Client
 	pool    *tunny.Pool
+
+	l core.Logger
 }
 
 const (
@@ -159,6 +160,9 @@ func (exr *Extractor) ListConfigurationOptions() []core.ConfigurationOption {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (exr *Extractor) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		exr.l = l
+	}
 	if val, exists := facts[ConfigUASTEndpoint].(string); exists {
 		exr.Endpoint = val
 	}
@@ -186,6 +190,7 @@ func (exr *Extractor) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (exr *Extractor) Initialize(repository *git.Repository) error {
+	exr.l = core.NewLogger()
 	if exr.Context == nil {
 		exr.Context = func() (context.Context, context.CancelFunc) {
 			return context.WithTimeout(context.Background(),
@@ -207,7 +212,7 @@ func (exr *Extractor) Initialize(repository *git.Repository) error {
 		client, err := bblfsh.NewClient(exr.Endpoint)
 		if err != nil {
 			if err.Error() == "context deadline exceeded" {
-				log.Println("Looks like the Babelfish server is not running. Please refer " +
+				exr.l.Error("Looks like the Babelfish server is not running. Please refer " +
 					"to https://docs.sourced.tech/babelfish/using-babelfish/getting-started#running-with-docker-recommended")
 			}
 			return err
@@ -289,7 +294,7 @@ func (exr *Extractor) Consume(deps map[string]interface{}) (map[string]interface
 		if exr.FailOnErrors {
 			return nil, errors.New(joined)
 		}
-		log.Println(joined)
+		exr.l.Error(joined)
 	}
 	return map[string]interface{}{DependencyUasts: uasts}, nil
 }
@@ -362,6 +367,8 @@ const (
 type Changes struct {
 	core.NoopMerger
 	cache map[plumbing.Hash]nodes.Node
+
+	l core.Logger
 }
 
 // Name of this PipelineItem. Uniquely identifies the type, used for mapping keys, etc.
@@ -392,12 +399,16 @@ func (uc *Changes) ListConfigurationOptions() []core.ConfigurationOption {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (uc *Changes) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		uc.l = l
+	}
 	return nil
 }
 
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (uc *Changes) Initialize(repository *git.Repository) error {
+	uc.l = core.NewLogger()
 	uc.cache = map[plumbing.Hash]nodes.Node{}
 	return nil
 }
@@ -463,6 +474,8 @@ type ChangesSaver struct {
 
 	repository *git.Repository
 	result     [][]Change
+
+	l core.Logger
 }
 
 const (
@@ -515,6 +528,9 @@ func (saver *ChangesSaver) Description() string {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (saver *ChangesSaver) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		saver.l = l
+	}
 	if val, exists := facts[ConfigUASTChangesSaverOutputPath]; exists {
 		saver.OutputPath = val.(string)
 	}
@@ -524,6 +540,7 @@ func (saver *ChangesSaver) Configure(facts map[string]interface{}) error {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (saver *ChangesSaver) Initialize(repository *git.Repository) error {
+	saver.l = core.NewLogger()
 	saver.repository = repository
 	saver.result = [][]Change{}
 	saver.OneShotMergeProcessor.Initialize()

+ 17 - 9
leaves/burndown.go

@@ -105,6 +105,8 @@ type BurndownAnalysis struct {
 	tickSize time.Duration
 	// references IdentityDetector.ReversedPeopleDict
 	reversedPeopleDict []string
+
+	l core.Logger
 }
 
 // BurndownResult carries the result of running BurndownAnalysis - it is returned by
@@ -253,6 +255,11 @@ func (analyser *BurndownAnalysis) ListConfigurationOptions() []core.Configuratio
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (analyser *BurndownAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		analyser.l = l
+	} else {
+		analyser.l = core.NewLogger()
+	}
 	if val, exists := facts[ConfigBurndownGranularity].(int); exists {
 		analyser.Granularity = val
 	}
@@ -305,24 +312,25 @@ func (analyser *BurndownAnalysis) Description() string {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (analyser *BurndownAnalysis) Initialize(repository *git.Repository) error {
+	analyser.l = core.NewLogger()
 	if analyser.Granularity <= 0 {
-		log.Printf("Warning: adjusted the granularity to %d ticks\n",
+		analyser.l.Warnf("adjusted the granularity to %d ticks\n",
 			DefaultBurndownGranularity)
 		analyser.Granularity = DefaultBurndownGranularity
 	}
 	if analyser.Sampling <= 0 {
-		log.Printf("Warning: adjusted the sampling to %d ticks\n",
+		analyser.l.Warnf("adjusted the sampling to %d ticks\n",
 			DefaultBurndownGranularity)
 		analyser.Sampling = DefaultBurndownGranularity
 	}
 	if analyser.Sampling > analyser.Granularity {
-		log.Printf("Warning: granularity may not be less than sampling, adjusted to %d\n",
+		analyser.l.Warnf("granularity may not be less than sampling, adjusted to %d\n",
 			analyser.Granularity)
 		analyser.Sampling = analyser.Granularity
 	}
 	if analyser.tickSize == 0 {
 		def := items.DefaultTicksSinceStartTickSize * time.Hour
-		log.Printf("Warning: tick size was not set, adjusted to %v\n", def)
+		analyser.l.Warnf("tick size was not set, adjusted to %v\n", def)
 		analyser.tickSize = items.DefaultTicksSinceStartTickSize * time.Hour
 	}
 	analyser.repository = repository
@@ -1321,7 +1329,7 @@ func (analyser *BurndownAnalysis) handleModification(
 
 	thisDiffs := diffs[change.To.Name]
 	if file.Len() != thisDiffs.OldLinesOfCode {
-		log.Printf("====TREE====\n%s", file.Dump())
+		analyser.l.Infof("====TREE====\n%s", file.Dump())
 		return fmt.Errorf("%s: internal integrity error src %d != %d %s -> %s",
 			change.To.Name, thisDiffs.OldLinesOfCode, file.Len(),
 			change.From.TreeEntry.Hash.String(), change.To.TreeEntry.Hash.String())
@@ -1352,13 +1360,13 @@ func (analyser *BurndownAnalysis) handleModification(
 		}
 		length := utf8.RuneCountInString(edit.Text)
 		debugError := func() {
-			log.Printf("%s: internal diff error\n", change.To.Name)
-			log.Printf("Update(%d, %d, %d (0), %d (0))\n", analyser.tick, position,
+			analyser.l.Errorf("%s: internal diff error\n", change.To.Name)
+			analyser.l.Errorf("Update(%d, %d, %d (0), %d (0))\n", analyser.tick, position,
 				length, utf8.RuneCountInString(pending.Text))
 			if dumpBefore != "" {
-				log.Printf("====TREE BEFORE====\n%s====END====\n", dumpBefore)
+				analyser.l.Errorf("====TREE BEFORE====\n%s====END====\n", dumpBefore)
 			}
-			log.Printf("====TREE AFTER====\n%s====END====\n", file.Dump())
+			analyser.l.Errorf("====TREE AFTER====\n%s====END====\n", file.Dump())
 		}
 		switch edit.Type {
 		case diffmatchpatch.DiffEqual:

+ 9 - 4
leaves/comment_sentiment.go

@@ -5,7 +5,6 @@ package leaves
 import (
 	"fmt"
 	"io"
-	"log"
 	"os"
 	"regexp"
 	"sort"
@@ -21,7 +20,7 @@ import (
 	"gopkg.in/src-d/hercules.v10/internal/pb"
 	items "gopkg.in/src-d/hercules.v10/internal/plumbing"
 	uast_items "gopkg.in/src-d/hercules.v10/internal/plumbing/uast"
-	"gopkg.in/vmarkovtsev/BiDiSentiment.v1"
+	sentiment "gopkg.in/vmarkovtsev/BiDiSentiment.v1"
 )
 
 // CommentSentimentAnalysis measures comment sentiment through time.
@@ -34,6 +33,8 @@ type CommentSentimentAnalysis struct {
 	commentsByTick map[int][]string
 	commitsByTick  map[int][]plumbing.Hash
 	xpather        *uast_items.ChangesXPather
+
+	l core.Logger
 }
 
 // CommentSentimentResult contains the sentiment values per tick, where 1 means very negative
@@ -116,6 +117,9 @@ func (sent *CommentSentimentAnalysis) Description() string {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (sent *CommentSentimentAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		sent.l = l
+	}
 	if val, exists := facts[ConfigCommentSentimentGap]; exists {
 		sent.Gap = val.(float32)
 	}
@@ -129,12 +133,12 @@ func (sent *CommentSentimentAnalysis) Configure(facts map[string]interface{}) er
 
 func (sent *CommentSentimentAnalysis) validate() {
 	if sent.Gap < 0 || sent.Gap >= 1 {
-		log.Printf("Sentiment gap is too big: %f => reset to the default %f",
+		sent.l.Warnf("Sentiment gap is too big: %f => reset to the default %f",
 			sent.Gap, DefaultCommentSentimentGap)
 		sent.Gap = DefaultCommentSentimentGap
 	}
 	if sent.MinCommentLength < 10 {
-		log.Printf("Comment minimum length is too small: %d => reset to the default %d",
+		sent.l.Warnf("Comment minimum length is too small: %d => reset to the default %d",
 			sent.MinCommentLength, DefaultCommentSentimentCommentMinLength)
 		sent.MinCommentLength = DefaultCommentSentimentCommentMinLength
 	}
@@ -143,6 +147,7 @@ func (sent *CommentSentimentAnalysis) validate() {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (sent *CommentSentimentAnalysis) Initialize(repository *git.Repository) error {
+	sent.l = core.NewLogger()
 	sent.commentsByTick = map[int][]string{}
 	sent.xpather = &uast_items.ChangesXPather{XPath: "//uast:Comment"}
 	sent.validate()

+ 6 - 0
leaves/commits.go

@@ -23,6 +23,8 @@ type CommitsAnalysis struct {
 	commits []*CommitStat
 	// reversedPeopleDict references IdentityDetector.ReversedPeopleDict
 	reversedPeopleDict []string
+
+	l core.Logger
 }
 
 // CommitsResult is returned by CommitsAnalysis.Finalize() and carries the statistics
@@ -77,6 +79,9 @@ func (ca *CommitsAnalysis) ListConfigurationOptions() []core.ConfigurationOption
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (ca *CommitsAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		ca.l = l
+	}
 	if val, exists := facts[identity.FactIdentityDetectorReversedPeopleDict].([]string); exists {
 		ca.reversedPeopleDict = val
 	}
@@ -96,6 +101,7 @@ func (ca *CommitsAnalysis) Description() string {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (ca *CommitsAnalysis) Initialize(repository *git.Repository) error {
+	ca.l = core.NewLogger()
 	return nil
 }
 

+ 6 - 0
leaves/couples.go

@@ -38,6 +38,8 @@ type CouplesAnalysis struct {
 	lastCommit *object.Commit
 	// reversedPeopleDict references IdentityDetector.ReversedPeopleDict
 	reversedPeopleDict []string
+
+	l core.Logger
 }
 
 // CouplesResult is returned by CouplesAnalysis.Finalize() and carries couples matrices from
@@ -99,6 +101,9 @@ func (couples *CouplesAnalysis) ListConfigurationOptions() []core.ConfigurationO
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (couples *CouplesAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		couples.l = l
+	}
 	if val, exists := facts[identity.FactIdentityDetectorPeopleCount].(int); exists {
 		couples.PeopleNumber = val
 		couples.reversedPeopleDict = facts[identity.FactIdentityDetectorReversedPeopleDict].([]string)
@@ -121,6 +126,7 @@ func (couples *CouplesAnalysis) Description() string {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (couples *CouplesAnalysis) Initialize(repository *git.Repository) error {
+	couples.l = core.NewLogger()
 	couples.people = make([]map[string]int, couples.PeopleNumber+1)
 	for i := range couples.people {
 		couples.people[i] = map[string]int{}

+ 6 - 0
leaves/devs.go

@@ -31,6 +31,8 @@ type DevsAnalysis struct {
 	ticks map[int]map[int]*DevTick
 	// reversedPeopleDict references IdentityDetector.ReversedPeopleDict
 	reversedPeopleDict []string
+
+	l core.Logger
 }
 
 // DevsResult is returned by DevsAnalysis.Finalize() and carries the daily statistics
@@ -92,6 +94,9 @@ func (devs *DevsAnalysis) ListConfigurationOptions() []core.ConfigurationOption
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (devs *DevsAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		devs.l = l
+	}
 	if val, exists := facts[ConfigDevsConsiderEmptyCommits].(bool); exists {
 		devs.ConsiderEmptyCommits = val
 	}
@@ -114,6 +119,7 @@ func (devs *DevsAnalysis) Description() string {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (devs *DevsAnalysis) Initialize(repository *git.Repository) error {
+	devs.l = core.NewLogger()
 	devs.ticks = map[int]map[int]*DevTick{}
 	devs.OneShotMergeProcessor.Initialize()
 	return nil

+ 6 - 0
leaves/file_history.go

@@ -25,6 +25,8 @@ type FileHistoryAnalysis struct {
 	core.OneShotMergeProcessor
 	files      map[string]*FileHistory
 	lastCommit *object.Commit
+
+	l core.Logger
 }
 
 // FileHistoryResult is returned by Finalize() and represents the analysis result.
@@ -79,12 +81,16 @@ func (history *FileHistoryAnalysis) Description() string {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (history *FileHistoryAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		history.l = l
+	}
 	return nil
 }
 
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (history *FileHistoryAnalysis) Initialize(repository *git.Repository) error {
+	history.l = core.NewLogger()
 	history.files = map[string]*FileHistory{}
 	history.OneShotMergeProcessor.Initialize()
 	return nil

+ 8 - 3
leaves/research/typos.go

@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"fmt"
 	"io"
-	"log"
 	"unicode/utf8"
 
 	"github.com/gogo/protobuf/proto"
@@ -38,6 +37,8 @@ type TyposDatasetBuilder struct {
 	xpather uast_items.ChangesXPather
 	// remote carries the repository remote URL (for debugging)
 	remote string
+
+	l core.Logger
 }
 
 // TyposResult is returned by TyposDatasetBuilder.Finalize() and carries the found typo-fix
@@ -101,6 +102,9 @@ func (tdb *TyposDatasetBuilder) ListConfigurationOptions() []core.ConfigurationO
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (tdb *TyposDatasetBuilder) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		tdb.l = l
+	}
 	if val, exists := facts[ConfigTyposDatasetMaximumAllowedDistance].(int); exists {
 		tdb.MaximumAllowedDistance = val
 	}
@@ -120,6 +124,7 @@ func (tdb *TyposDatasetBuilder) Description() string {
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (tdb *TyposDatasetBuilder) Initialize(repository *git.Repository) error {
+	tdb.l = core.NewLogger()
 	if tdb.MaximumAllowedDistance <= 0 {
 		tdb.MaximumAllowedDistance = DefaultMaximumAllowedTypoDistance
 	}
@@ -198,7 +203,7 @@ func (tdb *TyposDatasetBuilder) Consume(deps map[string]interface{}) (map[string
 		for _, n := range nodesAdded {
 			pos := uast.PositionsOf(n.(nodes.Object))
 			if pos.Start() == nil {
-				log.Printf("repo %s commit %s file %s adds identifier %s with no position",
+				tdb.l.Infof("repo %s commit %s file %s adds identifier %s with no position",
 					tdb.remote, commit.String(), change.Change.To.Name,
 					n.(nodes.Object)["Name"].(nodes.String))
 				continue
@@ -211,7 +216,7 @@ func (tdb *TyposDatasetBuilder) Consume(deps map[string]interface{}) (map[string
 		for _, n := range nodesRemoved {
 			pos := uast.PositionsOf(n.(nodes.Object))
 			if pos.Start() == nil {
-				log.Printf("repo %s commit %s file %s removes identifier %s with no position",
+				tdb.l.Infof("repo %s commit %s file %s removes identifier %s with no position",
 					tdb.remote, commit.String(), change.Change.To.Name,
 					n.(nodes.Object)["Name"].(nodes.String))
 				continue

+ 9 - 4
leaves/shotness.go

@@ -3,7 +3,6 @@ package leaves
 import (
 	"fmt"
 	"io"
-	"log"
 	"sort"
 	"unicode/utf8"
 
@@ -31,6 +30,8 @@ type ShotnessAnalysis struct {
 
 	nodes map[string]*nodeShotness
 	files map[string]map[string]*nodeShotness
+
+	l core.Logger
 }
 
 const (
@@ -129,6 +130,9 @@ func (shotness *ShotnessAnalysis) Description() string {
 
 // Configure sets the properties previously published by ListConfigurationOptions().
 func (shotness *ShotnessAnalysis) Configure(facts map[string]interface{}) error {
+	if l, exists := facts[core.ConfigLogger].(core.Logger); exists {
+		shotness.l = l
+	}
 	if val, exists := facts[ConfigShotnessXpathStruct]; exists {
 		shotness.XpathStruct = val.(string)
 	} else {
@@ -145,6 +149,7 @@ func (shotness *ShotnessAnalysis) Configure(facts map[string]interface{}) error
 // Initialize resets the temporary caches and prepares this PipelineItem for a series of Consume()
 // calls. The repository which is going to be analysed is supplied as an argument.
 func (shotness *ShotnessAnalysis) Initialize(repository *git.Repository) error {
+	shotness.l = core.NewLogger()
 	shotness.nodes = map[string]*nodeShotness{}
 	shotness.files = map[string]map[string]*nodeShotness{}
 	shotness.OneShotMergeProcessor.Initialize()
@@ -209,7 +214,7 @@ func (shotness *ShotnessAnalysis) Consume(deps map[string]interface{}) (map[stri
 		if change.Before == nil {
 			nodes, err := shotness.extractNodes(change.After)
 			if err != nil {
-				log.Printf("Shotness: commit %s file %s failed to filter UAST: %s\n",
+				shotness.l.Warnf("Shotness: commit %s file %s failed to filter UAST: %s\n",
 					commit.Hash.String(), toName, err.Error())
 				continue
 			}
@@ -245,14 +250,14 @@ func (shotness *ShotnessAnalysis) Consume(deps map[string]interface{}) (map[stri
 		// pass through new UAST
 		nodesBefore, err := shotness.extractNodes(change.Before)
 		if err != nil {
-			log.Printf("Shotness: commit ^%s file %s failed to filter UAST: %s\n",
+			shotness.l.Warnf("Shotness: commit ^%s file %s failed to filter UAST: %s\n",
 				commit.Hash.String(), change.Change.From.Name, err.Error())
 			continue
 		}
 		reversedNodesBefore := reverseNodeMap(nodesBefore)
 		nodesAfter, err := shotness.extractNodes(change.After)
 		if err != nil {
-			log.Printf("Shotness: commit %s file %s failed to filter UAST: %s\n",
+			shotness.l.Warnf("Shotness: commit %s file %s failed to filter UAST: %s\n",
 				commit.Hash.String(), toName, err.Error())
 			continue
 		}