github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/cmd/geth/log_display_gitlike.go (about)

     1  /*
     2  2018-06-02 17:52:15 [INFO] | ~ Download start ⟪peer:ae6c86671cdc73b9@[Geth/v1.8.10-stable-eae63c51/linux-amd64/go1.10] eth/63⟫ hash=⟪0xc1c9a09…⟫ TD=⟪337709287732711584590⟫
     3  2018-06-02 17:52:16 [INFO] | ~ Download fail ⟪peer:ae6c86671cdc73b9@[Geth/v1.8.10-stable-eae63c51/linux-amd64/go1.10] eth/63⟫ err=⟪block fetch canceled (requested)⟫
     4  2018-06-02 17:52:25 [INFO] : Discover ➫⟪#5923278 0x93c4980…⟫  ✌︎︎︎⟪ 1/25 peers⟫  qos=⟪rtt=18s ttl=1m0s conf=1.00⟫
     5  2018-06-02 17:52:25 [INFO] | ~ Download start ⟪peer:20ec67d824529152@[Geth/v5.2.1-456053c/linux/go1.9.5] eth/63⟫ hash=⟪0x7246ca0…⟫ TD=⟪308559817009843748587⟫
     6  2018-06-02 17:52:29 [INFO] |\
     7  2018-06-02 17:52:29 [INFO] | * Insert blocks=⟪processed=   2 queued=   0 ignored=   0 txs=  32⟫ ◼=⟪n= 5923280 hash=0x6bba7fd… time=1m9.032s ago⟫ took=⟪2.885s⟫
     8  2018-06-02 17:52:29 [INFO] |/
     9  2018-06-02 17:52:29 [INFO] | ~ Download done ⟪peer:20ec67d824529152@[Geth/v5.2.1-456053c/linux/go1.9.5] eth/63⟫ hash=⟪0x7246ca0…⟫ TD=⟪308559817009843748587⟫
    10  2018-06-02 17:52:29 [INFO] '* Inserted blocks=⟪processed=   2 queued=   0 ignored=   0 txs=  10⟫ ◼=⟪n= 5923282 hash=0x7246ca0… time=1m2.094s ago⟫ took=⟪62ms⟫
    11  2018-06-02 17:52:35 [INFO] | ~ Download start ⟪peer:73683b51a0d62db7@[Geth/v1.8.0-stable-5f540757/linux-amd64/go1.9.4] eth/63⟫ hash=⟪0xca24109…⟫ TD=⟪4368733862078872596412⟫
    12  2018-06-02 17:52:55 [INFO] | ~ Download fail ⟪peer:73683b51a0d62db7@[Geth/v1.8.0-stable-5f540757/linux-amd64/go1.9.4] eth/63⟫ err=⟪block header download canceled (requested)⟫
    13  2018-06-02 17:52:55 [INFO] | ~ Download start ⟪peer:8f185d68e6f283ad@[Geth/v1.8.10-stable-eae63c51/linux-amd64/go1.10.1] eth/63⟫ hash=⟪0xfc7888d…⟫ TD=⟪4539449821322174516116⟫
    14  2018-06-02 17:52:56 [INFO] * Import ◼=⟪n= 5923283 hash=0x2bc7de5 miner=0x9eab4b0 time=1m6.7s ago⟫ peer=⟪7e2995638c8a3c7e⟫
    15  2018-06-02 17:53:01 [INFO] | ~ Download fail ⟪peer:8f185d68e6f283ad@[Geth/v1.8.10-stable-eae63c51/linux-amd64/go1.10.1] eth/63⟫ err=⟪block fetch canceled (requested)⟫
    16  2018-06-02 17:53:07 [INFO] * Import ◼=⟪n= 5923284 hash=0xb9df06b miner=0xdf7d7e0 time=12.478s ago⟫ peer=⟪20ec67d824529152⟫
    17  2018-06-02 17:53:19 [INFO] | ~ Download start ⟪peer:8b6d82f20bfdfe97@[Parity/v1.7.12-shapeshift-f96d152-20180109/x86_64-linux-gnu/rustc1.23.0] eth/63⟫ hash=⟪0xe41a94e…⟫ TD=⟪4539806318512030442218⟫
    18  2018-06-02 17:53:21 [WARN] | ~ Download fail ⟪peer:8b6d82f20bfdfe97@[Parity/v1.7.12-shapeshift-f96d152-20180109/x86_64-linux-gnu/rustc1.23.0] eth/63⟫ err=⟪empty header set by peer⟫
    19  2018-06-02 17:53:25 [INFO] | ~ Download start ⟪peer:c1853910b52ee0ed@[Geth/v1.8.7-stable-66432f38/linux-amd64/go1.10] eth/63⟫ hash=⟪0x9bdd0fc…⟫ TD=⟪4539672566919915992687⟫
    20  2018-06-02 17:53:26 [INFO] | ~ Download fail ⟪peer:c1853910b52ee0ed@[Geth/v1.8.7-stable-66432f38/linux-amd64/go1.10] eth/63⟫ err=⟪block fetch canceled (requested)⟫
    21  2018-06-02 17:53:45 [INFO] | ~ Download start ⟪peer:a13882fc0f60fd5e@[Geth/v1.7.2-stable-1db4ecdc/windows-amd64/go1.9] eth/63⟫ hash=⟪0x7de95fd…⟫ TD=⟪4539596261192099816569⟫
    22  2018-06-02 17:54:00 [INFO] | ~ Download fail ⟪peer:a13882fc0f60fd5e@[Geth/v1.7.2-stable-1db4ecdc/windows-amd64/go1.9] eth/63⟫ err=⟪block fetch canceled (requested)⟫
    23  2018-06-02 17:54:04 [INFO] * Import ◼=⟪n= 5923285 hash=0xf0255ba miner=0x9eab4b0 time=58.545s ago⟫ peer=⟪7e2995638c8a3c7e⟫
    24  2018-06-02 17:54:07 [INFO] * Import ◼=⟪n= 5923286 hash=0x89024f1 miner=0x1c0fa19 time=4.023s ago⟫ peer=⟪20ec67d824529152⟫
    25  2018-06-02 17:54:15 [INFO] | ~ Download start ⟪peer:a8300e12fbb0de00@[Geth/v1.8.6-patched-leveldb-8818ab0b/linux-amd64/go1.9.2] eth/63⟫ hash=⟪0x8264a5a…⟫ TD=⟪4539819098507839148947⟫
    26  */
    27  
    28  package main
    29  
    30  import (
    31  	"fmt"
    32  	"math/big"
    33  	"strings"
    34  	"time"
    35  
    36  	"github.com/ethereumproject/go-ethereum/core"
    37  	"github.com/ethereumproject/go-ethereum/eth"
    38  	"github.com/ethereumproject/go-ethereum/eth/downloader"
    39  	"github.com/ethereumproject/go-ethereum/eth/fetcher"
    40  	"github.com/ethereumproject/go-ethereum/logger"
    41  	"github.com/ethereumproject/go-ethereum/logger/glog"
    42  	"gopkg.in/urfave/cli.v1"
    43  )
    44  
    45  var firstInsertChainEvent = true
    46  var gitChainColorFn = func(e *eth.Ethereum) func(string) string {
    47  	origin, current, height, _, _ := e.Downloader().Progress()
    48  	if origin > 0 && current >= height {
    49  		return logger.ColorGreen
    50  	}
    51  	return func(s string) string { return s }
    52  }
    53  
    54  // greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic.
    55  var gitDisplaySystem = displayEventHandlers{
    56  	{
    57  		eventT: logEventDownloaderInsertChain,
    58  		ev:     downloader.InsertChainEvent{},
    59  		handlers: displayEventHandlerFns{
    60  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
    61  				switch d := evData.(type) {
    62  				case downloader.InsertChainEvent:
    63  					// set up colors
    64  					colorFn, colorParenFn := logger.ColorGreen, greenParenify
    65  					if d.Processed == 0 {
    66  						colorFn, colorParenFn = logger.ColorYellow, yellowParenify
    67  					}
    68  					masterChainColorFn := gitChainColorFn(e)
    69  					prefix := masterChainColorFn("|") + " " + logger.ColorGreen("*") + " Insert "
    70  					if firstInsertChainEvent && e.Downloader().Synchronising() {
    71  						glog.D(logger.Info).Infoln(masterChainColorFn("|") + logger.ColorGreen(`\`))
    72  						firstInsertChainEvent = false
    73  					} else if firstInsertChainEvent {
    74  						// downloader "finishes" before chain inserted function has finished writing
    75  						prefix = logger.ColorGreen("'*") + " Inserted "
    76  						firstInsertChainEvent = true
    77  					}
    78  
    79  					glog.D(logger.Info).Infof(prefix+colorFn("blocks")+"=%s "+colorFn("◼")+"=%s "+colorFn("took")+"=%s",
    80  						colorParenFn(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)),
    81  						colorParenFn(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))),
    82  						colorParenFn(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))),
    83  					)
    84  					if bool(glog.D(logger.Info)) {
    85  						chainEventLastSent = time.Now()
    86  					}
    87  				}
    88  			},
    89  		},
    90  	},
    91  	{
    92  		eventT: logEventDownloaderInsertHeaderChain,
    93  		ev:     downloader.InsertHeaderChainEvent{},
    94  		handlers: displayEventHandlerFns{
    95  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
    96  				if ctx.GlobalInt(DisplayFlag.Name) <= 3 {
    97  					return
    98  				}
    99  				switch d := evData.(type) {
   100  				case downloader.InsertHeaderChainEvent:
   101  					masterChainColorFn := gitChainColorFn(e)
   102  					if firstInsertChainEvent && e.Downloader().Synchronising() {
   103  						glog.D(logger.Info).Infoln(masterChainColorFn("|") + logger.ColorGreen(`\`))
   104  					}
   105  					firstInsertChainEvent = false
   106  					glog.D(logger.Info).Infof(masterChainColorFn("|")+" "+logger.ColorGreen("+")+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s",
   107  						greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)),
   108  						greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])),
   109  						greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))),
   110  					)
   111  					if bool(glog.D(logger.Info)) {
   112  						chainEventLastSent = time.Now()
   113  					}
   114  				}
   115  			},
   116  		},
   117  	},
   118  	{
   119  		eventT: logEventDownloaderInsertReceiptChain,
   120  		ev:     downloader.InsertReceiptChainEvent{},
   121  		handlers: displayEventHandlerFns{
   122  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   123  				if ctx.GlobalInt(DisplayFlag.Name) <= 3 {
   124  					return
   125  				}
   126  				switch d := evData.(type) {
   127  				case downloader.InsertReceiptChainEvent:
   128  					masterChainColorFn := gitChainColorFn(e)
   129  					if firstInsertChainEvent && e.Downloader().Synchronising() {
   130  						glog.D(logger.Info).Infoln(masterChainColorFn("|") + logger.ColorGreen(`\`))
   131  					}
   132  					firstInsertChainEvent = false
   133  					glog.D(logger.Info).Infof(masterChainColorFn("|")+" "+logger.ColorGreen("=")+" Insert "+logger.ColorGreen("receipts")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s",
   134  						greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)),
   135  						greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])),
   136  						greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))),
   137  					)
   138  					if bool(glog.D(logger.Info)) {
   139  						chainEventLastSent = time.Now()
   140  					}
   141  				}
   142  			},
   143  		},
   144  	},
   145  	{
   146  		eventT: logEventFetcherInsert,
   147  		ev:     fetcher.FetcherInsertBlockEvent{},
   148  		handlers: displayEventHandlerFns{
   149  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   150  				switch d := evData.(type) {
   151  				case fetcher.FetcherInsertBlockEvent:
   152  					glog.D(logger.Info).Infof(logger.ColorGreen("*")+" Import "+logger.ColorGreen("◼")+"=%s "+"peer=%s",
   153  						greenParenify(fmt.Sprintf("n=%8d hash=%s miner=%s time=%v ago",
   154  							d.Block.NumberU64(),
   155  							d.Block.Hash().Hex()[:9],
   156  							d.Block.Coinbase().Hex()[:9],
   157  							time.Since(time.Unix(d.Block.Time().Int64(), 0)).Round(time.Millisecond))),
   158  						greenParenify(d.Peer),
   159  					)
   160  					if bool(glog.D(logger.Info)) {
   161  						chainEventLastSent = time.Now()
   162  					}
   163  				}
   164  			},
   165  		},
   166  	},
   167  	{
   168  		eventT: logEventCoreChainInsertSide,
   169  		ev:     core.ChainSideEvent{},
   170  		handlers: displayEventHandlerFns{
   171  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   172  				switch d := evData.(type) {
   173  				case core.ChainSideEvent:
   174  					masterChainColorFn := gitChainColorFn(e)
   175  					glog.D(logger.Info).Infoln(masterChainColorFn("|") + logger.ColorYellow(`\`))
   176  					glog.D(logger.Info).Infof(masterChainColorFn("|")+" "+logger.ColorYellow("*")+" Insert "+logger.ColorYellow("forked block")+"=%s", yellowParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9])))
   177  				}
   178  			},
   179  		},
   180  	},
   181  	{
   182  		eventT: logEventCoreMinedBlock,
   183  		ev:     core.NewMinedBlockEvent{},
   184  		handlers: displayEventHandlerFns{
   185  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   186  				switch d := evData.(type) {
   187  				case core.NewMinedBlockEvent:
   188  					glog.D(logger.Info).Infof(logger.ColorGreen("*) Mined") + " " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d",
   189  						d.Block.NumberU64(),
   190  						d.Block.Hash().Hex()[:9],
   191  						d.Block.Coinbase().Hex()[:9],
   192  						len(d.Block.Transactions()),
   193  						len(d.Block.Uncles()),
   194  					)))
   195  				}
   196  			},
   197  		},
   198  	},
   199  	{
   200  		eventT: logEventDownloaderStart,
   201  		ev:     downloader.StartEvent{},
   202  		handlers: displayEventHandlerFns{
   203  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   204  				if ctx.GlobalInt(DisplayFlag.Name) < 4 {
   205  					return
   206  				}
   207  				switch d := evData.(type) {
   208  				case downloader.StartEvent:
   209  					masterChainColorFn := gitChainColorFn(e)
   210  					s := masterChainColorFn("|") + " " + logger.ColorGreen("~") + " Download " + logger.ColorGreen("start") + " " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD))
   211  					glog.D(logger.Info).Infoln(s)
   212  				}
   213  			},
   214  		},
   215  	},
   216  	{
   217  		eventT: logEventDownloaderDone,
   218  		ev:     downloader.DoneEvent{},
   219  		handlers: displayEventHandlerFns{
   220  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   221  				if ctx.GlobalInt(DisplayFlag.Name) < 4 {
   222  					return
   223  				}
   224  				switch d := evData.(type) {
   225  				case downloader.DoneEvent:
   226  					masterChainColorFn := gitChainColorFn(e)
   227  					s := masterChainColorFn("|") + " " + logger.ColorGreen("~") + " Download " + logger.ColorGreen("done") + " " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD))
   228  					glog.D(logger.Info).Infoln("|" + logger.ColorGreen(`/`))
   229  					glog.D(logger.Info).Infoln(s)
   230  					firstInsertChainEvent = true
   231  				}
   232  			},
   233  		},
   234  	},
   235  	{
   236  		eventT: logEventDownloaderFailed,
   237  		ev:     downloader.FailedEvent{},
   238  		handlers: displayEventHandlerFns{
   239  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   240  				if ctx.GlobalInt(DisplayFlag.Name) < 4 {
   241  					return
   242  				}
   243  				switch d := evData.(type) {
   244  				case downloader.FailedEvent:
   245  					masterChainColorFn := gitChainColorFn(e)
   246  					s := masterChainColorFn("|") + " " + logger.ColorYellow("~") + " Download " + logger.ColorYellow("fail") + " " + yellowParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorYellow("err") + "=" + yellowParenify(d.Err.Error())
   247  					if downloader.ErrWasRequested(d.Err) {
   248  						glog.D(logger.Info).Infoln(s)
   249  					} else {
   250  						glog.D(logger.Warn).Warnln(s)
   251  					}
   252  					firstInsertChainEvent = true
   253  				}
   254  			},
   255  		},
   256  	},
   257  	{
   258  		eventT: logEventInterval,
   259  		handlers: displayEventHandlerFns{
   260  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   261  				if (ctx.GlobalInt(DisplayFlag.Name) <= 3 && e.Downloader().GetMode() == downloader.FastSync) || time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()))) {
   262  					currentBlockNumber = PrintStatusGit(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx)))
   263  				}
   264  			},
   265  		},
   266  	},
   267  	{
   268  		eventT: logEventBefore,
   269  		handlers: displayEventHandlerFns{
   270  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   271  				currentBlockNumber = e.BlockChain().CurrentFastBlock().NumberU64()
   272  			},
   273  		},
   274  	},
   275  }
   276  
   277  // PrintStatusGreen implements the displayEventHandlerFn interface
   278  var PrintStatusGit = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 {
   279  	lenPeers := e.Downloader().GetPeers().Len()
   280  
   281  	rtt, ttl, conf := e.Downloader().Qos()
   282  	confS := fmt.Sprintf("%01.2f", conf)
   283  	qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS)
   284  
   285  	_, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known
   286  	mode := e.Downloader().GetMode()
   287  
   288  	blockchain := e.BlockChain()
   289  	currentBlockHex := blockchain.CurrentBlock().Hash().Hex()
   290  	if mode == downloader.FastSync {
   291  		fb := e.BlockChain().CurrentFastBlock()
   292  		current = fb.NumberU64()
   293  		currentBlockHex = fb.Hash().Hex()
   294  	}
   295  
   296  	// Get our head block
   297  	// Discover -> not synchronising (searching for peers)
   298  	// FullSync/FastSync -> synchronising
   299  	// Import -> synchronising, at full height
   300  	fOfHeight := fmt.Sprintf("%7d", height)
   301  
   302  	// Calculate and format percent sync of known height
   303  	heightRatio := float64(current) / float64(height)
   304  	heightRatio = heightRatio * 100
   305  	fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio)
   306  
   307  	// Wait until syncing because real dl mode will not be engaged until then
   308  	if currentMode == lsModeImport {
   309  		fOfHeight = ""    // strings.Repeat(" ", 12)
   310  		fHeightRatio = "" // strings.Repeat(" ", 7)
   311  	}
   312  	if height == 0 {
   313  		fOfHeight = ""    // strings.Repeat(" ", 12)
   314  		fHeightRatio = "" // strings.Repeat(" ", 7)
   315  	}
   316  
   317  	// Calculate block stats for interval
   318  	numBlocksDiff := current - currentBlockNumber
   319  	numTxsDiff := 0
   320  	mGas := new(big.Int)
   321  
   322  	var numBlocksDiffPerSecond uint64
   323  	var numTxsDiffPerSecond int
   324  	var mGasPerSecond = new(big.Int)
   325  
   326  	var dominoGraph string
   327  	var nDom int
   328  	if numBlocksDiff > 0 && numBlocksDiff != current {
   329  		for i := currentBlockNumber + 1; i <= current; i++ {
   330  			b := blockchain.GetBlockByNumber(i)
   331  			if b != nil {
   332  				txLen := b.Transactions().Len()
   333  				// Add to tallies
   334  				numTxsDiff += txLen
   335  				mGas = new(big.Int).Add(mGas, b.GasUsed())
   336  				// Domino effect
   337  				if currentMode == lsModeImport {
   338  					if txLen > len(dominoes)-1 {
   339  						// prevent slice out of bounds
   340  						txLen = len(dominoes) - 1
   341  					}
   342  					if nDom <= 20 {
   343  						dominoGraph += dominoes[txLen]
   344  					}
   345  					nDom++
   346  				}
   347  			}
   348  		}
   349  		if nDom > 20 {
   350  			dominoGraph += "…"
   351  		}
   352  	}
   353  	dominoGraph = logger.ColorGreen(dominoGraph)
   354  
   355  	// Convert to per-second stats
   356  	// FIXME(?): Some degree of rounding will happen.
   357  	// For example, if interval is 10s and we get 6 blocks imported in that span,
   358  	// stats will show '0' blocks/second. Looks a little strange; but on the other hand,
   359  	// precision costs visual space, and normally just looks weird when starting up sync or
   360  	// syncing slowly.
   361  	numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds())
   362  
   363  	// Don't show initial current / per second val
   364  	if currentBlockNumber == 0 {
   365  		numBlocksDiffPerSecond = 0
   366  		numBlocksDiff = 0
   367  	}
   368  
   369  	// Divide by interval to yield per-second stats
   370  	numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds())
   371  	mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds())))
   372  	mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000))
   373  	mGasPerSecondI := mGasPerSecond.Int64()
   374  
   375  	// Format head block hex for printing (eg. d4e…fa3)
   376  	cbhexstart := currentBlockHex[:9] // trim off '0x' prefix
   377  
   378  	localHeadHeight := fmt.Sprintf("#%7d", current)
   379  	localHeadHex := fmt.Sprintf("%s…", cbhexstart)
   380  	peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers)
   381  	domOrHeight := fOfHeight + " " + fHeightRatio
   382  	if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 {
   383  		domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight)
   384  	} else {
   385  		domOrHeight = ""
   386  	}
   387  	var blocksprocesseddisplay string
   388  	qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay)
   389  	if currentMode != lsModeImport {
   390  		blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas  "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI))
   391  	} else {
   392  		blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64()))
   393  		domOrHeight = dominoGraph
   394  		qosDisplayable = ""
   395  	}
   396  	if currentMode == lsModeDiscover {
   397  		blocksprocesseddisplay = ""
   398  	}
   399  
   400  	// Log to ERROR.
   401  	headDisplay := greenParenify(localHeadHeight + " " + localHeadHex)
   402  	peerDisplay := greenParenify(peersOfMax)
   403  
   404  	modeIcon := logger.ColorGreen(lsModeIcon[currentMode])
   405  	if currentMode == lsModeDiscover {
   406  		// TODO: spin me
   407  		modeIcon = lsModeDiscoverSpinners[0]
   408  	}
   409  	modeIcon = logger.ColorGreen(modeIcon)
   410  
   411  	// This allows maximum user optionality for desired integration with rest of event-based logging.
   412  	glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s",
   413  		logger.ColorBlue(": "+currentMode.String()), headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable)
   414  	return current
   415  }