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

     1  // ---
     2  //2017-12-19 08:55:03 Discover ➫⟪#      0 0xd4e5674…⟫ ~⟪   0 blks    0 txs  0 mgas  /sec⟫ ✌︎︎︎⟪ 1/25 peers⟫  qos=⟪rtt=18s ttl=1m0s conf=1.00⟫
     3  //2017-12-19 08:55:17 ◼⋯⋯⬇ Start ⟪Peer id=931f21921fcc7654 eth/63 [Parity/v1.8.3-beta-b49c44a-20171114/x86_64-linux-gnu/rustc1.21.0] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss    0, rtt 20s]⟫ hash=⟪0xc5ea430…⟫ TD=⟪185549045030301150788⟫
     4  //2017-12-19 08:55:19 ◼⋯⋯❐ Insert headers=⟪processed=   0 ignored= 192⟫ ❐=⟪n=1836399 hash=0x1ecf991…⟫took=⟪73.546ms⟫
     5  //2017-12-19 08:55:20 ◼⋯⋯❐ Insert headers=⟪processed=   0 ignored= 192⟫ ❐=⟪n=1836591 hash=0xcff7c3c…⟫took=⟪275.686ms⟫
     6  //2017-12-19 08:57:25 ◼⋯⋯⬇ Fail  ⟪Peer id=931f21921fcc7654 eth/63 [Parity/v1.8.3-beta-b49c44a-20171114/x86_64-linux-gnu/rustc1.21.0] [hs 211.71/s, bs 124.20/s, rs 184.06/s, ss 735.50/s, miss    0, rtt 1.512187158s]⟫ err=⟪content processing canceled (requested)⟫
     7  //2017-12-19 09:00:43 ◼⋯⋯⬇ Start ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss    0, rtt 20s]⟫ hash=⟪0x26f9a91…⟫ TD=⟪185552382866154347587⟫
     8  //2017-12-19 09:00:46 Sync ︎◉⟪#5043160 0xed71bef…⟫ ~⟪   0 blks    0 txs  0 mgas  /sec⟫ ✌︎︎︎⟪ 5/25 peers⟫ height=⟪5047421 99.92%⟫ qos=⟪rtt=15.942s ttl=1m0s conf=0.70⟫
     9  //2017-12-19 09:00:51 ◼⋯⋯◼ Insert blocks=⟪processed=  48 queued=   0 ignored=   0 txs= 341⟫ ◼=⟪n= 5043208 hash=0x195123c… time=16h55m40.639s ago⟫ took=⟪5.692s⟫
    10  //2017-12-19 09:01:47 Sync ︎◉⟪#5044175 0xedb389d…⟫ ~⟪  16 blks  142 txs  3 mgas  /sec⟫ ✌︎︎︎⟪ 8/25 peers⟫ height=⟪5047421 99.94%⟫ qos=⟪rtt=9.585s ttl=36.603s conf=0.87⟫
    11  //2017-12-19 09:02:43 ◼⋯⋯◼ Insert blocks=⟪processed=2048 queued=   0 ignored=   0 txs=17899⟫ ◼=⟪n= 5045256 hash=0xcf3d3fb… time=8h49m5.014s ago⟫ took=⟪1m51.251s⟫
    12  //2017-12-19 09:03:47 Sync ︎◉⟪#5046394 0x8b10bdd…⟫ ~⟪  36 blks  306 txs  9 mgas  /sec⟫ ✌︎︎︎⟪11/25 peers⟫ height=⟪5047421 99.98%⟫ qos=⟪rtt=6.695s ttl=22.328s conf=1.00⟫
    13  //2017-12-19 09:04:43 ◼⋯⋯◼ Insert blocks=⟪processed=2048 queued=   0 ignored=   0 txs=18124⟫ ◼=⟪n= 5047304 hash=0xc0b4780… time=33m45.455s ago⟫ took=⟪2m0.462s⟫
    14  //2017-12-19 09:04:50 ◼⋯⋯⬇ Done  ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 813.97/s, bs 295.27/s, rs 0.00/s, ss 0.00/s, miss    0, rtt 1.918361657s]⟫ hash=⟪0x26f9a91…⟫ TD=⟪185552382866154347587⟫
    15  //2017-12-19 09:04:50 ◼⋯⋯◼ Insert blocks=⟪processed= 118 queued=   0 ignored=   0 txs=1183⟫ ◼=⟪n= 5047422 hash=0xc47a4fc… time=4m24.711s ago⟫ took=⟪7.258s⟫
    16  //2017-12-19 09:04:51 ◼⋯⋯⬇ Start ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 0.00/s, rs 0.00/s, ss 0.00/s, miss    0, rtt 1.918361657s]⟫ hash=⟪0x249c344…⟫ TD=⟪185554240788446349916⟫
    17  //2017-12-19 09:04:52 ◼⋯⋯◼ Insert blocks=⟪processed=   4 queued=   0 ignored=   0 txs=  53⟫ ◼=⟪n= 5047426 hash=0x6aff1b9… time=3m21.897s ago⟫ took=⟪185ms⟫
    18  //2017-12-19 09:04:53 ◼⋯⋯⬇ Done  ⟪Peer id=f30acc4f9c1a4369 eth/63 [Parity/v1.8.4-beta-c74c8c1-20171211/x86_64-linux-gnu/rustc1.22.1] [hs 0.00/s, bs 1.18/s, rs 0.00/s, ss 0.00/s, miss    0, rtt 1.743539409s]⟫ hash=⟪0x249c344…⟫ TD=⟪185554240788446349916⟫
    19  //2017-12-19 09:04:53 ◼⋯⋯◼ Insert blocks=⟪processed=  11 queued=   0 ignored=   0 txs= 119⟫ ◼=⟪n= 5047437 hash=0x3950d3d… time=1m27.504s ago⟫ took=⟪735ms⟫
    20  //2017-12-19 09:09:53 ◼⋯⋯◼ Insert blocks=⟪processed=  13 queued=   0 ignored=   0 txs= 124⟫ ◼=⟪n= 5047453 hash=0xff69a28… time=19.599s ago⟫ took=⟪630ms⟫
    21  //2017-12-19 09:10:11 ◼⋯⋯◼ Insert blocks=⟪processed=   1 queued=   0 ignored=   0 txs=   1⟫ ◼=⟪n= 5047454 hash=0xd03268a… time=34.122s ago⟫ took=⟪8ms⟫
    22  //2017-12-19 09:10:18 ◼⋯⋯◼ Insert blocks=⟪processed=   1 queued=   0 ignored=   0 txs=   7⟫ ◼=⟪n= 5047455 hash=0x1364b57… time=13.649s ago⟫ took=⟪17ms⟫
    23  //2017-12-19 09:10:25 ◼⋯⋯◼ Insert blocks=⟪processed=   1 queued=   0 ignored=   0 txs=   6⟫ ◼=⟪n= 5047456 hash=0x00ee977… time=12.328s ago⟫ took=⟪23ms⟫
    24  // ---
    25  
    26  package main
    27  
    28  import (
    29  	"fmt"
    30  	"math/big"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/ethereumproject/go-ethereum/core"
    35  	"github.com/ethereumproject/go-ethereum/eth"
    36  	"github.com/ethereumproject/go-ethereum/eth/downloader"
    37  	"github.com/ethereumproject/go-ethereum/eth/fetcher"
    38  	"github.com/ethereumproject/go-ethereum/logger"
    39  	"github.com/ethereumproject/go-ethereum/logger/glog"
    40  	"gopkg.in/urfave/cli.v1"
    41  )
    42  
    43  var lsModeIcon = []string{
    44  	"",
    45  	"︎◉",
    46  	"◎",
    47  	"▶︎",
    48  }
    49  
    50  var dominoes = []string{"🁣", "🁤", "🁥", "🁦", "🁭", "🁴", "🁻", "🁼", "🂃", "🂄", "🂋", "🂌", "🂓"} // 🁣🁤🁥🁦🁭🁴🁻🁼🂃🂄🂋🂌🂓
    51  var chainIcon = "◼⋯⋯" + logger.ColorGreen("◼")
    52  var forkIcon = "◼⋯⦦" + logger.ColorGreen("◼")
    53  var headerIcon = "◼⋯⋯" + logger.ColorGreen("❐")
    54  var downloaderIconStart = "◼⋯⋯" + logger.ColorGreen("⬇")
    55  var downloaderIconDone = "◼⋯⋯" + logger.ColorGreen("✔︎")
    56  var downloaderIconFail = "◼⋯⋯" + logger.ColorRed("✕")
    57  var minedIcon = "◼⋯⋯" + logger.ColorGreen("⟠")
    58  var lsModeDiscoverSpinners = []string{"➫", "➬", "➭"}
    59  var downloadingFrom = ""
    60  
    61  func greenParenify(s string) string {
    62  	return logger.ColorGreen("⟪") + s + logger.ColorGreen("⟫")
    63  }
    64  func redParenify(s string) string {
    65  	return logger.ColorRed("⟪") + s + logger.ColorRed("⟫")
    66  }
    67  func yellowParenify(s string) string {
    68  	return logger.ColorYellow("⟪") + s + logger.ColorYellow("⟫")
    69  }
    70  func prefix(ev interface{}, e *eth.Ethereum) string {
    71  	//s := "⋮⫟⫠⫶|"⇶⇉⇣⇣⥥⤹↙⤹⎯⏐↵↳⤶⤷⤵↔
    72  	switch d := ev.(type) {
    73  	case downloader.StartEvent:
    74  		downloadingFrom = d.Peer.String()
    75  		return logger.ColorGreen(`⤹  `)
    76  	case downloader.DoneEvent:
    77  		downloadingFrom = ""
    78  		return logger.ColorGreen(`⤷  `)
    79  	case downloader.FailedEvent:
    80  		downloadingFrom = ""
    81  		return logger.ColorRed(`⤷  `)
    82  	case downloader.InsertChainEvent:
    83  		return logger.ColorYellow(`⇣  `)
    84  	//case fetcher.FetcherInsertBlockEvent:
    85  	default:
    86  	}
    87  	return ""
    88  }
    89  
    90  // greenDisplaySystem is "spec'd" in PR #423 and is a little fancier/more detailed and colorful than basic.
    91  var greenDisplaySystem = displayEventHandlers{
    92  	{
    93  		eventT: logEventDownloaderInsertChain,
    94  		ev:     downloader.InsertChainEvent{},
    95  		handlers: displayEventHandlerFns{
    96  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
    97  				switch d := evData.(type) {
    98  				case downloader.InsertChainEvent:
    99  					colorFn, colorParenFn := logger.ColorGreen, greenParenify
   100  					if d.Processed == 0 {
   101  						colorFn, colorParenFn = logger.ColorYellow, yellowParenify
   102  					}
   103  					glog.D(logger.Info).Infof(prefix(d, e)+chainIcon+" Insert "+colorFn("blocks")+"=%s "+colorFn("◼")+"=%s "+colorFn("took")+"=%s",
   104  						colorParenFn(fmt.Sprintf("processed=%4d queued=%4d ignored=%4d txs=%4d", d.Processed, d.Queued, d.Ignored, d.TxCount)),
   105  						colorParenFn(fmt.Sprintf("n=%8d hash=%s… time=%v ago", d.LastNumber, d.LastHash.Hex()[:9], time.Since(d.LatestBlockTime).Round(time.Millisecond))),
   106  						colorParenFn(fmt.Sprintf("%v", d.Elasped.Round(time.Millisecond))),
   107  					)
   108  					if bool(glog.D(logger.Info)) {
   109  						chainEventLastSent = time.Now()
   110  					}
   111  				}
   112  			},
   113  		},
   114  	},
   115  	{
   116  		eventT: logEventFetcherInsert,
   117  		ev:     fetcher.FetcherInsertBlockEvent{},
   118  		handlers: displayEventHandlerFns{
   119  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   120  				switch d := evData.(type) {
   121  				case fetcher.FetcherInsertBlockEvent:
   122  					glog.D(logger.Info).Infof(prefix(d, e)+chainIcon+" Import "+logger.ColorGreen("◼")+"=%s "+"peer=%s",
   123  						greenParenify(fmt.Sprintf("n=%8d hash=%s miner=%s time=%v ago",
   124  							d.Block.NumberU64(),
   125  							d.Block.Hash().Hex()[:9],
   126  							d.Block.Coinbase().Hex()[:9],
   127  							time.Since(time.Unix(d.Block.Time().Int64(), 0)).Round(time.Millisecond))),
   128  						greenParenify(d.Peer),
   129  					)
   130  					if bool(glog.D(logger.Info)) {
   131  						chainEventLastSent = time.Now()
   132  					}
   133  				}
   134  			},
   135  		},
   136  	},
   137  	{
   138  		eventT: logEventCoreChainInsertSide,
   139  		ev:     core.ChainSideEvent{},
   140  		handlers: displayEventHandlerFns{
   141  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   142  				switch d := evData.(type) {
   143  				case core.ChainSideEvent:
   144  					glog.D(logger.Info).Infof(prefix(d, e)+forkIcon+" Insert "+logger.ColorGreen("forked block")+"=%s", greenParenify(fmt.Sprintf("n=%8d hash=%s…", d.Block.NumberU64(), d.Block.Hash().Hex()[:9])))
   145  				}
   146  			},
   147  		},
   148  	},
   149  	{
   150  		eventT: logEventCoreHeaderChainInsert,
   151  		ev:     core.HeaderChainInsertEvent{},
   152  		handlers: displayEventHandlerFns{
   153  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   154  				switch d := evData.(type) {
   155  				case core.HeaderChainInsertEvent:
   156  					glog.D(logger.Info).Infof(prefix(d, e)+headerIcon+" Insert "+logger.ColorGreen("headers")+"=%s "+logger.ColorGreen("❐")+"=%s"+logger.ColorGreen("took")+"=%s",
   157  						greenParenify(fmt.Sprintf("processed=%4d ignored=%4d", d.Processed, d.Ignored)),
   158  						greenParenify(fmt.Sprintf("n=%4d hash=%s…", d.LastNumber, d.LastHash.Hex()[:9])),
   159  						greenParenify(fmt.Sprintf("%v", d.Elasped.Round(time.Microsecond))),
   160  					)
   161  					if bool(glog.D(logger.Info)) {
   162  						chainEventLastSent = time.Now()
   163  					}
   164  				}
   165  			},
   166  		},
   167  	},
   168  	{
   169  		eventT: logEventCoreMinedBlock,
   170  		ev:     core.NewMinedBlockEvent{},
   171  		handlers: displayEventHandlerFns{
   172  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   173  				switch d := evData.(type) {
   174  				case core.NewMinedBlockEvent:
   175  					glog.D(logger.Info).Infof(prefix(d, e) + minedIcon + " Mined " + logger.ColorGreen("◼") + "=" + greenParenify(fmt.Sprintf("n=%8d hash=%s… coinbase=%s… txs=%3d uncles=%d",
   176  						d.Block.NumberU64(),
   177  						d.Block.Hash().Hex()[:9],
   178  						d.Block.Coinbase().Hex()[:9],
   179  						len(d.Block.Transactions()),
   180  						len(d.Block.Uncles()),
   181  					)))
   182  				}
   183  			},
   184  		},
   185  	},
   186  	{
   187  		eventT: logEventDownloaderStart,
   188  		ev:     downloader.StartEvent{},
   189  		handlers: displayEventHandlerFns{
   190  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   191  				switch d := evData.(type) {
   192  				case downloader.StartEvent:
   193  					s := prefix(d, e) + downloaderIconStart + " Start " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD))
   194  					glog.D(logger.Info).Infoln(s)
   195  				}
   196  			},
   197  		},
   198  	},
   199  	{
   200  		eventT: logEventDownloaderDone,
   201  		ev:     downloader.DoneEvent{},
   202  		handlers: displayEventHandlerFns{
   203  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   204  				switch d := evData.(type) {
   205  				case downloader.DoneEvent:
   206  					s := prefix(d, e) + downloaderIconDone + " Done  " + greenParenify(fmt.Sprintf("%s", d.Peer)) + " hash=" + greenParenify(d.Hash.Hex()[:9]+"…") + " TD=" + greenParenify(fmt.Sprintf("%v", d.TD))
   207  					glog.D(logger.Info).Infoln(s)
   208  				}
   209  			},
   210  		},
   211  	},
   212  	{
   213  		eventT: logEventDownloaderFailed,
   214  		ev:     downloader.FailedEvent{},
   215  		handlers: displayEventHandlerFns{
   216  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   217  				switch d := evData.(type) {
   218  				case downloader.FailedEvent:
   219  					s := prefix(d, e) + downloaderIconFail + " Fail  " + redParenify(fmt.Sprintf("%s", d.Peer)) + " " + logger.ColorRed("err") + "=" + redParenify(d.Err.Error())
   220  					glog.D(logger.Info).Warnln(s)
   221  				}
   222  			},
   223  		},
   224  	},
   225  	{
   226  		eventT: logEventInterval,
   227  		handlers: displayEventHandlerFns{
   228  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   229  				if time.Since(chainEventLastSent) > time.Duration(time.Second*time.Duration(int32(tickerInterval.Seconds()))) {
   230  					currentBlockNumber = PrintStatusGreen(e, tickerInterval, ctx.GlobalInt(aliasableName(MaxPeersFlag.Name, ctx)))
   231  				}
   232  			},
   233  		},
   234  	},
   235  	{
   236  		eventT: logEventBefore,
   237  		handlers: displayEventHandlerFns{
   238  			func(ctx *cli.Context, e *eth.Ethereum, evData interface{}, tickerInterval time.Duration) {
   239  			},
   240  		},
   241  	},
   242  }
   243  
   244  // PrintStatusGreen implements the displayEventHandlerFn interface
   245  var PrintStatusGreen = func(e *eth.Ethereum, tickerInterval time.Duration, maxPeers int) uint64 {
   246  	lenPeers := e.Downloader().GetPeers().Len()
   247  
   248  	rtt, ttl, conf := e.Downloader().Qos()
   249  	confS := fmt.Sprintf("%01.2f", conf)
   250  	qosDisplay := fmt.Sprintf("rtt=%v ttl=%v conf=%s", rtt.Round(time.Millisecond), ttl.Round(time.Millisecond), confS)
   251  
   252  	_, current, height, _, _ := e.Downloader().Progress() // origin, current, height, pulled, known
   253  	mode := e.Downloader().GetMode()
   254  	if mode == downloader.FastSync {
   255  		current = e.BlockChain().CurrentFastBlock().NumberU64()
   256  	}
   257  
   258  	// Get our head block
   259  	blockchain := e.BlockChain()
   260  	currentBlockHex := blockchain.CurrentBlock().Hash().Hex()
   261  
   262  	// Discover -> not synchronising (searching for peers)
   263  	// FullSync/FastSync -> synchronising
   264  	// Import -> synchronising, at full height
   265  	fOfHeight := fmt.Sprintf("%7d", height)
   266  
   267  	// Calculate and format percent sync of known height
   268  	heightRatio := float64(current) / float64(height)
   269  	heightRatio = heightRatio * 100
   270  	fHeightRatio := fmt.Sprintf("%4.2f%%", heightRatio)
   271  
   272  	// Wait until syncing because real dl mode will not be engaged until then
   273  	if currentMode == lsModeImport {
   274  		fOfHeight = ""    // strings.Repeat(" ", 12)
   275  		fHeightRatio = "" // strings.Repeat(" ", 7)
   276  	}
   277  	if height == 0 {
   278  		fOfHeight = ""    // strings.Repeat(" ", 12)
   279  		fHeightRatio = "" // strings.Repeat(" ", 7)
   280  	}
   281  
   282  	// Calculate block stats for interval
   283  	numBlocksDiff := current - currentBlockNumber
   284  	numTxsDiff := 0
   285  	mGas := new(big.Int)
   286  
   287  	var numBlocksDiffPerSecond uint64
   288  	var numTxsDiffPerSecond int
   289  	var mGasPerSecond = new(big.Int)
   290  
   291  	var dominoGraph string
   292  	var nDom int
   293  	if numBlocksDiff > 0 && numBlocksDiff != current {
   294  		for i := currentBlockNumber + 1; i <= current; i++ {
   295  			b := blockchain.GetBlockByNumber(i)
   296  			if b != nil {
   297  				txLen := b.Transactions().Len()
   298  				// Add to tallies
   299  				numTxsDiff += txLen
   300  				mGas = new(big.Int).Add(mGas, b.GasUsed())
   301  				// Domino effect
   302  				if currentMode == lsModeImport {
   303  					if txLen > len(dominoes)-1 {
   304  						// prevent slice out of bounds
   305  						txLen = len(dominoes) - 1
   306  					}
   307  					if nDom <= 20 {
   308  						dominoGraph += dominoes[txLen]
   309  					}
   310  					nDom++
   311  				}
   312  			}
   313  		}
   314  		if nDom > 20 {
   315  			dominoGraph += "…"
   316  		}
   317  	}
   318  	dominoGraph = logger.ColorGreen(dominoGraph)
   319  
   320  	// Convert to per-second stats
   321  	// FIXME(?): Some degree of rounding will happen.
   322  	// For example, if interval is 10s and we get 6 blocks imported in that span,
   323  	// stats will show '0' blocks/second. Looks a little strange; but on the other hand,
   324  	// precision costs visual space, and normally just looks weird when starting up sync or
   325  	// syncing slowly.
   326  	numBlocksDiffPerSecond = numBlocksDiff / uint64(tickerInterval.Seconds())
   327  
   328  	// Don't show initial current / per second val
   329  	if currentBlockNumber == 0 {
   330  		numBlocksDiffPerSecond = 0
   331  		numBlocksDiff = 0
   332  	}
   333  
   334  	// Divide by interval to yield per-second stats
   335  	numTxsDiffPerSecond = numTxsDiff / int(tickerInterval.Seconds())
   336  	mGasPerSecond = new(big.Int).Div(mGas, big.NewInt(int64(tickerInterval.Seconds())))
   337  	mGasPerSecond = new(big.Int).Div(mGasPerSecond, big.NewInt(1000000))
   338  	mGasPerSecondI := mGasPerSecond.Int64()
   339  
   340  	// Format head block hex for printing (eg. d4e…fa3)
   341  	cbhexstart := currentBlockHex[:9] // trim off '0x' prefix
   342  
   343  	localHeadHeight := fmt.Sprintf("#%7d", current)
   344  	localHeadHex := fmt.Sprintf("%s…", cbhexstart)
   345  	peersOfMax := fmt.Sprintf("%2d/%2d peers", lenPeers, maxPeers)
   346  	domOrHeight := fOfHeight + " " + fHeightRatio
   347  	if len(strings.Replace(domOrHeight, " ", "", -1)) != 0 {
   348  		domOrHeight = logger.ColorGreen("height") + "=" + greenParenify(domOrHeight)
   349  	} else {
   350  		domOrHeight = ""
   351  	}
   352  	var blocksprocesseddisplay string
   353  	qosDisplayable := logger.ColorGreen("qos") + "=" + greenParenify(qosDisplay)
   354  	if currentMode != lsModeImport {
   355  		blocksprocesseddisplay = logger.ColorGreen("~") + greenParenify(fmt.Sprintf("%4d blks %4d txs %2d mgas  "+logger.ColorGreen("/sec"), numBlocksDiffPerSecond, numTxsDiffPerSecond, mGasPerSecondI))
   356  	} else {
   357  		blocksprocesseddisplay = logger.ColorGreen("+") + greenParenify(fmt.Sprintf("%4d blks %4d txs %8d mgas", numBlocksDiff, numTxsDiff, mGas.Uint64()))
   358  		domOrHeight = dominoGraph
   359  		qosDisplayable = ""
   360  	}
   361  	if currentMode == lsModeDiscover {
   362  		blocksprocesseddisplay = ""
   363  	}
   364  
   365  	// Log to ERROR.
   366  	headDisplay := greenParenify(localHeadHeight + " " + localHeadHex)
   367  	peerDisplay := greenParenify(peersOfMax)
   368  
   369  	modeIcon := logger.ColorGreen(lsModeIcon[currentMode])
   370  	if currentMode == lsModeDiscover {
   371  		// TODO: spin me
   372  		modeIcon = lsModeDiscoverSpinners[0]
   373  	}
   374  	modeIcon = logger.ColorGreen(modeIcon)
   375  
   376  	// This allows maximum user optionality for desired integration with rest of event-based logging.
   377  	glog.D(logger.Warn).Infof("%s "+modeIcon+"%s %s "+logger.ColorGreen("✌︎︎︎")+"%s %s %s",
   378  		logger.ColorBlue(currentMode.String()), headDisplay, blocksprocesseddisplay, peerDisplay, domOrHeight, qosDisplayable)
   379  	return current
   380  }