Skip to content
Snippets Groups Projects
plugin.go 4.78 KiB
Newer Older
// Package remotelog is a plugin that enables log messages being sent via UDP to a central ELK stack for debugging.
// It is disabled by default and when enabled, additionally, logger.disableEvents=false in config.json needs to be set.
// The destination can be set via logger.remotelog.serverAddress.
// All events according to logger.level in config.json are sent.
package remotelog

import (
	"os"
	"path/filepath"
	"runtime"
Jonas Theis's avatar
Jonas Theis committed
	"sync"
	"time"

	"github.com/iotaledger/goshimmer/packages/shutdown"
	"github.com/iotaledger/goshimmer/plugins/autopeering/local"
Hans Moog's avatar
Hans Moog committed
	"github.com/iotaledger/goshimmer/plugins/banner"
	"github.com/iotaledger/goshimmer/plugins/config"
	"github.com/iotaledger/hive.go/daemon"
	"github.com/iotaledger/hive.go/events"
	"github.com/iotaledger/hive.go/logger"
	"github.com/iotaledger/hive.go/node"
	"github.com/iotaledger/hive.go/workerpool"
	flag "github.com/spf13/pflag"
	"gopkg.in/src-d/go-git.v4"
	// CfgLoggerRemotelogServerAddress defines the config flag of the server address.
	CfgLoggerRemotelogServerAddress = "logger.remotelog.serverAddress"
	// CfgDisableEvents defines the config flag for disabling logger events.
	CfgDisableEvents = "logger.disableEvents"
	// PluginName is the name of the remote log plugin.
	PluginName = "RemoteLog"
Jonas Theis's avatar
Jonas Theis committed

	remoteLogType = "log"
	// plugin is the plugin instance of the remote plugin instance.
	plugin      *node.Plugin
	pluginOnce	sync.Once
	log         *logger.Logger
	myID        string
	myGitHead   string
	myGitBranch string
	workerPool  *workerpool.WorkerPool
Jonas Theis's avatar
Jonas Theis committed

	remoteLogger     *RemoteLoggerConn
	remoteLoggerOnce sync.Once
// Plugin gets the plugin instance.
func Plugin() *node.Plugin {
	pluginOnce.Do(func() {
		plugin = node.NewPlugin(PluginName, node.Disabled, configure, run)
	})
	return plugin
}

func init() {
	flag.String(CfgLoggerRemotelogServerAddress, "remotelog.goshimmer.iota.cafe:5213", "RemoteLog server address")
}

func configure(plugin *node.Plugin) {
	log = logger.NewLogger(PluginName)
	if config.Node().GetBool(CfgDisableEvents) {
		log.Fatalf("%s in config.json needs to be false so that events can be captured!", CfgDisableEvents)
Jonas Theis's avatar
Jonas Theis committed
	// initialize remote logger connection
	RemoteLogger()

	if local.GetInstance() != nil {
		myID = local.GetInstance().ID().String()
	getGitInfo()

	workerPool = workerpool.New(func(task workerpool.Task) {
		sendLogMsg(task.Param(0).(logger.Level), task.Param(1).(string), task.Param(2).(string))

		task.Return(nil)
	}, workerpool.WorkerCount(runtime.GOMAXPROCS(0)), workerpool.QueueSize(1000))
}

func run(plugin *node.Plugin) {
	logEvent := events.NewClosure(func(level logger.Level, name string, msg string) {
		workerPool.TrySubmit(level, name, msg)
	})

	if err := daemon.BackgroundWorker(PluginName, func(shutdownSignal <-chan struct{}) {
		logger.Events.AnyMsg.Attach(logEvent)
		workerPool.Start()
		<-shutdownSignal
		log.Infof("Stopping %s ...", PluginName)
		logger.Events.AnyMsg.Detach(logEvent)
		workerPool.Stop()
		log.Infof("Stopping %s ... done", PluginName)
	}, shutdown.PriorityRemoteLog); err != nil {
		log.Panicf("Failed to start as daemon: %s", err)
	}
}

func sendLogMsg(level logger.Level, name string, msg string) {
	m := logMessage{
Hans Moog's avatar
Hans Moog committed
		banner.AppVersion,
		myGitHead,
		myGitBranch,
		myID,
		level.CapitalString(),
		name,
		msg,
		time.Now(),
Jonas Theis's avatar
Jonas Theis committed
		remoteLogType,
Jonas Theis's avatar
Jonas Theis committed

	_ = RemoteLogger().Send(m)

func getGitInfo() {
	r, err := git.PlainOpen(getGitDir())
	if err != nil {
		log.Debug("Could not open Git repo.")
		return
	}

	// extract git branch and head
	if h, err := r.Head(); err == nil {
		myGitBranch = h.Name().String()
		myGitHead = h.Hash().String()
	}
}

func getGitDir() string {
	var gitDir string

	// this is valid when running an executable, when using "go run" this is a temp path
	if ex, err := os.Executable(); err == nil {
		temp := filepath.Join(filepath.Dir(ex), ".git")
		if _, err := os.Stat(temp); err == nil {
			gitDir = temp
		}
	}

	// when running "go run" from the same directory
	if gitDir == "" {
		if wd, err := os.Getwd(); err == nil {
			temp := filepath.Join(wd, ".git")
			if _, err := os.Stat(temp); err == nil {
				gitDir = temp
			}
		}
	}

	return gitDir
}
Jonas Theis's avatar
Jonas Theis committed

// RemoteLogger represents a connection to our remote log server.
func RemoteLogger() *RemoteLoggerConn {
	remoteLoggerOnce.Do(func() {
		r, err := newRemoteLoggerConn(config.Node().GetString(CfgLoggerRemotelogServerAddress))
Jonas Theis's avatar
Jonas Theis committed
		if err != nil {
			log.Fatal(err)
			return
		}

		remoteLogger = r
	})

	return remoteLogger
}

type logMessage struct {
	Version   string    `json:"version"`
	GitHead   string    `json:"gitHead,omitempty"`
	GitBranch string    `json:"gitBranch,omitempty"`
	NodeID    string    `json:"nodeId"`
	Level     string    `json:"level"`
	Name      string    `json:"name"`
	Msg       string    `json:"msg"`
	Timestamp time.Time `json:"timestamp"`
	Type      string    `json:"type"`
}